# Running Cloudflare Tunnel with Docker If you want to expose an internal service to the internet **without opening inbound ports**, **without port-forwarding**, and without maintaining a complex reverse-proxy stack, **Cloudflare Tunnel** is one of the cleanest options. In this post, I’ll show a **token-based** Cloudflare Tunnel setup using **Docker**. The goal is a setup that is: - **Configless** (no `config.yml` required) - **Stateless** (easy to recreate anywhere) - **Restart-safe** (survives reboots) - **CI/CD friendly** (works great with secrets) --- ## What you’ll build By the end, you’ll have a `cloudflared` container running that: - Authenticates to your Cloudflare account using a **tunnel token** - Runs continuously on your server - Can reach your internal services via a Docker network (e.g., `app-net`) --- ## Prerequisites Before you start: - Docker is installed on your machine/server - You have access to **Cloudflare Zero Trust** - You created a **Tunnel** in Zero Trust - You have the **Tunnel Token** for that tunnel > You can find the token in Cloudflare Zero Trust → **Tunnels** → select your tunnel → **Run connector** (or equivalent). --- ## Why token-based (configless) setup? Using a token directly: - avoids managing `config.yml` and credentials files - makes the container **stateless** and reproducible - fits nicely into infrastructure automation and CI/CD pipelines In production, you should store the token as a secret (e.g., `.env`, Docker secrets, CI/CD secrets) instead of hardcoding it. --- ## Step-by-step: the script structure ### 1) Run Bash in “safe mode” We start with strict Bash flags: - exit on error - fail on undefined variables - fail on pipeline errors This prevents silent failures and makes the script more reliable. ### 2) Define the core variables We keep three things configurable: - container name - image version (pinned for reproducibility) - tunnel token ### 3) Start the Docker container We run `cloudflared` in detached mode and ensure it restarts automatically. We also attach it to a Docker network (`app-net`) so it can reach internal services by container name. ### 4) Run the tunnel with `--no-autoupdate` When using Docker, auto-updating inside the container is not desirable. You should update by changing the image tag. --- ## Full script (copy/paste) > Save as `cloudflare-tunnel.sh`, then run: `chmod +x cloudflare-tunnel.sh && ./cloudflare-tunnel.sh` ```bash #!/usr/bin/env bash set -euo pipefail # ============================================================ # Cloudflare Tunnel setup script (bash) # # This script: # - starts a cloudflared container # - runs a Cloudflare Tunnel using a token # ============================================================ # ------------------------------------------------------------ # 0) Base variables # ------------------------------------------------------------ CONTAINER="cloudflared" IMAGE="cloudflare/cloudflared:2025.11.1" TUNNEL_TOKEN="eyJhIjo..." # <-- replace with your real token # ------------------------------------------------------------ # 1) Start cloudflared container # ------------------------------------------------------------ docker run -d \ --name "$CONTAINER" \ --restart unless-stopped \ --network app-net \ "$IMAGE" \ tunnel --no-autoupdate run --token "$TUNNEL_TOKEN"