$docker.recipes
·11 min read·Updated February 2026

Exposing Docker Services Securely with Cloudflare Tunnels (No Port Forwarding)

How to make your self-hosted Docker services accessible from the internet without opening any ports on your router using Cloudflare Tunnels.

cloudflaretunnelsecuritynetworkingdocker-compose

01The Port Forwarding Problem

Traditional self-hosting requires opening ports on your router and pointing DNS records at your home IP address. This works, but it comes with real downsides: your home IP is exposed, you need to handle DDoS protection yourself, your ISP might block ports or change your IP, and every open port is a potential attack surface. Cloudflare Tunnels flip this model. Instead of exposing ports to the internet, you run a lightweight connector (cloudflared) on your server that creates an outbound connection to Cloudflare's network. Traffic flows through Cloudflare to your tunnel to your service. No inbound ports, no exposed IP, and you get Cloudflare's DDoS protection and WAF for free. I switched from port forwarding to Cloudflare Tunnels a year ago and it simplified my networking dramatically. No more dynamic DNS, no more router configuration, no more worrying about ISP port blocking.

02Setting Up Cloudflare Tunnel with Docker

You need a Cloudflare account (free tier works) and a domain managed by Cloudflare DNS. Create a tunnel in the Cloudflare Zero Trust dashboard, and you'll get a tunnel token.
[docker-compose.yml]
1services:
2 cloudflared:
3 image: cloudflare/cloudflared:latest
4 container_name: cloudflared
5 restart: unless-stopped
6 command: tunnel --no-autoupdate run
7 environment:
8 - TUNNEL_TOKEN=${CF_TUNNEL_TOKEN}
9 networks:
10 - proxy
11
12networks:
13 proxy:
14 external: true

03Routing to Your Services

Configure routes in the Cloudflare dashboard or via config file. Each route maps a public hostname to an internal service: cloud.yourdomain.com → http://nextcloud:80 git.yourdomain.com → http://gitea:3000 grafana.yourdomain.com → http://grafana:3000 The cloudflared container needs to be on the same Docker network as the services it's routing to. Since we use the shared proxy network, it can reach any service connected to that network by container name. The beauty of this approach is that your services don't need to bind to the host network at all. Remove the ports directive from your compose files entirely — traffic flows through the tunnel, not through exposed ports.

You can run Cloudflare Tunnels alongside a reverse proxy like Traefik. Point the tunnel at Traefik, and let Traefik handle routing, middleware, and SSL termination internally.

04Adding Access Policies

Cloudflare Zero Trust lets you add authentication in front of your tunnels. You can require login via Google, GitHub, one-time PIN, or other identity providers before anyone reaches your service. This adds a layer of protection even before traffic reaches your server. For public services (a blog, a public API), set the route to bypass access policies. For private services (admin panels, monitoring), require authentication. This gives you the same tiered security as Authelia but managed entirely in Cloudflare's dashboard. The free tier includes 50 users, which is more than enough for personal use. It's one of the best free tools available for self-hosters. Browse our security category for Docker Compose configurations that include Cloudflare Tunnel setup alongside other networking solutions.

About the Author

Frank Pegasus

DevOps engineer and self-hosting enthusiast with over a decade of experience running containerized workloads in production. Creator of docker.recipes.