Skip to main content

Tailscale VPN

VoidRun provides native Tailscale integration, allowing sandboxes to securely connect to your private tailnet. This enables AI agents and code execution environments to interact with internal APIs, databases, and services without exposing them to the public internet.

🔒 Zero-Trust Security

WireGuard-based encryption with automatic key rotation.

🌐 Private Access

Connect to internal APIs, databases, and services securely.

⚡ Simple Setup

Pre-installed in all VoidRun images. Just authenticate and connect.

Prerequisites

Before connecting a sandbox to your tailnet, ensure you have:
1

Create a Tailscale Account

Sign up at tailscale.com if you don’t have one.
2

Generate an Auth Key

  1. Go to the Tailscale Admin Console
  2. Click “Generate auth key”
  3. Configure key settings:
    • Description: voidrun-sandboxes
    • Reusable: Enable for multiple sandboxes
    • Ephemeral: Enable (recommended) - nodes auto-remove after disconnect
    • Tags: Optional - assign ACL tags for access control
  4. Copy the auth key (starts with tskey-auth-)
3

Store Auth Key Securely

Store your auth key in environment variables or a secrets manager. Never commit it to source control.
Ephemeral nodes are strongly recommended for VoidRun sandboxes. Without ephemeral mode, each sandbox creates a permanent node in your tailnet, which can clutter your network and reach node limits.

Connecting to a Tailnet

Basic Connection

Connect a sandbox to your tailnet using an auth key:
import { VoidRun } from '@voidrun/sdk';

const vr = new VoidRun({ apiKey: process.env.VOIDRUN_API_KEY });

// Create a sandbox
const sandbox = await vr.createSandbox({
  name: 'tailscale-sandbox',
  cpu: 2,
  mem: 2048
});

// Connect to Tailscale using auth key
const authKey = process.env.TAILSCALE_AUTH_KEY!;
const result = await sandbox.exec({ 
  command: `sudo tailscale up --authkey=${authKey} --hostname=voidrun-${sandbox.id.slice(0, 8)}` 
});

console.log('Tailscale status:', result.data?.stdout);

// Verify connection
const status = await sandbox.exec({ command: 'sudo tailscale status' });
console.log(status.data?.stdout);

Connection with Tags

Use ACL tags for fine-grained access control:
import { VoidRun } from '@voidrun/sdk';

const vr = new VoidRun({ apiKey: process.env.VOIDRUN_API_KEY });
const sandbox = await vr.createSandbox({ cpu: 2, mem: 2048 });

const authKey = process.env.TAILSCALE_AUTH_KEY!;

// Connect with a tag for ACL control
await sandbox.exec({ 
  command: `sudo tailscale up --authkey=${authKey} --hostname=voidrun-agent --tag=tag:voidrun` 
});

// The tag allows you to control what this sandbox can access via Tailscale ACLs
console.log('Connected with tag:voidrun');

Interacting with Private Resources

Once connected to your tailnet, the sandbox can access any resource available to your Tailscale network.

Access Internal APIs

import { VoidRun } from '@voidrun/sdk';

const vr = new VoidRun({ apiKey: process.env.VOIDRUN_API_KEY });
const sandbox = await vr.getSandbox('sandbox-id');

// Connect to Tailscale first (see above)
// ...

// Access an internal API via Tailscale
const result = await sandbox.exec({ 
  command: 'curl -s http://internal-api.tailnet-name.tsvc:8080/health' 
});

console.log('Internal API response:', result.data?.stdout);

// Access using Tailscale MagicDNS
const dbResult = await sandbox.exec({ 
  command: 'curl -s http://postgres.internal.ts.net:5432/status' 
});

Connect to Databases

import { VoidRun } from '@voidrun/sdk';

const vr = new VoidRun({ apiKey: process.env.VOIDRUN_API_KEY });
const sandbox = await vr.getSandbox('sandbox-id');

// Install PostgreSQL client
await sandbox.exec({ command: 'sudo apt-get update && sudo apt-get install -y postgresql-client' });

// Connect to internal PostgreSQL via Tailscale
const query = await sandbox.exec({ 
  command: 'PGPASSWORD=secret psql -h postgres.internal.ts.net -U app_user -d mydb -c "SELECT * FROM users LIMIT 5"' 
});

