Run a Cloudflare Tunnel in Docker
Run cloudflared in a container with either a dashboard-managed tunnel token or a locally managed config file and credentials.
On this page
A Cloudflare Tunnel publishes services on a private network, such as a NAS web interface, a printer, or an internal app, through Cloudflare without opening inbound firewall ports. This page shows how to run the cloudflared connector in Docker, either with a token from the Cloudflare dashboard or with a locally managed configuration file.
Choose a Management Mode
Remotely managed (recommended). You create the tunnel and its public hostnames in the Cloudflare Zero Trust dashboard, and the container needs only a token. Routing changes don't require touching the host.
Locally managed. You define the tunnel's routes in a
config.ymlon the host. Use this when you want routing kept in version control or managed alongside other host configuration.
Remotely Managed Tunnel
In the Cloudflare Zero Trust dashboard, open the Tunnels page under Networks, create a tunnel of type Cloudflared, and copy the token from the install command it shows.
Save the token to a file only root can read, so it doesn't appear in
docker inspector the process list:mkdir -p /opt/cloudflared printf '%s' '<TUNNEL_TOKEN>' > /opt/cloudflared/token chown 65532:65532 /opt/cloudflared/token chmod 400 /opt/cloudflared/tokenStart the connector:
docker run -d --name cloudflared --restart unless-stopped --network host \ -v /opt/cloudflared/token:/etc/cloudflared/token:ro \ cloudflare/cloudflared:latest tunnel run --token-file /etc/cloudflared/tokenIn the tunnel's settings, add a public hostname route (for example
app.example.com→http://localhost:8080). Newer dashboards call these Published application routes.
The image runs as the unprivileged user 65532, which is why the token file is owned by that UID.
Locally Managed Tunnel
Create the Tunnel and Credentials
Do the one-time authorization with cloudflared installed on a workstation (not in the container). tunnel login opens a browser and saves an account certificate to ~/.cloudflared/cert.pem. tunnel create writes the tunnel's credentials file:
cloudflared tunnel login
cloudflared tunnel create <TUNNEL_NAME>
cloudflared tunnel route dns <TUNNEL_NAME> app.example.comCreated tunnel <TUNNEL_NAME> with id <TUNNEL_ID>
Tunnel credentials written to ~/.cloudflared/<TUNNEL_ID>.jsonCopy only <TUNNEL_ID>.json to the Docker host. The container needs only the credentials file to run the tunnel. cert.pem is for management commands and should stay on the workstation.
Write the Configuration
Create a directory on the host, for example /opt/cloudflared, containing the credentials file and a config.yml:
{
"AccountTag": "<ACCOUNT_TAG>",
"TunnelSecret": "<TUNNEL_SECRET>",
"TunnelID": "<TUNNEL_ID>"
}tunnel: <TUNNEL_ID>
credentials-file: /etc/cloudflared/<TUNNEL_ID>.json
originRequest:
connectTimeout: 10s
ingress:
- hostname: printer.example.com
service: https://192.0.2.46:631
originRequest:
noTLSVerify: true
- hostname: drive.example.com
service: http://127.0.0.1:8080
- service: http_status:404The credentials-file path is the path inside the container. Ingress rules are matched top to bottom, and the last rule must be a catch-all with no hostname.
Warning
noTLSVerify: true turns off certificate checking between cloudflared and the origin. Limit it to individual devices with self-signed certificates (like the printer above) rather than setting it globally. Where possible, use originServerName or caPool instead.
Make the files readable by the container user:
chown -R 65532:65532 /opt/cloudflared
chmod 600 /opt/cloudflared/<TUNNEL_ID>.jsonStart the Container
docker run -d --name cloudflared --restart unless-stopped --network host \
-v /opt/cloudflared:/etc/cloudflared:ro \
cloudflare/cloudflared:latest tunnel --config /etc/cloudflared/config.yml run--network host lets ingress rules reach services on the host at 127.0.0.1 and on the LAN directly. If you'd rather keep cloudflared on a Docker network, attach it to the same network as the services and use their container names in service: URLs, such as http://app:8080.
Docker Compose
The token method as a Compose service:
services:
cloudflared:
image: cloudflare/cloudflared:latest
restart: unless-stopped
network_mode: host
command: tunnel run --token-file /etc/cloudflared/token
volumes:
- /opt/cloudflared/token:/etc/cloudflared/token:roCheck the Tunnel
docker logs -f cloudflaredLook for Registered tunnel connection lines. The tunnel also shows as Healthy in the dashboard. To publish a static site behind the tunnel on a NAS, see Docker macvlan Networking on Synology.
Sources
This article is in the public domain (CC0 1.0), code samples included. Use it however helps you.