0 / 0
Skip to content

Setting Up Cloudflare Tunnel with Docker Compose on macOS ​

Posted on August 29, 2025

Cloudflare Tunnel provides a secure way to expose your local services to the internet without opening ports on your firewall. When combined with Docker Compose on macOS, you can create a robust, containerized setup that's perfect for development, testing, or production deployments. In this guide, I'll show you how to set up and configure Cloudflare Tunnel using Docker Compose.

What is Cloudflare Tunnel? ​

Cloudflare Tunnel (formerly Argo Tunnel) creates an outbound-only connection to Cloudflare's edge network. This means:

  • No open ports: Your local services remain secure behind your firewall
  • Zero-trust security: Traffic is encrypted end-to-end
  • Global CDN: Your services benefit from Cloudflare's global network
  • DDoS protection: Built-in protection against attacks

Basic Docker Compose Setup ​

Here's the basic docker-compose.yml configuration for Cloudflare Tunnel:

yaml
version: '3.8'

networks:
  default:
    name: cloudflared
    driver: bridge

services:
  cloudflared:
    image: cloudflare/cloudflared:latest
    command: tunnel --no-autoupdate run --token YOUR_TUNNEL_TOKEN
    restart: unless-stopped 
    extra_hosts:
      - "host.docker.internal:host-gateway"

Configuration Breakdown ​

Network Configuration ​

yaml
networks:
  default:
    name: cloudflared
    driver: bridge
  • driver: bridge: Creates a local bridge network
  • name: cloudflared: Gives the default network a specific name for external access
  • Why this approach: Creates the network automatically while allowing other Docker Compose files to connect to it

Service Configuration ​

yaml
services:
  cloudflared:
    image: cloudflare/cloudflare/cloudflared:latest
    command: tunnel --no-autoupdate run --token YOUR_TUNNEL_TOKEN
    restart: unless-stopped 
    extra_hosts:
      - "host.docker.internal:host-gateway"
  • image: cloudflare/cloudflared:latest: Uses the official Cloudflare Tunnel Docker image
  • --no-autoupdate: Prevents automatic updates (useful for containerized environments)
  • --token: Your Cloudflare Tunnel token for authentication
  • restart: unless-stopped: Automatically restarts the container unless manually stopped
  • extra_hosts: Maps host.docker.internal to the host machine (macOS-specific)

Setting Up Your Cloudflare Tunnel ​