console.log('Query result:', query.data?.stdout);

SSH to Internal Servers

import { VoidRun } from '@voidrun/sdk';

const vr = new VoidRun({ apiKey: process.env.VOIDRUN_API_KEY });
const sandbox = await vr.getSandbox('sandbox-id');

// SSH to an internal server via Tailscale
// Note: You'll need to set up SSH keys first
await sandbox.exec({ 
  command: 'ssh user@internal-server.tailnet-name.ts.net "uname -a"' 
});

Ephemeral Nodes

Ephemeral nodes are critical for VoidRun sandboxes. Each sandbox creates a new node in your tailnet. Without ephemeral mode, these nodes persist forever, cluttering your network and potentially reaching Tailscale’s node limits.

Creating Ephemeral Auth Keys

When generating your auth key in the Tailscale Admin Console:
  1. Navigate to SettingsKeys
  2. Click Generate auth key
  3. Enable “Ephemeral” - This is essential!
  4. Optionally enable “Reusable” for multiple sandboxes

Ephemeral Node Behavior

EventBehavior
Sandbox connectsNew node appears in tailnet
Sandbox disconnectsNode marked as offline
After timeout (~1 hour)Node automatically removed from tailnet
Sandbox terminatesNode immediately removed

Checking Node Status

import { VoidRun } from '@voidrun/sdk';

const vr = new VoidRun({ apiKey: process.env.VOIDRUN_API_KEY });
const sandbox = await vr.getSandbox('sandbox-id');

// Check Tailscale connection status
const status = await sandbox.exec({ command: 'sudo tailscale status --json' });
console.log('Tailscale status:', JSON.parse(status.data?.stdout || '{}'));

// Get current IP address
const ip = await sandbox.exec({ command: 'sudo tailscale ip -4' });
console.log('Tailscale IP:', ip.data?.stdout?.trim());

ACL Configuration

Control what your VoidRun sandboxes can access using Tailscale ACLs. Create a tag-based access policy:

Example ACL Policy

{
  "tagOwners": {
    "tag:voidrun": ["autogroup:admin"]
  },
  "acls": [
    {
      "action": "accept",
      "src": ["tag:voidrun"],
      "dst": [
        "tag:api-server:*",
        "tag:database:5432",
        "group:dev:22"
      ]
    }
  ]
}
This policy allows sandboxes with tag:voidrun to:
  • Access all ports on servers tagged api-server
  • Access port 5432 (PostgreSQL) on servers tagged database
  • SSH (port 22) to the dev group

Disconnecting

When a sandbox is terminated, its Tailscale node is automatically removed (for ephemeral nodes). To manually disconnect:
import { VoidRun } from '@voidrun/sdk';

const vr = new VoidRun({ apiKey: process.env.VOIDRUN_API_KEY });
const sandbox = await vr.getSandbox('sandbox-id');

// Disconnect from Tailscale
await sandbox.exec({ command: 'sudo tailscale down' });

console.log('Disconnected from Tailscale');

Troubleshooting

Common Issues

IssueSolution
”tailscale: command not found”Tailscale is pre-installed. Try sudo tailscale or check the image.
”not logged in”Run tailscale up --authkey=... to authenticate.
”connection refused”Verify the target service is running and accessible via Tailscale.
”ACL denied”Check your Tailscale ACL policy allows the connection.
Node not appearingEnsure the auth key is valid and not expired.

Debug Commands

# Check Tailscale service status
voidrun exec <sandbox-id> --command "sudo systemctl status tailscaled"

# View Tailscale logs
voidrun exec <sandbox-id> --command "sudo journalctl -u tailscaled -n 50"

# Ping another node
voidrun exec <sandbox-id> --command "tailscale ping other-node.tailnet-name.ts.net"

# Debug connectivity
voidrun exec <sandbox-id> --command "tailscale status --verbose"
Security Note: Auth keys provide access to your entire tailnet (limited by ACLs). Treat them like passwords. Rotate keys regularly and use tags to limit access scope.

Next Steps

SSH & VNC Access

Learn how to access sandboxes directly via SSH and VNC.

Nested Containers

Run Docker and Kubernetes inside your sandboxes.