5 min read

Deploying Containers via Cloudflare Tunnel (Zero Trust)

Deploying Containers via Cloudflare Tunnel (Zero Trust)
Source
1 - How I Built My Own Server Using Local and Cloud Resources
2 - Deploying containers via Cloudflare Tunnel (Zero Trust)
3 - Reverse proxy on Oracle VPS with Traefik
4 - Encrypted Server Backups to S3 with Duplicati

To securely expose the services running on my local server to the internet without opening any ports, I use Cloudflare Tunnel through Cloudflare Zero Trust. This lets my set-top box (STB) act like a public-facing server, even though it’s behind NAT and ISP restrictions.

Cloudflare Tunnel creates an encrypted connection between my device and Cloudflare’s edge network. I don’t need a static IP, DDNS, or port forwarding — just a tunnel and a free Cloudflare account.


Requirements

Before you proceed with the setup, make sure you have the following:

  • Docker is installed on your local server and now how to use it. (Tutorial here)
  • A Cloudflare account (the free tier is enough)
  • A registered domain
    • The domain doesn't have to be purchased from Cloudflare — you can buy it from any registrar (e.g., Namecheap, GoDaddy, etc.)
    • However, you must point the domain’s nameservers to Cloudflare to use the DNS and Tunnel features
📌 Note: I won’t go into details about registering a domain or setting up nameservers. These steps are already well-covered online — just search for a guide that fits your registrar.

For domains not registered through Cloudflare, this guide might be helpful.

Setup
If you want to use Cloudflare as your primary DNS provider and manage your DNS records on Cloudflare, your domain should be using a full setup.

Create a Tunnel

Step 1. Go to the Cloudflare Dashboard. It should look like this.

Step 2. Navigate to Zero Trust

Step 3. Go to Networks -> Tunnels -> Create a Tunnel

Step 4. Select Cloudflared, give the tunnel a name (it can be anything), then save it.

Step 5. Once the tunnel is created, click on Docker and copy the provided Docker command. Save it somewhere for later use.


Set Up Portainer for Easy Docker Management
At first, I prefer to run Portainer to make the later setup easier.

Step 1. Access your local server via SSH, and navigate to your preferred location for installing Portainer.

Step 2. Create a folder called portainer, then go inside the folder:

mkdir portainer && cd portainer

Step 3. Create Docker networks that will be used by all containers:

# to communicate with cloudflare
docker network create tunnel-network

# to communicate internally
docker network create local-network

Step 4. Run nano docker-compose.yml, and fill it with the following:

services:
  portainer:
    image: portainer/portainer-ce:latest
    container_name: portainer
    ports:
      - 8000:9000
    volumes:
      - /var/run/docker.sock:/var/run/docker.sock
      - <YOUR_PATH>/portainer_data/data:/data
    restart: unless-stopped
    cap_drop:
      - ALL
    security_opt:
      - "no-new-privileges=true"
    networks:
      - net
networks:
  net:
    external: true
    name: local-network
Replace <YOUR_PATH> with your preferred location on the server where you want to store Portainer's data.

Step 5. Save it and run:

docker compose up -d

Now your Portainer instance is accessible at http://<your-server-ip>:8000, create an Admin account when prompted.


Connect Cloudflare Tunnel to Your Server

Step 1. In Portainer, go to your local environmentStacksAdd Stack

Step 2. Enter a name for your stack

Step 3. In the web editor field, paste the following:

services:
  cloudflared:
      command: 'tunnel --no-autoupdate run --token <YOUR_TOKEN>'
      image: 'cloudflare/cloudflared:latest'
      container_name: zero-trust
      restart: unless-stopped
      extra_hosts: ["host.docker.internal:host-gateway"]
      networks:
        - net

networks:
  net:
    name: tunnel-network
    external: true
Replace <YOUR_TOKEN> with the token you copied from Cloudflare Zero Trust after creating your tunnel.

Step 4. Click Deploy the stack.

Now your local server is successfully connected to the Cloudflare Tunnel. You can verify this by going to Cloudflare Zero Trust — the Status column should show HEALTHY.


Publish Your First App to the Internet

Once everything is set up, you can now deploy an app and register it with your tunnel using a subdomain.

Step 1. Deploy the app, use Portainer to deploy the app. In this example, we'll use Nginx:

services:
  app:
    image: nginx:latest
    container_name: nginx
    networks:
      - net

networks:
  net:
    name: tunnel-network
    external: true
When deploying an app, make sure you know the default port it uses. In the case of Nginx, the default is port 80. Always check the official documentation for the Docker image you're using.

Step 2. Register a Subdomain. Go to Cloudflare Zero Trust, click on the three dots of your tunnel, then select Configure.

Step 3. Navigate to the Public Hostname tab, and click Add a public hostname.

Step 4. Fill in the fields:

  • In this example, we'll publish Nginx at nginx.carens.dev.
  • The URL field should be in the format <container_name>:<container_port>.
    Since the container is named nginx and uses the default port 80, you can simply enter nginx.

Step 5. Save the hostname settings. Then, try accessing your app via: https://nginx.carens.dev

If everything is successful, you should see the default Nginx welcome page.

Now you're ready to deploy and expose any containerized app through your Cloudflare Tunnel!


Final Thoughts

With this setup, you've taken your first step toward building a modern, secure, and self-hosted server environment. By combining Docker, Portainer, and Cloudflare Tunnel (Zero Trust), you've created a flexible system where your services can be deployed, managed, and securely accessed from anywhere — without exposing your server directly to the internet.

This approach not only simplifies container management but also enhances security and scalability. Whether you're hosting personal projects or small-scale production services, this foundation will serve you well as you expand your infrastructure.

Stay tuned for the next part — where we’ll cover how to configure an Oracle Cloud VPS, and use Traefik for advanced reverse proxying.

Thanks for following along — and happy self-hosting! 🚀