Skip to content

Tailscale ​

The tailscale lease backend creates a Tailscale auth key through the Tailscale API and injects it into fnox exec. Use it to join CI runners and containers to your tailnet without keeping a long-lived key.

Config typetailscale
ProducesTS_AUTHKEY (configurable with env_var)
Max duration90 days, set by Tailscale
RevocationYes; fnox deletes the key
NeedsAn OAuth client with the auth_keys write scope, or an API access token

Quick start ​

toml
[providers.op]
type = "1password"
vault = "Development"

[secrets]
TAILSCALE_OAUTH_CLIENT_ID = { provider = "op", value = "Tailscale/oauth client id", env = false }
TAILSCALE_OAUTH_CLIENT_SECRET = { provider = "op", value = "Tailscale/oauth client secret", env = false }

[leases.ts]
type = "tailscale"
tags = ["tag:ci"]
duration = "1h"
sh
fnox exec -- sh -c 'tailscale up --auth-key "$TS_AUTHKEY" --hostname "ci-$(hostname)"'

The key is ephemeral and pre-authorized by default, so the device joins without approval and disappears from the tailnet shortly after it goes offline.

Bootstrap credentials ​

fnox needs one of these, checked in this order:

  1. TAILSCALE_API_KEY, an API access token. It acts as the user who created it and expires within 90 days.
  2. TAILSCALE_OAUTH_CLIENT_ID and TAILSCALE_OAUTH_CLIENT_SECRET, an OAuth client with the auth_keys write scope. OAuth clients are not tied to a person, so prefer one for CI. Tailscale requires every key an OAuth client creates to carry tags that the client owns.

Store them as secrets with env = false, as in the quick start, or export them. fnox lease create -i ts prompts for the OAuth client when none is set.

Configuration ​

FieldRequiredDefaultDescription
tagsNononeACL tags applied to devices that use the key, such as ["tag:ci"]. Required with OAuth.
tailnetNo"-"Tailnet name. "-" is the tailnet the credentials belong to.
ephemeralNotrueDevices are removed from the tailnet after they go offline.
preauthorizedNotrueDevices join without manual approval.
reusableNofalseAllow more than one device to register with the key. A key that is not reusable is never cached, so every command gets a new one.
env_varNo"TS_AUTHKEY"Variable that receives the key. The Tailscale container image reads TS_AUTHKEY.
api_baseNo"https://api.tailscale.com"API base URL. Must use https; http is accepted only for localhost. Redirects are not followed.
durationNo"15m"How long the key can register devices. At most "90d".

A key that is not reusable is spent by the first device that registers with it, so fnox does not cache it: each fnox exec creates a new key and leaves the unused ones to expire, or to be removed with fnox lease cleanup. Set reusable = true to share one key between commands until the lease expires.

duration limits when the key can be used to register a device. A device that has already joined stays on the tailnet after the key expires.

Troubleshooting ​

Tailscale credentials not found ​

None of TAILSCALE_API_KEY, or TAILSCALE_OAUTH_CLIENT_ID with TAILSCALE_OAUTH_CLIENT_SECRET, is set when the lease is created. Under fnox exec, the lease is skipped with this warning unless a cached lease is still valid.

requested tags are invalid or not permitted ​

The key needs tags, and an OAuth client can only apply tags it owns. Add the tags to the OAuth client in the Tailscale admin console, and to tagOwners in your ACL.

HTTP 401 or 403 ​

The credentials are invalid or cannot write auth keys. For an OAuth client, check that it has the auth_keys write scope.

See also ​

MIT LicenseCopyright © 2026jdx.dev