Skip to content

Tailscale Access

Run tunlx on your Tailnet — listeners, dashboard, and even outbound egress — without exposing anything to the public internet. tunlx supports two modes:

Embedded tsnet tunlx runs its own Tailscale node inside the binary. No sidecar. Configured via tailscale. in config.json or TUNLX_TS_ env vars.

Sidecar container Use the official tailscale/tailscale image alongside tunlx with network_mode: service:tailscale. Easier when you already run Tailscale that way.

Tailscale configuration in the dashboard

This guide covers the sidecar pattern in detail (compose-friendly) and includes notes on the embedded mode where they differ.

Embedded tsnet quick start

For a containerless or single-binary deploy, enable the embedded node:

{
  "tailscale": {
    "enabled": true,
    "hostname": "tunlx",
    "stateDir": "/app/data/tailscale-state",
    "ephemeral": false
  }
}
TS_AUTHKEY=tskey-auth-XXXXXXXX \
TUNLX_TS_ENABLED=true \
TUNLX_TS_HOSTNAME=tunlx \
TUNLX_TS_STATE_DIR=/app/data/tailscale-state \
./tunlx -configFile=/app/data/config.json

Precedence is config.jsonTUNLX_TS_* env vars → defaults. The first start logs the node in to your Tailnet using TS_AUTHKEY; state is then reused from stateDir across restarts.

Once running, the dashboard's Networking panel exposes:

  • Current Tailnet status (logged-in, IPs, peers)
  • Inbound mode picker (public / tailscale / both)
  • Outbound mode picker (public / vpn / tsexit)
  • Exit-node selector populated from /tailnet/peers

See Inbound & Outbound Modes for the topology overview.

Sidecar pattern

The sidecar pattern keeps a vanilla Tailscale container alongside tunlx and uses network_mode: service:tailscale for shared networking.

Enable Tailscale-Only During Install

When running install.sh, choose Tailscale access when prompted.

For silent mode:

TUNLX_ADMIN_PASSWORD='change-me-now' \
TUNLX_TAILSCALE_ONLY=1 \
TUNLX_TAILSCALE_AUTHKEY='tskey-auth-XXXXXXXX' \
TUNLX_TAILSCALE_HOSTNAME='tunlx' \
bash install.sh --silent

This configures:

  • tunlx with network_mode: service:tailscale
  • a tailscale sidecar service
  • persistent Tailnet state in tailscale_state

Convert an Existing Install to Tailscale-Only (Manual)

If you already installed tunlx in host mode, switch the compose stack manually.

  1. Back up your compose file.
cp docker-compose.yaml docker-compose.yaml.bak
  1. Update your compose stack:

  2. Set tunlx.network_mode: service:tailscale

  3. Add depends_on: tailscale
  4. Add a tailscale service
  5. If you run MediaFlow, also set mediaflow.network_mode: service:tailscale

Example:

services:
  tunlx:
    image: ghcr.io/eyupio/tunlx:latest
    restart: unless-stopped
    environment:
      - commandLine=-configFile=/app/data/config.json
    volumes:
      - ./data:/app/data
      - /var/run/docker.sock:/var/run/docker.sock
      - /etc/wireguard:/etc/wireguard
    cap_add:
      - NET_ADMIN
      - NET_RAW
    devices:
      - /dev/net/tun
    network_mode: service:tailscale
    depends_on:
      tailscale:
        condition: service_healthy

  mediaflow:
    image: mhdzumair/mediaflow-proxy:latest
    restart: unless-stopped
    environment:
      # tunlx shares this network namespace — set mediaFlowURL to http://localhost:8800
      - PORT=8800
      - API_PASSWORD=change-me
    network_mode: service:tailscale
    depends_on:
      tailscale:
        condition: service_healthy

  tailscale:
    image: tailscale/tailscale:latest
    container_name: tailscale
    restart: unless-stopped
    hostname: tunlx
    entrypoint: /usr/local/bin/containerboot
    volumes:
      - tailscale_state:/var/lib/tailscale
    environment:
      - TS_AUTHKEY=tskey-auth-XXXXXXXX
      - TS_STATE_DIR=/var/lib/tailscale
      - TS_USERSPACE=true
    healthcheck:
      test: ["CMD-SHELL", "tailscale status >/dev/null 2>&1"]
      interval: 10s
      timeout: 5s
      retries: 12

volumes:
  tailscale_state:
  1. Apply changes.
docker compose up -d
  1. Verify Tailnet status.
docker exec tailscale tailscale status
  1. Access tunlx from Tailnet-only endpoints.
http://tunlx:6060

Use your Tailnet DNS name or Tailnet IP if hostname resolution differs.

How-To: Use an Exit Node (Exit Point)

If you want tunlx egress to leave from another Tailscale node:

  1. Choose an available exit node in your Tailnet.
  2. Set it from the tailscale sidecar.
docker exec tailscale tailscale up \
  --accept-routes \
  --exit-node=<exit-node-tailnet-ip-or-name> \
  --exit-node-allow-lan-access=true
  1. Confirm it is active.
docker exec tailscale tailscale status
  1. Confirm tunlx egress path.
docker exec tunlx curl -s https://api.ipify.org

How-To: Use a VPN Exit Point

A VPN exit point means your selected Tailscale exit node itself routes through a VPN provider.

Typical pattern:

  1. Run VPN on the exit-node host (WireGuard/OpenVPN/provider app).
  2. Advertise/enable that host as a Tailscale exit node.
  3. Point tunlx to that exit node with the tailscale up --exit-node=... command above.

This gives tunlx a Tailnet path with VPN-backed egress.

Optional: Advertise This Host as an Exit Node

If you want this Tailscale sidecar host to be selectable as an exit point by other Tailnet devices:

docker exec tailscale tailscale up --advertise-exit-node

Then approve the exit-node advertisement in the Tailscale admin console.

Per-proxy tsexit

Even without flipping the global outbound mode to tsexit, individual proxies can opt in:

{
  "proxies": [
    { "name": "premiumA", "route": "tsexit" },
    { "name": "publicB",  "route": "system" }
  ]
}

Each proxy with route: tsexit sends its egress through the configured Tailscale exit node, while others use the system route. See Inbound & Outbound Modes for the topology overview.

Notes

  • If you also use per-proxy route: vpn in tunlx, you may create double-hop routing.
  • Prefer one clear egress policy per workload (either tunlx per-proxy VPN routing, or Tailscale exit-node routing).