FLY-VPN(1)

NAME

fly-vpnDisposable global VPN in seconds ephemeral Tailscale exit node on Fly.io with a terminal UI

SYNOPSIS

$brew install invilso/tap/fly-vpn

INFO

107 stars
6 forks
0 views

DESCRIPTION

Disposable global VPN in seconds ephemeral Tailscale exit node on Fly.io with a terminal UI

README

🛡️ Fly VPN — disposable global VPN in seconds

One API key. One command. Private exit node in 18 regions.

Fly VPN spins up an ephemeral Tailscale exit node on Fly.io, auto-configures your tailnet, routes your traffic, and destroys everything the moment you press Stop.

# 1. Clone & install
git clone https://github.com/invilso/fly-vpn.git && cd fly-vpn && bash install.sh

2. That's it. Paste your Tailscale API key when prompted.

ACL, auth keys, device cleanup — all automatic.

No always-on VM. No manual ACL editing. No auth key management. No billing anxiety. No shared IPs.

Under the hood: WireGuard-powered Tailscale mesh + Fly Machines that cold-start in ~3 seconds and bill per-second. A typical session costs a fraction of a cent.

Fly VPN demo


Use cases

  • 🔒 Test geo-restricted APIs — hit endpoints as if you're in Frankfurt, Tokyo, or São Paulo
  • 🛡️ Secure browsing on public Wi-Fi — route traffic through your own ephemeral node
  • 🧪 QA regional content — verify localization, pricing, or CDN behavior per region
  • 🏗️ Dev/staging access — reach region-locked services without a permanent VPN
  • 🎯 Ad & SEO audits — see what users in different markets actually see
  • 🚀 Demo day — show your product from a client's region in real time
  • 🎮 Gaming — connect to region-locked servers or get a fresh IP in seconds

Tech stack

LayerTechnology
LanguagePython 3.14
TUI frameworkTextual
Cloud runtimeFly.io (ephemeral machines)
VPN meshTailscale (WireGuard-based)
Package manageruv
Build backendHatchling
LinterRuff

Requirements

  • macOS or Linux (Windows is not supported)
  • Python 3.14+
  • Fly.io account with a payment method on file
  • Fly.io CLI (fly) — auto-installed by install.sh
  • Tailscale account + API key (handles everything automatically) — or a self-hosted Headscale server with an auth key

Why teams love it

  • 🔑 One API key — ACL, auth keys, cleanup — all automatic
  • 🌍 18 gateway regions — pick from dropdown, launch in seconds
  • One-click launch from a polished terminal UI
  • 🔗 Auto-connect to the exit node when it comes online
  • 🧹 Safe teardown on Stop / Quit / Signal
  • 💸 Cost-aware by design — ephemeral infra only
  • 🛟 Watchdog mode to remove orphaned Fly apps

What happens when you press Launch

  1. 🔑 ACL check — ensures your tailnet allows exit nodes (idempotent, via API)
  2. 🔐 Auth key — generates a single-use, ephemeral key (valid 30 days, consumed instantly)
  3. 🚀 Fly machine — starts a Tailscale container in your chosen region (~3 s cold start)
  4. 🔗 Auto-connect — waits for the node to appear, then routes your traffic through it
  5. 🗑️ Press Stop — disconnects, destroys machine + app, removes device from tailnet

You provide one API key. Everything else is automated, per-session, and ephemeral.


Quick start

git clone https://github.com/invilso/fly-vpn.git
cd fly-vpn
bash install.sh

The installer will ask for one thing: your Tailscale API key (generate here).

That single key gives Fly VPN everything it needs:

  • ACL auto-configuration — exit-node tags, approvals, permissions
  • Per-session auth keys — generated on every Launch, single-use, ephemeral
  • Instant device cleanup — node deleted from tailnet on Stop

No auth key to create. No ACL to edit. No manual steps.

Full installer flow
  1. Installs uv and Fly CLI (if missing)
  2. Prompts for TAILSCALE_API_KEY
  3. Asks if you use Headscale — prompts for TS_LOGIN_SERVER and manual auth key
  4. Syncs dependencies
  5. Checks Fly.io auth (opens fly auth login if needed)
  6. Registers desktop entry (macOS Applications / GNOME menu)
  7. Optionally sets up daily watchdog (orphan-app safety net)

Why Fly.io?

CriteriaFly.ioAWS EC2 / LightsailDigitalOceanHetzner
Cold start~3 s (Machines API)30–60 s30–55 s10–30 s
Per-second billing❌ (per-hour)❌ (per-hour)❌ (per-hour)
Regions18 worldwide30+155
Destroy on stopnative (Machines)manual / APImanual / APImanual / API
Free tier3 shared VMs, 160 GB out750 h/mo (t2.micro)

Bottom line: Fly Machines are pay-per-second, start in seconds, and auto-destroy — ideal for ephemeral workloads. No idle costs when the VPN is off.

Cost estimate

UsageFly.io cost
1 h/day, 30 days (shared-cpu-1x, 256 MB)~$0.50–1.00/mo
4 h/day, 30 days~$2–4/mo
Always-on equivalent (730 h)~$3.50/mo

