Skip to content

aaomidi/tslink

Folders and files

NameName
Last commit message
Last commit date

Latest commit

Β 

History

44 Commits
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 

Repository files navigation

Tailscale Container Network Plugin

A Docker network plugin that gives each container its own Tailscale identity. Containers appear as individual nodes in your tailnet with their own Tailscale IPs.

How It Works

When you create a Docker network with this plugin and run containers on it:

  1. Each container gets its own Tailscale node identity
  2. The container appears in your Tailscale admin console as a separate device
  3. The container can reach other nodes in your tailnet
  4. The container also has internet access via NAT
β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”
β”‚ Docker Host                                              β”‚
β”‚                                                          β”‚
β”‚  β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”  β”‚
β”‚  β”‚ Container (Tailscale IP: 100.x.y.z)                β”‚  β”‚
β”‚  β”‚                                                    β”‚  β”‚
β”‚  β”‚   App ──► tailscaled ──► Tailnet                   β”‚  β”‚
β”‚  β”‚                                                    β”‚  β”‚
β”‚  β”‚   eth0 ──► veth ──► NAT ──► Internet               β”‚  β”‚
β”‚  β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜  β”‚
β”‚                                                          β”‚
β”‚  β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”                                β”‚
β”‚  β”‚ Plugin               β”‚                                β”‚
β”‚  β”‚ β€’ Manages tailscaled β”‚                                β”‚
β”‚  β”‚ β€’ Creates veth pairs β”‚                                β”‚
β”‚  β”‚ β€’ Sets up NAT        β”‚                                β”‚
β”‚  β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜                                β”‚
β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜

Installation

Prerequisites

Install the Plugin

# Create the data directory (required)
sudo mkdir -p /var/lib/docker-plugins/tailscale

Docker plugins require architecture-specific tags:

# For amd64 (Intel/AMD, most cloud VMs)
docker plugin install ghcr.io/aaomidi/tslink:latest-amd64

# For arm64 (Apple Silicon, AWS Graviton, Raspberry Pi)
docker plugin install ghcr.io/aaomidi/tslink:latest-arm64

# Or install a specific version
docker plugin install ghcr.io/aaomidi/tslink:v0.0.1-amd64

# Or follow main branch (latest development)
docker plugin install ghcr.io/aaomidi/tslink:main-amd64

# The plugin will be enabled automatically
docker plugin ls

Usage

Create a Network

# With auth key in command (ephemeral nodes by default)
# Replace :latest-amd64 with :latest-arm64 for ARM systems
docker network create \
  --driver ghcr.io/aaomidi/tslink:latest-amd64 \
  --opt tslink.authkey=tskey-auth-xxxxx \
  my-tailnet

# Or set the auth key globally when installing the plugin
docker plugin set ghcr.io/aaomidi/tslink:latest-amd64 TS_AUTHKEY=tskey-auth-xxxxx
docker network create --driver ghcr.io/aaomidi/tslink:latest-amd64 my-tailnet

Run Containers

# Run a container - it automatically gets a Tailscale IP
docker run --rm --network my-tailnet alpine sh

# Inside the container:
# - Check your Tailscale IP in the Tailscale admin console
# - Ping other tailnet nodes
# - Access the internet normally

Example: Web Server on Tailnet

# Run nginx, accessible from your tailnet
docker run -d --name web --network my-tailnet nginx

# From any device on your tailnet, access via the node's Tailscale hostname
curl http://web.your-tailnet.ts.net

Example: Docker Compose

# Use :latest-amd64 or :latest-arm64 depending on your system
networks:
  tailnet:
    driver: ghcr.io/aaomidi/tslink:latest-amd64
    driver_opts:
      tslink.authkey: ${TS_AUTHKEY}

services:
  api:
    image: my-api
    networks:
      - tailnet

  worker:
    image: my-worker
    networks:
      - tailnet

Configuration

Network Options

