# Tailscale on ZimaOS via Docker

Run Tailscale as a Docker container with `network_mode: host` for full VPN access.

## Initial Setup

```bash
# Prepare directory and tun device
mkdir -p /DATA/AppData/tailscale/state
modprobe tun
[ -e /dev/net/tun ] || (mkdir -p /dev/net && mknod /dev/net/tun c 10 200 && chmod 600 /dev/net/tun)

# Start container with auth key (one-time)
docker run -d \
  --name tailscale \
  --restart unless-stopped \
  --network host \
  --cap-add NET_ADMIN --cap-add NET_RAW \
  --device /dev/net/tun \
  -v /DATA/AppData/tailscale/state:/var/lib/tailscale \
  -e TS_AUTHKEY=tskey-auth-XXXXXXXXXXXXXXX \
  -e TS_STATE_DIR=/var/lib/tailscale \
  -e TS_HOSTNAME=zimaos \
  -e TS_EXTRA_ARGS="--accept-routes" \
  -e TS_USERSPACE=false \
  tailscale/tailscale:latest
```

The auth key is only needed for first login. After that, the state in `/DATA/AppData/tailscale/state/` persists authentication.

## Verify

```bash
docker logs tailscale | tail -20
docker exec tailscale tailscale status
docker exec tailscale tailscale ip -4
```

## Changing Flags (CRITICAL PITFALL)

**The state file remembers old flags.** If you change `TS_EXTRA_ARGS` (e.g., remove `--ssh`), the container will fail with:

```
Error: changing settings via 'tailscale up' requires mentioning all non-default flags.
To proceed, either re-run your command with --reset or use the command below...
```

**Fix: Run a one-shot container with `--reset`, then restart with new flags:**

```bash
# Stop and remove current container
docker stop tailscale && docker rm tailscale

# Reset the state (removes stale flags)
docker run -d \
  --name tailscale-reset \
  --restart no \
  --network host \
  --cap-add NET_ADMIN --cap-add NET_RAW \
  --device /dev/net/tun \
  -v /DATA/AppData/tailscale/state:/var/lib/tailscale \
  -e TS_STATE_DIR=/var/lib/tailscale \
  -e TS_HOSTNAME=zimaos \
  -e TS_EXTRA_ARGS="--accept-routes --reset" \
  -e TS_USERSPACE=false \
  tailscale/tailscale:latest

# Wait ~8s, verify IP unchanged
sleep 8 && docker logs tailscale-reset | tail -15
docker exec tailscale-reset tailscale ip -4

# Clean up reset container
docker stop tailscale-reset && docker rm tailscale-reset

# Start permanent container with new flags (no --reset, no TS_AUTHKEY needed)
docker run -d \
  --name tailscale \
  --restart unless-stopped \
  --network host \
  --cap-add NET_ADMIN --cap-add NET_RAW \
  --device /dev/net/tun \
  -v /DATA/AppData/tailscale/state:/var/lib/tailscale \
  -e TS_STATE_DIR=/var/lib/tailscale \
  -e TS_HOSTNAME=zimaos \
  -e TS_EXTRA_ARGS="--accept-routes" \
  -e TS_USERSPACE=false \
  tailscale/tailscale:latest
```

## Key Flags

| Flag | Effect |
|---|---|
| `--ssh` | Redirects SSH connections via Tailscale **into the container**, not the host. Omit if you want host SSH via `sshd` on port 22. |
| `--accept-routes` | Accept subnet routes advertised by other Tailnet nodes (e.g., access LAN devices behind another node). |
| `--accept-dns` | Use Tailscale's MagicDNS. Default is `false` in Docker mode. |

## ZimaOS-Specific Notes

- tun device at `/dev/net/tun` usually exists already (crw-rw-rw-)
- Harmless warning: `failed to enable src_valid_mark` — ZimaOS read-only /proc prevents this, doesn't affect functionality
- DERP relay auto-selects nearest (typically `derp-14` = Amsterdam or `derp-26` = Nuremberg)
