Using CloudFlare Tunnels as a Reverse Proxy

There are some issues self-hosting services from residential ISP connections. Some of them include:

  • Dynamic IP address allocation
  • Support for IPv6 routing
  • NAT and port forwarding setup
  • Certain well-known ports are sometimes blocked
  • Reverse proxy setup
  • Privacy issues with exposing home IP address via DNS

All of these can be addressed by using a CloudFlare tunnel as a part of the container setup of the self-hosted service. Most services require a reverse proxy proxy such as nginx which can be replaced by the CF tunnel configuration as well. We can also optionally stop attaching ports to the container host creating less potential for port conflict when hosting multiple services on a home server.


This guide assumes you have:

  • a registered domain with DNS being handled by CloudFlare
  • a working docker / docker-compose (or similar) setup on your home server

Create a Tunnel at CloudFlare

  1. Using the CloudFlare web dashboard navigate to Zero Trust > Access > Tunnels.
  2. Click “Create a tunnel”
  3. Name the tunnel. I recommend using the same name as the service being hosted.
  4. Copy the generated tunnel token

This token should be kept secret. If lost, it can be rotated later so there is little reason to store it anywhere other than the .env file of our docker-compose setup.

Establish a Tunnel Connection

In your docker-compose setup on your home server add the cloudflared container and set the TUNNEL_TOKEN from the previous step.

version: "3"
    image: cloudflare/cloudflared
    restart: unless-stopped
    command: tunnel run
TUNNEL_TOKEN="<CloudFlare Tunnel Token>"

Then bring up the cluster with docker-compose up. You should see the connection in the CloudFlare dashboard.

Add Reverse Proxy Rules

In the CloudFlare dashboard you should see the ability to route traffic from a public DNS entry to the docker-compose cluster. To get back to the routing configuration in CloudFlare navigate to Zero Trust > Access > Tunnels, click on the tunnel name, click “Configure” and navigate to the Public Hostname tab.

Similar to nginx you can now route subdomain (and optional path prefix) to other containers in the same docker compose cluster. Note you do not need to expose ports in the docker-compose. For the host name, simply use the container name defined in the docker-compose.yml. You can even route the domain apex to a specific container.

Disclaimer: This blog post is for educational purposes only. Test your setup in a non-production environment before implementing.

This post is licensed under CC BY 4.0 by the author.