Skip to content
Go back

Standard Deployment Architecture with Cloudflare Tunnel and Traefik

This standard deployment architecture uses Cloudflare Zero Trust Tunnel as the secure public entry point, Traefik as the global reverse proxy, and Docker Compose for app stacks. It keeps server inbound ports closed, enables dynamic service discovery, and supports monorepo or separate repo setups.

Request Flow

When a user visits an app like gatepass.arzlabserver.my.id, Cloudflare receives the request and forwards it via the tunnel to the cloudflared connector on your server. The connector routes it to traefik:80 on the shared Docker network, where Traefik uses Docker labels and host/path rules to direct it to the right container.

This flow ensures no direct server exposure.

Environment Model

Deploy one shared proxy stack per environment—separate servers for staging and production, each with its own Cloudflare tunnel, Traefik container, and proxy_network. This isolation makes deployments predictable and secure.

Naming Conventions:

Global Setup Steps

Start with these core components on every server.

  1. Create the shared network:

    docker network create proxy_network
  2. Run Cloudflare Tunnel:

    docker run -d \
      --name cloudflared \
      --network proxy_network \
      --restart unless-stopped \
      cloudflare/cloudflared:latest \
      tunnel --no-autoupdate run --token <YOUR_TUNNEL_TOKEN>
    ``` [hub.docker](https://hub.docker.com/r/zenkiet/traefik-tunnel-expose)
    
  3. Deploy Traefik via ~/traefik/docker-compose.yml:

    name: global-proxy
    services:
      traefik:
        image: traefik:latest
        container_name: traefik
        restart: unless-stopped
        command:
          - "--api.insecure=false"
          - "--providers.docker=true"
          - "--providers.docker.exposedbydefault=false"
          - "--entrypoints.web.address=:80"
        volumes:
          - /var/run/docker.sock:/var/run/docker.sock:ro
        networks:
          - proxy_network
    networks:
      proxy_network:
        external: true

    Run: cd ~/traefik && docker compose up -d

  4. In Cloudflare Zero Trust, set public hostnames to http://traefik:80 (not server IP).

App Deployment Patterns

Pattern A: Single Container App

Ideal for tools like Metabase—Traefik routes directly.

Example docker-compose.yml:

name: metabase
services:
  metabase:
    image: metabase/metabase:latest
    container_name: metabase
    restart: unless-stopped
    labels:
      - "traefik.enable=true"
      - "traefik.docker.network=proxy_network"
      - "traefik.http.routers.metabase.rule=Host(`test-meta.arzlabserver.my.id`)"
      - "traefik.http.routers.metabase.entrypoints=web"
      - "traefik.http.services.metabase.loadbalancer.server.port=3000"
    volumes:
      - metabase_data:/metabase-data
    networks:
      - proxy_network
volumes: metabase_data:
networks:
  proxy_network:
    external: true

Key: Match loadbalancer.server.port to app’s internal port; no host port publishing.

Pattern B: Monorepo with Internal Nginx

For single-repo frontend/backend, use app Nginx for /api vs / routing; Traefik handles hostname.

Nginx nginx.conf proxies /api/ to backend:5000 and / to frontend:3000.

Traefik labels on Nginx container point to its port 80.

Separate Repositories

Frontend: Host rule for main domain. Backend: Host + PathPrefix(/api); add StripPrefix middleware if backend expects clean paths.

Example backend labels:

- "traefik.http.routers.myapp-back.rule=Host(`app.arzlabserver.my.id`) && PathPrefix(`/api`)"
- "traefik.http.routers.myapp-back.middlewares=myapp-strip-api"
- "traefik.http.middlewares.myapp-strip-api.stripprefix.prefixes=/api"

Both join proxy_network.

Deployment Checklist and Standards

Troubleshooting Tips

Hostname resolves but app fails? Check Cloudflare points to traefik:80. Bad gateway? Verify internal port and network label.

Use monorepo+Nginx for self-contained apps; separate repos for independent cycles under one domain.

Ready to deploy? Follow the checklist for your first zero-trust stack.


Share this post on:

Next Post
Create Custom Shortcuts in Windows Terminal