> ## Documentation Index
> Fetch the complete documentation index at: https://blaxel-cdrappier-devin-archive-external-storage-docs.mintlify.site/llms.txt
> Use this file to discover all available pages before exploring further.

# Run Tailscale in a sandbox

> Connect a Blaxel sandbox to your Tailscale tailnet so you can SSH into it from any authorized device, with step-by-step setup instructions.

The Blaxel Tailscale image includes Tailscale and connects to your tailnet when the sandbox starts. Provide a Tailscale authentication key to access the sandbox from any authorized device on your tailnet.

## Prerequisites

Before starting, ensure you have:

* a [Blaxel account](https://blaxel.ai)
* a [Tailscale account](https://tailscale.com/)
* a Tailscale authentication key
* the [Blaxel CLI](/cli-reference/introduction), logged in to your workspace
* the Blaxel TypeScript or Python SDK installed in your project

## 1. Configure a Tailscale authentication key

Follow the [Tailscale authentication key documentation](https://tailscale.com/docs/features/access-control/auth-keys) to create a key.

Then set it as an environment variable:

```sh theme={null}
export TS_AUTHKEY=tskey-auth-...
```

<Warning>
  Treat `TS_AUTHKEY` as a secret. Do not commit it to source control or include
  it directly in application code.
</Warning>

## 2. Create the sandbox through the API (recommended)

The image uses userspace networking. You do not need to enable a TUN device or `iptables`.

Use the TypeScript or Python SDK to call the Blaxel API. Create the sandbox with the `blaxel/tailscale:latest` image and pass `TS_AUTHKEY` as a runtime environment variable.

<Warning>
  The sandbox stops immediately when `TS_AUTHKEY` is missing. An invalid, expired,
  or revoked key prevents Tailscale from connecting, so the sandbox stops when
  Tailscale exits or after the 60-second startup timeout.
</Warning>

<CodeGroup>
  ```typescript TypeScript theme={null}
  import { SandboxInstance } from "@blaxel/core";

  const authKey = process.env.TS_AUTHKEY;
  if (!authKey) throw new Error("TS_AUTHKEY is required");

  await SandboxInstance.create({
    name: "tailscale-sandbox",
    image: "blaxel/tailscale:latest",
    envs: [{ name: "TS_AUTHKEY", value: authKey }],
  });
  ```

  ```python Python theme={null}
  import asyncio
  import os

  from blaxel.core import SandboxInstance

  async def main():
      auth_key = os.environ["TS_AUTHKEY"]

      await SandboxInstance.create({
          "name": "tailscale-sandbox",
          "image": "blaxel/tailscale:latest",
          "envs": [{"name": "TS_AUTHKEY", "value": auth_key}],
      })

  asyncio.run(main())
  ```
</CodeGroup>

## 3. Verify the Tailscale connection

Connect to the sandbox terminal:

```sh theme={null}
bl connect sandbox tailscale-sandbox
```

Check that the sandbox is connected and retrieve its Tailscale IP address:

```sh theme={null}
tailscale status
tailscale ip
```

The image uses the first available hostname value in this order: `TS_HOSTNAME`, a manually provided `SANDBOX_NAME`, the Blaxel-provided `BL_NAME`, then `tailscale-sandbox`.

In the standard Blaxel flow, the Tailscale hostname defaults to the sandbox name through `BL_NAME`. Set `TS_HOSTNAME` when creating the sandbox to use a different hostname.

## 4. Connect with Tailscale SSH

From another authorized device on your tailnet, connect with the sandbox hostname or Tailscale IP:

```sh theme={null}
tailscale ssh root@tailscale-sandbox
```

Your [Tailscale SSH access policy](https://tailscale.com/kb/1193/tailscale-ssh) determines which users and devices can connect.

## Appendix: Configure Tailscale manually

### Create a sandbox

This manual setup requires iptables, which is not enabled in sandboxes by default. You enable it by passing [`extraArgs` at creation time](/Sandboxes/Overview#kernel-networking).

<CodeGroup>
  ```typescript TypeScript theme={null}
  import { SandboxInstance } from "@blaxel/core";

  const sandbox = await SandboxInstance.create({
    name: "tailscale-sandbox",
    extraArgs: { iptables: "enabled" },
  });
  ```

  ```python Python theme={null}
  from blaxel.core import SandboxInstance

  sandbox = await SandboxInstance.create({
      "name": "tailscale-sandbox",
      "extra_args": {"iptables": "enabled"},
  })
  ```
</CodeGroup>

### Install and configure Tailscale in the sandbox

Connect to the sandbox terminal:

```sh theme={null}
bl connect sandbox tailscale-sandbox
export TS_AUTHKEY=tskey-auth-...
```

Next, install the `tailscale` and `iptables` packages and start the `tailscaled` daemon as a background process.

```sh theme={null}
# Install dependencies
apk add tailscale iptables

# Start the daemon
tailscaled &
```

You can then run `tailscale up --ssh` to authenticate and enable Tailscale SSH.

```sh theme={null}
# Authenticate and enable Tailscale SSH
# Prints an auth URL if no key is provided, or authenticates silently with a key
tailscale up --hostname=my-sandbox --ssh
# or: tailscale up --authkey=$TS_AUTHKEY --hostname=my-sandbox --ssh
```

Retrieve the Tailscale IP.

```sh theme={null}
# Get your Tailscale IP
tailscale ip
```

### Connect to the sandbox using Tailscale

Once authenticated, the sandbox is reachable via SSH from any device on your Tailscale network:

```sh theme={null}
ssh root@<tailscale-ip>
# or using the hostname directly:
ssh root@my-sandbox
```

### Using the SDK

It's also possible to create a sandbox and configure Tailscale using the Blaxel SDKs instead of the sandbox terminal:

<CodeGroup>
  ```typescript TypeScript theme={null}
  import { SandboxInstance } from "@blaxel/core";

  const TS_AUTHKEY = process.env.TS_AUTHKEY!;
  const SANDBOX_NAME = "tailscale-sandbox";

  // 1. Create sandbox with iptables enabled
  const sandbox = await SandboxInstance.create({
    name: SANDBOX_NAME,
    extraArgs: { iptables: "enabled" },
  });

  // 2. Install dependencies
  await sandbox.process.exec({
    name: "install-deps",
    command: "apk add --no-cache tailscale iptables",
    waitForCompletion: true,
    timeout: 60, // 60 seconds
  });

  // 3. Start the tailscaled daemon in the background
  await sandbox.process.exec({
    name: "tailscaled",
    command: "tailscaled",
    keepAlive: true,
    timeout: 0, // run indefinitely
  });

  // Give the daemon a moment to initialize
  await new Promise((r) => setTimeout(r, 2000));

  // 4. Authenticate and bring up the interface
  const up = await sandbox.process.exec({
    name: "tailscale-up",
    command: `tailscale up --authkey=$TS_AUTHKEY --hostname=${SANDBOX_NAME} --ssh`,
    env: { TS_AUTHKEY },
    waitForCompletion: true,
    timeout: 30, // 30 seconds
  });
  console.log("tailscale up:", up.logs);

  // 5. Get the Tailscale IP
  const ip = await sandbox.process.exec({
    name: "tailscale-ip",
    command: "tailscale ip",
    waitForCompletion: true,
    timeout: 10, // 10 seconds
  });
  console.log("Tailscale IP:", ip.logs?.trim());
  ```

  ```python Python theme={null}
  import asyncio
  import os
  from blaxel.core import SandboxInstance

  TS_AUTHKEY = os.environ["TS_AUTHKEY"]
  SANDBOX_NAME = "tailscale-sandbox"

  async def setup_tailscale():
      # 1. Create sandbox with iptables enabled
      sandbox = await SandboxInstance.create({
          "name": SANDBOX_NAME,
          "extra_args": {"iptables": "enabled"},
      })

      # 2. Install dependencies
      await sandbox.process.exec({
          "name": "install-deps",
          "command": "apk add --no-cache tailscale iptables",
          "wait_for_completion": True,
          "timeout": 60,  # 60 seconds
      })

      # 3. Start the tailscaled daemon in the background
      await sandbox.process.exec({
          "name": "tailscaled",
          "command": "tailscaled",
          "keep_alive": True,
          "timeout": 0,  # run indefinitely
      })

      # Give the daemon a moment to initialize
      await asyncio.sleep(2)

      # 4. Authenticate and bring up the interface
      up = await sandbox.process.exec({
          "name": "tailscale-up",
          "command": f"tailscale up --authkey=$TS_AUTHKEY --hostname={SANDBOX_NAME} --ssh",
          "env": {"TS_AUTHKEY": TS_AUTHKEY},
          "wait_for_completion": True,
          "timeout": 30,  # 30 seconds
      })
      print("tailscale up:", up.logs)

      # 5. Get the Tailscale IP
      ip = await sandbox.process.exec({
          "name": "tailscale-ip",
          "command": "tailscale ip",
          "wait_for_completion": True,
          "timeout": 10,  # 10 seconds
      })
      print("Tailscale IP:", ip.logs.strip())

  asyncio.run(setup_tailscale())
  ```
</CodeGroup>

## Resources

<CardGroup cols={2}>
  <Card title="Sandbox overview" href="/Sandboxes/Overview">
    Create and manage Blaxel sandboxes.
  </Card>

  <Card title="Tailscale authentication keys" href="https://tailscale.com/kb/1085/auth-keys">
    Configure reusable and ephemeral authentication keys.
  </Card>
</CardGroup>
