When I build containerized apps that need to be exposed on the internet, I usually need to forward ports, set up let's encrypt and reverse proxy some random port. In this blog I'll show you how to ditch all of that in favor of 1 secure Cloudflare Tunnel in a docker-compose file.
Part 1: Setting Up the App
First, let's create and configure our state of the art app, named
<html> <head> <meta charset="utf-8" /> <meta name="viewport" content="width=device-width, initial-scale=1.0" /> </head> <body> <h1>🤓 Hello world! 👋</h1> <p>Answer from container.</p> </body> </html>
We'll host it using the latest standard NGINX container. The
docker-compose.yml looks like this:
version: "3" services: web: image: nginx:latest volumes: - ./index.html:/usr/share/nginx/html/index.html restart: unless-stopped
Notice how we are not exposing port 80 to the host!
Part 2: Setting Up the Cloudflare Tunnel
To set up a Cloudflare Tunnel for your app, you can follow the instructions in this guide: Creating a Tunnel through the Cloudflare Dashboard. Navigate to one.dash.cloudflare.com > Access > Tunnels and click the "Create" button.
When prompted, specify the hostname as http://web:80. The web refers to the service named web in our
Once the tunnel is created, click the "Configure" button and scroll down to find the Tunnel token. Copy this token as you will need it in the next steps.
Part 3: Include the tunnel as a service
Now that we've created our tunnel, we can configure the tunnel on our server side. Let's create a
tunnel.env file to separate the token from our
Our compose file could end up in a repository, our
.env file should only live on the server.
And now we can add the
cloudflared service to our
docker-compose like this:
version: "3" services: web: image: nginx:latest volumes: - ./index.html:/usr/share/nginx/html/index.html restart: always container_name: web tunnel: image: cloudflare/cloudflared:latest command: tunnel --no-autoupdate run env_file: tunnel.env restart: always container_name: tunnel depends_on: - web
Now you can run the stack with
docker compose up -d.
Cloudflare. Tunnels. Are. Easy! And they are free. What I like about this setup:
- A tunnel is a secure way of communication between Cloudflare and your application.
- You don't need to setup port-forwarding for this to work. No more adding your IP to DNS.
- You don't need a reverse proxy for this to work.
- You don't need Let's Encrypt certificates for this to work.
- You don't even need to expose any ports to your host (which I super love 😍).
- You can create a specific tunnel per application by just adding the
cloudflaredservice to your Docker Compose stack.
- 2023-05-14 Added container names for more predictable naming of the stack (without the numbering). Removed quotation from the YAML files.