Skip to content

Repository files navigation

Foxtail Logo

Foxtail

(Fire)fox + Tail(scale) = Foxtail

A Firefox browser that lives on your Tailscale network — accessible from any device on your tailnet, directly in a browser tab.


Foxtail runs Firefox inside Docker and joins it to your tailnet as a named node. From any device on your tailnet you open a tab, and you're in. No port forwarding. No public exposure. No VPN client on the remote device.

Some things this is good for:

  • Browse the web through your home or office network from anywhere
  • Reach internal services on your tailnet (dashboards, dev servers, NAS) without exposing them
  • Give a teammate a shared browser pointed at your internal resources
  • Test how a site behaves from a specific network's perspective

How it uses Tailscale

Userspace networking — Tailscale runs entirely in userspace inside the container. No /dev/net/tun, no host networking, no special kernel modules. It works anywhere Docker runs.

MagicDNS — the browser resolves .ts.net hostnames out of the box. Internal services are reachable by name, not IP.

Auth keys — the auth key is injected as an environment variable at runtime. You can pass it directly, fetch it from any password manager, or use the built-in 1Password CLI integration. It never touches disk or an image layer.

Ephemeral nodes — use an ephemeral auth key and the device disappears from your tailnet automatically when the container stops. No stale devices to clean up.

Prerequisites

  1. Docker Desktop installed and running
  2. Task installed:
    brew install go-task
  3. Tailscale account with an auth key (generate one at https://login.tailscale.com/admin/settings/keys)

Setup

1. Provide a Tailscale auth key

Generate a key at https://login.tailscale.com/admin/settings/keys, then choose how to supply it:

Option A — direct (any password manager or shell)

# In your .env file:
TS_AUTHKEY=tskey-auth-...

# Or inline at runtime:
TS_AUTHKEY='tskey-auth-...' task start

# Or export from your password manager first, e.g. Bitwarden:
export TS_AUTHKEY=$(bw get password "Tailscale Foxtail key")
task start

Option B — 1Password CLI (automatic fetch)

Install and sign in to the 1Password CLI, then store the key in 1Password:

brew install --cask 1password-cli
op signin
  • Vault: Private (or set OP_VAULT)
  • Item name: Foxtail (or set OP_ITEM_NAME)
  • Field name: auth_key (or set OP_FIELD_NAME)

Leave TS_AUTHKEY unset and start.sh will fetch it automatically.

Key options to consider:

  • Reusable — lets you recreate the container without generating a new key each time
  • Ephemeral — the tailnet device is removed automatically when the container stops

2. Configure credentials

cp .env.example .env
# Edit .env and set VNC_PW, VNC_VIEW_ONLY_PW, and optionally TS_AUTHKEY

3. Start

task start

4. Open the browser

https://localhost:6901
  • Username: kasm_user
  • Password: (your VNC_PW from .env, default: foxtail)

Usage

Run task with no arguments to see all available commands.

Command Description
task start Fetch auth key from 1Password and start
task stop Stop and remove the container
task restart Stop and restart
task build Rebuild the Docker image from scratch
task logs Stream container logs
task status Check running container processes
task debug Start with verbose logging
task test-auth Validate 1Password CLI and auth key
task tailscale-status Show Tailscale connection inside the container

Configuration

Custom hostname

TAILSCALE_HOSTNAME=my-browser task start

Pass the auth key directly

TS_AUTHKEY='tskey-auth-...' task start

Custom 1Password item

OP_VAULT="Personal" OP_ITEM_NAME="Tailscale Keys" task start

Tailscale ACL tags

TAILSCALE_TAGS="tag:browser,tag:docker" task start

Architecture

┌─────────────────────────────────────┐
│     Docker Container                │
│                                     │
│  ┌──────────────────────────────┐   │
│  │ Tailscale (userspace mode)   │   │
│  │ - No /dev/net/tun required   │   │
│  │ - Connected to your tailnet  │   │
│  └──────────────────────────────┘   │
│                                     │
│  ┌──────────────────────────────┐   │
│  │ Xvfb (Virtual Display)       │   │
│  └──────────────────────────────┘   │
│                                     │
│  ┌──────────────────────────────┐   │
│  │ Firefox                      │   │
│  └──────────────────────────────┘   │
│                                     │
│  ┌──────────────────────────────┐   │
│  │ x11vnc → noVNC               │   │
│  │ (Web-based VNC interface)    │   │
│  └──────────────────────────────┘   │
│                                     │
└─────────────────────────────────────┘
              │
              │ Port 6901
              ▼
     Your Web Browser
    (https://localhost:6901)

Built on kasmweb/firefox. Supervisor manages all four processes inside the container. The entrypoint starts Tailscale as root, connects to your tailnet, then drops to an unprivileged user before handing off to Kasm.

Troubleshooting

Auth key not found

task test-auth

# Or check manually
op whoami
op read "op://Private/Foxtail/auth_key"

Container won't start

task logs
task tailscale-status

Can't access noVNC

  • Verify the container is running: docker-compose ps
  • Check port 6901 isn't in use: lsof -i :6901
  • Check noVNC logs: task logs and search for novnc

Security notes

  • The Tailscale auth key is injected at runtime as an environment variable — never stored in an image layer
  • VNC credentials are set via .env which is gitignored
  • The container requires NET_ADMIN and NET_RAW capabilities for Tailscale userspace networking
  • Tailscale state persists in a Docker volume — delete it with docker volume rm foxtail_tailscale-state to force re-authentication

License

MIT

About

Firefox in Docker on your Tailscale network — access it from anywhere on your tailnet, right in a browser tab

Topics

Resources

Stars

Watchers

Forks

Releases

Packages

Used by

Contributors

Languages