Step 1: Create a Tunnel in Cloudflare Dashboard ​

  1. Log into your Cloudflare dashboard
  2. Navigate to Zero Trust → Access → Tunnels
  3. Click Create a tunnel
  4. Choose Cloudflared as the connector type
  5. Give your tunnel a name (e.g., "macos-docker-tunnel")
  6. Copy the tunnel token (you'll need this for the Docker Compose file)

Step 2: Create Your Docker Compose File ​

yaml
# docker-compose.yml
version: '3.8'

networks:
  default:
    name: cloudflared
    driver: bridge

services:
  cloudflared:
    image: cloudflare/cloudflared:latest
    command: tunnel --no-autoupdate run --token YOUR_ACTUAL_TOKEN_HERE
    restart: unless-stopped 
    extra_hosts:
      - "host.docker.internal:host-gateway"
    environment:
      - TZ=Europe/Amsterdam
    volumes:
      - ./cloudflared:/etc/cloudflared

Note: This configuration automatically creates a network named cloudflared that other Docker Compose files can connect to using external: true.

Step 3: Start the Tunnel ​

bash
# Start the Cloudflare Tunnel
docker-compose up -d

# Check the logs
docker-compose logs -f cloudflared

Advanced Configuration Examples ​

Example 1: ​

yaml
# docker-compose.yml
# MUST create network, if not exist: docker network create cloudflared
networks:
  default:
    external: true
    name: cloudflared

services:
  whoami:
    image: "traefik/whoami"
    volumes:
      - ./data/ssl:/ssl

Uses default network for all services as cloudflared

  • or -
yaml
# docker-compose.yml
# MUST create network, if not exist: docker network create cloudflared
networks:
  cloudflared:
    external: true

services:
  whoami:
    image: "traefik/whoami"
    networks:
      - cloudflared
    volumes:
      - ./data/ssl:/ssl

Using cloudflared existing network

In Cloudflared Public hostnames:

  • Public hostname: whoami.example.com
  • Path: *
  • Service: http://whoami:80
  • Origin configurations: 0

Example 2: Local Development Services ​

When you have local development servers running on your Mac:

bash
# Vite development server
pnpm dev  # Runs on http://localhost:5173

# Nuxt development server  
npm run dev  # Runs on http://localhost:3000

In Cloudflare Dashboard → Public Hostnames:

For Vite:

  • Public hostname: vite.example.com
  • Path: *
  • Service: http://host.docker.internal:5173
  • Origin configurations: 0

For Nuxt:

  • Public hostname: nuxt.example.com
  • Path: *
  • Service: http://host.docker.internal:3000
  • Origin configurations: 0

Example 3: Multiple Docker Services ​

yaml
# docker-compose.yml
networks:
  default:
    external: true
    name: cloudflared

services:
  webapp:
    image: nginx:alpine
    ports:
      - "8080:80"
    volumes:
      - ./webapp:/usr/share/nginx/html

  api:
    image: node:18-alpine
    ports:
      - "3000:3000"
    volumes:
      - ./api:/app
    working_dir: /app
    command: npm start

In Cloudflare Dashboard → Public Hostnames:

For Web App:

  • Public hostname: webapp.example.com
  • Path: *
  • Service: http://webapp:80
  • Origin configurations: 0

For API:

  • Public hostname: api.example.com
  • Path: *
  • Service: http://api:3000
  • Origin configurations: 0

Configuring Routes in Cloudflare Dashboard ​

After your tunnel is running, configure routes in the Cloudflare dashboard:

Step 1: Access Tunnel Configuration ​

  1. Go to Zero Trust → Access → Tunnels
  2. Click on your tunnel name
  3. Go to the Public Hostnames tab

Step 2: Add Public Hostnames ​

For each service you want to expose:

Subdomain: your-subdomain
Domain: yourdomain.com
Service: http://service-name:port (for Docker services)
Service: http://host.docker.internal:port (for local services)

Step 3: Configure Settings ​

  • Type: HTTP
  • Path: * (for all paths)
  • Origin configurations: 0 (no additional settings)

Troubleshooting ​

Check if Tunnel is Running ​

bash
# Check container status
docker-compose ps

# View logs
docker-compose logs cloudflared

# Check network
docker network ls | grep cloudflared

Test Connectivity ​

bash
# Test from within container
docker-compose exec cloudflared ping host.docker.internal

# Test local service
docker-compose exec cloudflared curl http://host.docker.internal:5173

Common Issues ​

Issue: "Network not found"

bash
# Create the network manually
docker network create cloudflared

Issue: "Service not accessible"

bash
# Check if service is running on host
curl http://localhost:5173

# Verify network connectivity
docker network inspect cloudflared

Security Tips ​

Use Environment Variables ​

yaml
# docker-compose.yml
services:
  cloudflared:
    image: cloudflare/cloudflared:latest
    command: tunnel --no-autoupdate run --token ${CLOUDFLARE_TOKEN}
    environment:
      - CLOUDFLARE_TOKEN=${CLOUDFLARE_TOKEN}
bash
# .env file
CLOUDFLARE_TOKEN=your_actual_token_here

Access Policies (Optional) ​

In Cloudflare Dashboard:

  1. Go to Zero Trust → Access → Applications
  2. Create policies to restrict access to your services
  3. Add authentication if needed

Quick Start Checklist ​

  1. ✅ Create tunnel in Cloudflare dashboard
  2. ✅ Copy tunnel token
  3. ✅ Create docker-compose.yml with your token
  4. ✅ Start tunnel: docker-compose up -d
  5. ✅ Add public hostnames in Cloudflare dashboard
  6. ✅ Test your services

Conclusion ​

Cloudflare Tunnel with Docker Compose on macOS provides a simple, secure way to expose your local services. The key benefits:

  • Simple setup: Just a few lines of YAML
  • Secure: No open ports, encrypted traffic
  • Flexible: Works with local services and Docker containers
  • Reliable: Automatic restarts and health monitoring

This approach is perfect for development, testing, and sharing your work securely with others!