Compare: a $5/mo DigitalOcean droplet runs 24/7 whether you need it or not. Fly VPN runs only when you click Launch.


Why Tailscale?

CriteriaTailscaleOpenVPNWireGuard (raw)Cloudflare WARP
Setup complexityZero-config meshCerts + config filesKey exchange + routingManaged (no self-host)
NAT traversal✅ built-in (DERP)Manual / STUNManualN/A
Exit node support✅ nativeManual iptablesManual iptables
Auto-approve nodes✅ via ACL tagsN/A
Ephemeral nodes✅ (auto-expire keys)N/A
ProtocolWireGuard underneathTLS / UDPWireGuardWireGuard (modified)

Bottom line: Tailscale gives us WireGuard performance with zero manual key management. Ephemeral auth keys + ACL auto-approval = nodes that appear, serve traffic, and vanish — no cleanup.


Approaches compared

ApproachSpin-upMonthly cost (casual)CleanupMulti-region
Fly VPN (this project)~5 s<$1automatic✅ 18 regions
Commercial VPN (Mullvad, PIA…)instant$5–10N/A✅ but shared IPs
Self-hosted WireGuard on VPS30–60 s$5+ (always-on)manualone region per VPS
SSH SOCKS proxyinstant$5+ (always-on VPS)manualone region per VPS
Outline VPN (Jigsaw)30–60 s$5+ (always-on)manualone region per VPS
Cloud Functions + proxyvariespay-per-requestautomatic✅ but complex

Fly VPN wins when you need: your own IP (not shared), multi-region on demand, zero idle cost, and fully automated lifecycle.


Fly VPN vs fly-tailscale-exit

patte/fly-tailscale-exit (1.6k ⭐) pioneered the idea of running Tailscale exit nodes on Fly.io. Fly VPN builds on the same concept but wraps it into a zero-config product instead of a DevOps guide.

fly-tailscale-exitFly VPN
Setup13 manual stepsbash install.sh + paste API key
Keys to manageAuth key (create manually, keep alive)None — generated per session
ACL configurationManual (copy-paste into admin)✅ Automatic on first launch
Exit node approvalManual in Tailscale admin✅ Auto-approved via ACL
Connect to exit nodetailscale up --use-exit-node=…✅ Automatic
Switch regionEdit config + redeployPick from dropdown → Launch
Cleanup on stop❌ Machine keeps running (💸)✅ App + machine + device destroyed
Deployment modelfly deploy (Dockerfile + fly.toml)fly m run (no deploy, no repo)
GitHub org required
UINone (CLI only)Textual TUI with hotkeys
Cost safety netNoneWatchdog (cron/launchd)
Headscale support
Desktop integrationNonemacOS .app / GNOME menu

tl;dr — fly-tailscale-exit requires you to create keys, edit ACL, set up a GitHub org, deploy with Dockerfile, and manually connect. Fly VPN: paste one API key, press Launch.

Credit where due — fly-tailscale-exit proved the idea works. Fly VPN just makes it zero-config.


Tailscale setup (required once)

Just an API key — everything else is automatic

Generate an API key at Tailscale Admin → Keys → API keys and paste it during install (or add to .env):

TAILSCALE_API_KEY=tskey-api-…

On every Launch, Fly VPN will:

StepWhatWhen
ACL setupAdds tag:ephemeral-vpn, exit-node attrs, auto-approversFirst launch (idempotent, skips if already configured)
Auth keyGenerates single-use, ephemeral, pre-authorized keyEvery launch
Device cleanupDeletes the node from your tailnetEvery stop

You can also trigger ACL setup manually:

fly-vpn --setup-acl
Manual auth key (without API key)

If you prefer not to use an API key, create a reusable, ephemeral, pre-authorized auth key tagged with tag:ephemeral-vpn at the Tailscale admin console:

TAILSCALE_AUTHKEY=tskey-auth-…

You'll also need to configure ACL manually:

{
  "tagOwners": {
    "tag:ephemeral-vpn": ["autogroup:owner"]
  },
  "nodeAttrs": [
    {
      "target": ["tag:ephemeral-vpn"],
      "attr": ["can-be-exit-node"]
    }
  ],
  "autoApprovers": {
    "exitNode": ["tag:ephemeral-vpn"]
  }
}

With a manual auth key, ephemeral nodes auto-remove in ~5–30 min instead of instantly.

