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 TrustAccessTunnels
  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 TrustAccessTunnels
  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 TrustAccessApplications
  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!