Skip to content

Tailscale extension

Set up and operate the bundled Tailscale connectivity extension — auth key, tailnet prerequisites, Funnel for public delivery, state, and clean removal.

For people running agent work7 pages in this section

The Tailscale extension is the first-party connectivity provider that ships inside CompozyOS. It is what carries traffic from your other devices — and, when you choose, from the internet — to the gateway's local listeners.

It runs the Tailscale node inside the CompozyOS process (through tsnet), against your Tailscale account. You do not install or run a separate Tailscale client on the daemon machine, and CompozyOS operates no relay, server, or account on your behalf. The extension is installed automatically; you only enable it and give it a key.

What you get

TierTransportAddress
PrivateYour tailnethttps://compozy-gateway.<your-tailnet>.ts.net:8443
PublicTailscale Funnel (internet-facing)https://compozy-gateway.<your-tailnet>.ts.net

The node always joins your tailnet as compozy-gateway, so the address is predictable. Devices that use the private address must run Tailscale and belong to the same tailnet; the public Funnel address works from any network.

Before you start

  • A Tailscale account and access to its admin console.
  • The gateway ceiling is raised: compozy config set gateway.enabled true.

Step 1: Prepare your tailnet

In the Tailscale admin console:

  1. Under DNS, enable MagicDNS and HTTPS certificates. The gateway serves HTTPS on both tiers and cannot start without a certificate domain.
  2. Only if you will use the public tier: allow Funnel for the node, by accepting the funnel policy when Tailscale prompts for it or by adding the funnel node attribute to your tailnet policy file.

Skipping either produces a clear failure later — tailnet HTTPS certificate domain is unavailable or a refused Funnel listener — so it is worth doing first.

Step 2: Create and bind an auth key

Create an auth key in the admin console under Settings → Keys. Bind it through a hidden prompt:

compozy extension secrets set tailscale --env TS_AUTHKEY

The extension manifest declares only the variable name; CompozyOS stores the value through the extension secret-binding surface and never returns it in status or diagnostics. Replace a rotated key by running the same command again; remove the binding with compozy extension secrets unset tailscale --env TS_AUTHKEY.

Step 3: Enable the extension

compozy extension enable tailscale

The extension declares that it carries gateway traffic on the gateway.private and gateway.public channels, so the first enable refuses with a network requirement digest and prints the exact confirmation command to run:

compozy extension enable tailscale --confirm-network-requirement <digest>

Confirming the digest is consent to exactly that declaration — an update that changes it asks again. compozy extension preview tailscale shows the current digest and whether a confirmation is pending; on a fresh install the extension arrives enabled, and this consent is the only step left.

Step 4: Activate it for a tier

compozy gateway provider enable tailscale --tier private --source bundled
compozy gateway provider enable tailscale --tier public --source bundled

Enable only the tiers you use; the same provider can serve both. Activation makes nothing reachable by itself — a tier goes live only when it also has an enabled surface (quickstart, webhooks).

Verify

compozy gateway status -o json

A healthy activation shows the provider and a verified address (output trimmed):

{
  "providers": [{ "name": "tailscale", "tier": "private", "health": "healthy" }],
  "addresses": [
    {
      "tier": "private",
      "address": "https://compozy-gateway.<your-tailnet>.ts.net:8443",
      "live": true
    }
  ]
}

Two more inspection commands are specific to the extension: compozy extension status tailscale reports the runtime state — Missing Env: TS_AUTHKEY there is the clearest sign the key was never bound — and compozy extension logs tailscale streams the provider's own log lines.

Configuration

There is no [extensions.tailscale] section in config.toml, and nothing to hunt for: the extension's entire configurable surface is the TS_AUTHKEY binding above. The node hostname (compozy-gateway), the tailnet ports (8443 private, 443 public), and the forwarding limits are fixed. The daemon-side tunables that shape verification live under the [gateway] keys.

The first activation is the slow one

Three things happen once, in sequence, before an address can be verified: Tailscale issues the HTTPS certificate for the new node, the public DNS name is published, and the daemon fetches its one-time challenge through the address. Certificate issuance alone can take up to a minute, and public DNS can lag behind it.

While that converges, the daemon keeps the listener staged but unadvertised and retries with backoff — status shows the provider establishing or degraded, which is a wait state, not a failure. Public addresses are resolved through an authenticated DNS-over-TLS resolver (gateway.verify.public_dns_resolver, default 1.1.1.1:853) rather than this machine's resolver, so a private MagicDNS answer can never stand in for the public route.

Private verification travels through the embedded node's own network stack. The provider opens a loopback TCP relay with one fixed destination: its private HTTPS listener. The daemon sends TLS through that relay and still authenticates the advertised hostname, requires the exact tier challenge response, and rejects redirects. This works without a host Tailscale installation or a host route to Tailnet addresses. The relay is not an advertised address and closes with the tier. Public Funnel verification never uses this private transport.

Where state lives

The embedded node keeps its identity, and the private key of its issued certificate, in $COMPOZY_HOME/gateway/tailscale (owner-only permissions). Treat the directory as secret material. Deleting it de-authenticates the node: the next activation needs a valid auth key and registers a fresh node.

Troubleshooting

SymptomCause and fix
TS_AUTHKEY binding is requiredNo key bound, or the binding was removed. Run step 2. Expired and revoked keys fail the same way.
tailscale: tailnet HTTPS certificate domain is unavailableMagicDNS or HTTPS certificates are off. Enable both under DNS in the admin console.
tailscale: provision HTTPS certificate: …Certificate issuance failed or timed out. Confirm HTTPS certificates are enabled, then let the daemon retry.
tailscale: listen for public gateway: …Funnel is not allowed for this node. Grant the funnel policy in the admin console (step 1) and retry.
endpoint DNS resolution failedThe probe resolver could not resolve the endpoint. For public Funnel, check DNS propagation and the configured public resolver.
endpoint connection refused or endpoint network unreachableCheck provider listener readiness and the network route. The embedded private provider uses its own tsnet transport.
endpoint probe timed outCheck provider routing, listener readiness, and certificate provisioning. The daemon retries while keeping the endpoint unadvertised.
endpoint TLS certificate verification failedCheck the endpoint hostname, certificate validity and system trust store. TLS verification must remain enabled.
challenge returned HTTP … or challenge nonce did not matchThe endpoint did not return the exact assigned tier challenge. Inspect forwarding and tier binding; a successful request to / is not sufficient proof.
endpoint transport failed (or older endpoint probe failed)Inspect provider health and network connectivity. This message alone does not identify DNS propagation as the cause.
Provider degraded with forward target is unavailableThe tier's loopback listener stopped. Check daemon health with compozy status, then compozy gateway status -o json.

Remove it cleanly

Four artifacts outlive a casual disable; remove all of them when you are done:

compozy gateway provider disable tailscale --tier private
compozy gateway provider disable tailscale --tier public
compozy extension disable tailscale
compozy extension secrets unset tailscale --env TS_AUTHKEY

Then delete the node state directory ($COMPOZY_HOME/gateway/tailscale) and, in the Tailscale admin console, delete the compozy-gateway machine and revoke the auth key if it is still valid.

On this page