```

Self-hosted Tailscale (Headscale)

Headscale is an open-source, self-hosted implementation of the Tailscale coordination server. Fly VPN works with Headscale out of the box — just set the right env vars.

1. Set TS_LOGIN_SERVER in .env

The installer will ask if you use a self-hosted coordination server. If you skipped it, add the variable manually:

TS_LOGIN_SERVER=https://hs.example.com

The Fly exit node will register with your Headscale instance instead of login.tailscale.com.

2. Make sure your local client is on the same Headscale server

tailscale login --login-server=https://hs.example.com

Both the local machine and the Fly exit node must be on the same Headscale tailnet.

3. Create an auth key via Headscale

# Headscale CLI
headscale preauthkeys create --user your-user --reusable --ephemeral

or via Headscale API

curl -X POST https://hs.example.com/api/v1/preauthkey
-H "Authorization: Bearer $HS_API_KEY"
-d '{"user":"your-user","reusable":true,"ephemeral":true}'

Put the key in .env as usual:

TAILSCALE_AUTHKEY=your-headscale-preauth-key

4. ACL policy

Headscale ACLs live in the config file (typically /etc/headscale/acl.yaml or acl.json). The same policy applies — allow the ephemeral tag and auto-approve exit nodes:

# acl.yaml
tagOwners:
  tag:ephemeral-vpn:
    - your-user

autoApprovers: exitNode: - tag:ephemeral-vpn

5. Minimal .env for Headscale

TAILSCALE_AUTHKEY=your-headscale-preauth-key
TS_LOGIN_SERVER=https://hs.example.com

No TAILSCALE_API_KEY needed — Headscale removes ephemeral nodes immediately on disconnect.

6. Comparison

FeatureTailscale SaaSHeadscale
Auth keysAdmin consoleheadscale preauthkeys create
ACL configWeb UIConfig file on server
Instant device cleanup (TAILSCALE_API_KEY)✅ Supported❌ Not compatible — not needed
Ephemeral node auto-remove~5–30 minImmediate (on disconnect)

Run

# Preferred
fly-vpn

Alternatives

uv run fly-vpn python main.py

Keyboard shortcuts

KeyAction
lLaunch exit node
sStop and cleanup
tToggle dark/light theme
qQuit

Safety model

  • Exit-node usage is explicit (manual Launch)
  • Exit route is removed during teardown
  • Fly app/machines are destroyed on cleanup paths
  • Tailscale device is removed instantly when API key is set (otherwise auto-removes in ~5–30 min)
  • Auth keys are generated per-session, single-use, ephemeral (when using API key)
  • Watchdog can be run from cron/CI to enforce cleanup

Fly VPN does not replace your identity/privacy model. It automates infra lifecycle and routing ergonomics.


Watchdog mode

Cleanup helper for CI/cron/manual recovery:

python main.py --watchdog

It checks for orphaned app resources and destroys them to prevent charges.

Tip: great as a daily cron safety net. The installer will offer to set this up automatically.


Troubleshooting quick hits

  • "Fly.io not authenticated" → run fly auth login
  • Region timeout / no capacity → switch region in UI and retry
  • Node appears but no auto-connect → run tailscale set --exit-node=fly-vpn-exit
  • Want hard cleanup now → run watchdog: python main.py --watchdog

Architecture (clean layered design)

flyexit/
├── app.py            # UI layer (Textual only)
├── session.py        # business orchestration (preflight/launch/connect/teardown)
├── fly_ops.py        # Fly.io adapter (CLI operations)
├── tailscale.py      # Tailscale adapter (local CLI)
├── tailscale_api.py  # Tailscale Admin API client (ACL, auth keys, devices)
├── acl_setup.py      # ACL business logic + CLI entry-point (--setup-acl)
├── diagnosis.py      # friendly failure hints
├── config.py         # persisted user config
├── constants.py      # defaults, regions, timeouts
├── styles.py         # UI styling
└── watchdog.py       # headless safety cleanup

main.py # entry-point (app / watchdog / setup-acl) install.sh # installer/uninstaller

Design principle: UI-only app layer + enum-based session orchestration + thin infrastructure adapters.


Uninstall

bash install.sh uninstall

Roadmap

  • Drop flyctl dependency — replace all CLI calls with the Fly.io Machines API directly, removing the only heavy external binary requirement
  • Windows support — make installer, Tailscale integration, and routing work on Windows (PowerShell installer, Windows-native tailscale.exe path detection)
  • Linux app package.deb / .rpm / AUR package for one-line install on Linux
  • History screen in TUI — a dedicated tab showing past sessions with region, duration, and cost (already implemented via --stats)
  • Multiple simultaneous nodes — spin up nodes in several regions at once and switch between them without teardown
  • Auto-rotate on region failure — if the chosen region has no capacity, automatically retry the next closest region instead of failing
  • Connection latency indicator — show live ping to the exit node in the status bar so you can pick the fastest region
  • Long-session billing alert — warn in the TUI after a configurable threshold (e.g. 2 h) to prevent accidental charges from a forgotten session
  • macOS menu bar widget — a tiny status icon that shows VPN on/off state and lets you stop the session without opening the full TUI
  • Homebrew tapbrew install invilso/tap/fly-vpn for a one-liner install without cloning the repo
  • Custom machine size picker — let the user choose Fly Machine CPU/RAM tier (e.g. shared-cpu-1x vs performance-2x) from the TUI
  • Config profiles — save named profiles (preferred region, machine size, etc.) and switch between them quickly

License

MIT

SEE ALSO

clihub3/18/2026FLY-VPN(1)