Skip to main content

Cloudflare Tunnel

A Cloudflare tunnel replaces the need for an open inbound port or a TLS certificate. The cloudflared daemon on the server opens an outbound connection to Cloudflare's network, and Cloudflare forwards HTTPS traffic from docs.trickfirerobotics.com through that connection to nginx on localhost:80.

1. Install cloudflared

curl -fsSL https://pkg.cloudflare.com/cloudflare-main.gpg \
| sudo gpg --dearmor -o /usr/share/keyrings/cloudflare-main.gpg

echo "deb [signed-by=/usr/share/keyrings/cloudflare-main.gpg] \
https://pkg.cloudflare.com/cloudflared $(lsb_release -cs) main" \
| sudo tee /etc/apt/sources.list.d/cloudflared.list

sudo apt update
sudo apt install -y cloudflared

Verify:

cloudflared --version

2. Authenticate with Cloudflare

cloudflared tunnel login

This opens a browser URL. Log in and select the trickfirerobotics.com zone. A certificate is saved to ~/.cloudflared/cert.pem.

3. Create the tunnel

cloudflared tunnel create trickfire-docs

This creates a tunnel and saves a credentials file at ~/.cloudflared/<tunnel-id>.json. Note the tunnel ID in the output — you'll need it in the next step.

4. Write the tunnel config

Create /etc/cloudflared/trickfire-docs.yml (named after the tunnel, not the generic config.yml, so it coexists with other tunnels on the same server):

tunnel: <tunnel-id>
credentials-file: /home/trickfire/.cloudflared/<tunnel-id>.json

ingress:
- hostname: docs.trickfirerobotics.com
service: http://localhost:80
- service: http_status:404

Replace <tunnel-id> with the UUID from step 3.

5. Add the DNS record

cloudflared tunnel route dns trickfire-docs docs.trickfirerobotics.com

This creates a CNAME record in Cloudflare pointing docs.trickfirerobotics.com<tunnel-id>.cfargotunnel.com. Cloudflare automatically sets the record to proxied (orange cloud).

Verify in the Cloudflare dashboard under DNS → Records.

6. Run as a systemd service

cloudflared service install always creates a unit named cloudflared.service regardless of any --config flag. To get a named service that can coexist with other tunnels, write the unit file manually:

sudo tee /etc/systemd/system/cloudflared-trickfire-docs.service << 'EOF'
[Unit]
Description=cloudflared - trickfire-docs
After=network-online.target
Wants=network-online.target

[Service]
TimeoutStartSec=15
Type=notify
ExecStart=/usr/bin/cloudflared --no-autoupdate --config /etc/cloudflared/trickfire-docs.yml tunnel run
Restart=on-failure
RestartSec=5s

[Install]
WantedBy=multi-user.target
EOF

sudo systemctl daemon-reload
sudo systemctl enable --now cloudflared-trickfire-docs

Check status:

sudo systemctl status cloudflared-trickfire-docs

The tunnel starts automatically on boot. Cloudflare will reconnect automatically if the server restarts.

7. Verify end-to-end

curl -I https://docs.trickfirerobotics.com
# HTTP/2 200

You should see HTTP/2 200 and Cloudflare response headers (cf-ray, server: cloudflare).

nginx config for tunnel

When using a Cloudflare tunnel, nginx only needs to handle plain HTTP on localhost. TLS termination is done by Cloudflare, so no certificate is needed. The config in scripts/nginx.conf is already written for this setup.

Key points in the nginx config:

  • Listens only on 127.0.0.1:80 — not reachable from outside
  • No SSL directives
  • try_files for Docusaurus SPA routing
  • Immutable cache headers for hashed JS/CSS assets

Troubleshooting

Tunnel shows as unhealthy in Cloudflare dashboard:

sudo journalctl -u cloudflared-trickfire-docs -f

Common causes: server can't reach cloudflare.com, or credentials file path is wrong.

Site loads but returns 502: Check nginx is running and the build directory exists:

sudo systemctl status nginx
ls /home/trickfire/docs/build/index.html

DNS not resolving: DNS propagation can take a few minutes. The cloudflared tunnel route dns command should update it instantly in Cloudflare, but local resolvers may cache the old value.