Servers and containers
Run Teitunnel's routes on a VPS, a cloud VM (AWS, Azure, GCP) or in Docker, without the app.
The app is for your computer. On a server, teitunnel-cli does the same job from the
terminal: it adds routes through the same reviewed plans, and runs the connectors itself
or as a system service.
The API token
A server usually has no keychain, so Teitunnel doesn't store the token there. Give it to each command instead:
export CLOUDFLARE_API_TOKEN=… # or
export CLOUDFLARE_API_TOKEN_FILE=/run/secrets/cloudflare_api_tokenThe token is checked, used for that command and never written to disk. Create it in the
Cloudflare dashboard with Cloudflare Tunnel · Edit, DNS · Edit and Zone · Read
(add the two Access permissions if routes should require a login). On a machine that
does have a keychain, teitunnel-cli setup stores it there once instead.
Any Linux server (VPS, EC2, Azure VM, Compute Engine)
teitunnel-cli route add app.example.com 3000 --yes # creates this server's tunnel
sudo -E teitunnel-cli always-on on # a systemd service, started at boot
teitunnel-cli routes # statusRun as root, always-on installs a system unit in /etc/systemd/system: it starts at
boot, restarts when it stops, and runs sandboxed (no new privileges, a read-only system,
writing only its log). As a normal user it installs a user unit instead, which stops at
logout unless lingering is on (loginctl enable-linger). always-on off removes it;
always-on status shows it.
Nothing about the cloud provider matters: the connector only makes outbound connections to
Cloudflare, so no inbound port, load balancer or security-group rule is needed. Keep the
origin (your app) listening on localhost.
Without systemd, run teitunnel-cli up under the supervisor you have (runit, s6,
OpenRC, a @reboot job): it runs every tunnel of the machine in the foreground and stops
them on SIGTERM.
Docker
The image has the CLI and Cloudflare's own cloudflared, runs as a non-root user, keeps
its state in /data, and runs up by default:
docker volume create teitunnel
docker run --rm -e CLOUDFLARE_API_TOKEN -v teitunnel:/data \
ghcr.io/teispace/teitunnel route add app.example.com http://web:80 --yes
docker run -d --name teitunnel --restart unless-stopped \
-e CLOUDFLARE_API_TOKEN -v teitunnel:/data --network my-app ghcr.io/teispace/teitunnelRoutes point at other containers by name (http://web:80) when they share a network.
The container's health check is teitunnel-cli routes --check, which fails unless every
route is live.
Docker Compose
services:
teitunnel:
image: ghcr.io/teispace/teitunnel:latest
environment:
CLOUDFLARE_API_TOKEN_FILE: /run/secrets/cloudflare_api_token
secrets: [cloudflare_api_token]
volumes: [teitunnel:/data]
restart: unless-stopped
web:
image: nginx:alpine
secrets:
cloudflare_api_token:
file: ./cloudflare_api_token
volumes:
teitunnel:docker compose up -d
docker compose run --rm teitunnel route add app.example.com http://web:80 --yes
docker compose restart teitunnelKubernetes
Run one replica with the token in a Secret and /data on a small volume:
apiVersion: apps/v1
kind: Deployment
metadata: { name: teitunnel }
spec:
replicas: 1
selector: { matchLabels: { app: teitunnel } }
template:
metadata: { labels: { app: teitunnel } }
spec:
containers:
- name: teitunnel
image: ghcr.io/teispace/teitunnel:latest
env:
- name: CLOUDFLARE_API_TOKEN
valueFrom: { secretKeyRef: { name: cloudflare, key: api-token } }
volumeMounts: [{ name: data, mountPath: /data }]
volumes:
- name: data
persistentVolumeClaim: { claimName: teitunnel }Route to Services by their cluster name, e.g.
kubectl exec deploy/teitunnel -- teitunnel-cli route add app.example.com http://web.default.svc:80 --yes.
Web dashboard and API
teitunnel-cli serve runs the tunnels like up and adds a small dashboard: every
tunnel and route with its status, adding and removing routes and tunnels (each change is
reviewed before it's applied), and shares on your domains.
teitunnel-cli serve --set-password # once; or set TEITUNNEL_WEB_PASSWORD
teitunnel-cli serve # http://127.0.0.1:8765It listens on this machine only. To reach it from elsewhere, publish it through Teitunnel
itself with a login (teitunnel-cli route add admin.example.com http://127.0.0.1:8765 --allow you@example.com), or pass --listen 0.0.0.0:8765 --allow-remote behind a TLS
proxy with --secure-cookies.
For automation, create an API key (teitunnel-cli api-key create deploy, shown once) and
call the API with Authorization: Bearer <key>. A change is two calls: POST /api/preview returns the plan and its fingerprint, and POST /api/apply applies exactly
that plan (or answers 409 if Cloudflare changed in between). The API is described at
/api/openapi.json.
Temporary shares from a server
teitunnel-cli share 8080 prints a random trycloudflare.com address for as long as the
command runs; --on demo.example.com uses your own domain instead.