Option Description Default
tslink.authkey Tailscale auth key Required (or set via plugin env)

Container Labels

Configure per-container Tailscale settings using labels:

Label Description Example
tslink.hostname Tailscale hostname tslink.hostname=my-api
tslink.tags ACL tags (comma-separated) tslink.tags=tag:server,tag:prod
tslink.serve.<port> Expose port via Tailscale Serve tslink.serve.443=https:8080
tslink.service Register as Tailscale Service backend tslink.service=svc:my-api
tslink.direct Enable direct machine serve tslink.direct=true (default)
# Example: Container with custom hostname and tags
docker run -d --network my-tailnet \
  --label tslink.hostname=my-api \
  --label tslink.tags=tag:server \
  nginx

# Example: Expose HTTPS on port 443, forwarding to container port 8080
docker run -d --network my-tailnet \
  --label tslink.hostname=web \
  --label tslink.serve.443=https:8080 \
  my-web-app

Setting Default Auth Key

Instead of passing the auth key with each network, set it as a plugin environment variable:

# Set default auth key (use :latest-arm64 for ARM systems)
docker plugin disable ghcr.io/aaomidi/tslink:latest-amd64
docker plugin set ghcr.io/aaomidi/tslink:latest-amd64 TS_AUTHKEY=tskey-auth-xxxxx
docker plugin enable ghcr.io/aaomidi/tslink:latest-amd64

# Now create networks without specifying the auth key
docker network create --driver ghcr.io/aaomidi/tslink:latest-amd64 my-tailnet

Auth Key Types

Key Type Behavior
Ephemeral key Nodes are automatically removed when the container stops
Reusable key Nodes persist in your tailnet after container stops
Pre-approved key Nodes don't require manual approval

For most use cases, use an ephemeral, reusable, pre-approved key.

Cleanup

# Remove network (stops all containers using it)
docker network rm my-tailnet

# Ephemeral nodes are automatically removed from Tailscale
# Non-ephemeral nodes remain in your admin console and need manual removal

Troubleshooting

View Debug Logs

Each container's Tailscale daemon writes logs to the host:

# List all endpoint logs
docker run --rm -v /var/lib/docker-plugins/tailscale:/data alpine \
  sh -c 'for d in /data/by-hostname/*/; do echo "=== $d ==="; cat "$d/debug.log" 2>/dev/null | tail -20; done'

# Check plugin logs (Linux)
journalctl -u docker -f | grep -i tailscale

# Check plugin logs (macOS/OrbStack)
docker run --rm -it --privileged --pid=host alpine nsenter -t 1 -m -u -n -i sh
# Then: journalctl -u docker -f

Container Won't Start

  1. Check debug logs (above) for errors
  2. Verify auth key is valid and not expired
  3. Ensure the plugin is enabled: docker plugin ls

Node Appears "Offline" in Tailscale Console

  • Auth key may be expired - create a new one
  • Container may have stopped - check docker ps
  • Network issues - check debug logs for connection errors

Internet Works but Can't Reach Tailnet

Check debug logs for:

  • Switching ipn state Starting -> Running = connected successfully
  • Switching ipn state NeedsLogin = auth key issue
  • network is unreachable = veth setup failed

Identity Reuse

Containers with the same hostname reuse the same Tailscale identity. If you need a fresh identity:

# Clear state for a specific hostname
docker run --rm -v /var/lib/docker-plugins/tailscale:/data alpine \
  rm -rf /data/by-hostname/<hostname>

Known Limitations

  • macOS/Windows: Only works with Docker in a Linux VM (OrbStack, Docker Desktop)
  • MagicDNS in container: Containers can reach tailnet by IP; MagicDNS resolution requires additional DNS config

Development

See CLAUDE.md for development setup.

License

MIT

About

Docker network plugin that gives each container its own Tailscale identity

Topics

Resources

License

Stars

Watchers

Forks

Packages

 
 
 

Contributors 3

  •  
  •  
  •