Management scripts for wiring self-hosted apps behind a Cloudflare Tunnel + Traefik reverse proxy.
These scripts are designed to be run by an AI agent (e.g. opencode) that reads the README, understands the stack, and executes the scripts on your behalf. You can run them yourself too, but the interactive prompts and --dry-run flags make them agent-friendly.
your-domain.com
└── Cloudflare Access (Zero Trust auth)
└── Cloudflare Tunnel (no open ports)
└── Traefik (reverse proxy, wildcard TLS)
└── Docker container
Copy .env.example to .env and fill in the values:
| Key | Description |
|---|---|
CF_API_TOKEN |
Cloudflare API token with Tunnel Edit, Access Edit, DNS Edit permissions |
CF_ACCOUNT_ID |
Cloudflare account ID (from tunnel token or dashboard) |
CF_TUNNEL_ID |
Cloudflare Tunnel ID |
DOMAIN |
Your domain (e.g. example.com) |
CF_ACCESS_EMAIL |
Email allowed through Zero Trust Access |
TRAEFIK_NETWORK |
Docker network Traefik listens on (default: traefik-proxy) |
GLANCE_CONFIG_DIR |
Path to Glance config dir (enables auto dashboard integration) |
./add-app --name <subdomain> --port <container-port> [options]Creates a public hostname, DNS record, Zero Trust Access policy, adds Traefik labels to your compose file, and optionally adds the app to your Glance dashboard.
| Flag | Description |
|---|---|
--name |
Subdomain (e.g. outline → outline.yourdomain.com) |
--port |
Port the container listens on internally |
--dir |
Path to the app's docker-compose directory (patches it automatically) |
--service |
Which service in the compose file to add labels to (defaults to --name) |
--no-access |
Skip Cloudflare Access setup (app will be public) |
--no-restart |
Don't restart the stack after patching |
--dry-run |
Show what would happen without making changes |
- Cloudflare Tunnel — adds/updates a public hostname rule pointing to Traefik
- DNS — creates/corrects a CNAME record → tunnel
- Cloudflare Access — creates a Zero Trust self-hosted app with an email-allow policy
- Docker Compose — patches the compose file with Traefik labels and
traefik-proxynetwork membership, then restarts the stack - Glance Dashboard — interactively prompts to add the app to the monitor and/or services pages
./add-app --name outline --port 3000 --dir ~/outline
./add-app --name gitea --port 3000 --dir ~/gitea --service server
./add-app --name someapp --port 8080 --dir ~/someapp --dry-run
./add-app --name someapp --port 8080 # tunnel + DNS + Access only./remove-app --name <subdomain> [options]Reverses add-app — removes the Tunnel ingress, DNS record, Access app, Traefik labels, and Glance dashboard entries. Each step is confirmed interactively.
| Flag | Description |
|---|---|
--name |
Subdomain to remove (e.g. outline → outline.yourdomain.com) |
--dir |
Path to the app's docker-compose directory |
--service |
Which service in the compose file to clean up (defaults to --name) |
--no-access |
Skip Cloudflare Access removal |
--force |
Skip all confirmations (use defaults — careful) |
--dry-run |
Show what would be removed without making changes |
- Cloudflare Tunnel — removes the public hostname ingress rule
- DNS — deletes the CNAME record
- Cloudflare Access — deletes the Zero Trust app and its policies
- Docker Compose — offers a choice: strip Traefik labels only, stop the stack, or stop + remove volumes
- Glance Dashboard — removes entries from the monitor and services pages, then restarts Glance
./remove-app --name outline --dir ~/outline
./remove-app --name someapp --dry-run
./remove-app --name someapp # CF/DNS/Access only, skip compose
./remove-app --name outline --dir ~/outline --forceApp returns 404 from Cloudflare — tunnel ingress rule not saved, or the app isn't joined to the Traefik network. Run docker inspect <container> and check for the traefik network in its networks.
Traefik shows "no route matched" — verify the traefik.http.routers.<name>.rule label matches the hostname exactly.
Access blocks you — make sure you're logged in with the email matching CF_ACCESS_EMAIL. Add more emails in the Zero Trust dashboard under Access > Applications.
API token errors — confirm the token has Tunnel Edit + Access Edit + DNS Edit permissions.