Need Help with Traefik and Docker Compose Setup - Can't Access Containers Externally

Hello everyone, I'll try to simplify this, I'm new to traefik. I'm running Ubuntu 22.04.3 LTS with some containers, and I'm having trouble accessing certain containers externally. I've followed the documentation and watched online videos, but I'm facing issues, even with a basic Docker Compose setup as documented here.

While trying to expose some of my other stuff I had trouble with following the guides - I just went back to the basics and trying to see where the problem really starts with this basic compose.

Here is my docker-compose file -

version: "3.3"

services:

  traefik:
    image: "traefik:v2.10"
    container_name: "traefik"
    command:
      #- "--log.level=DEBUG"
      - "--api.insecure=true"
      - "--providers.docker=true"
      - "--providers.docker.exposedbydefault=false"
      - "--entrypoints.web.address=:80"
    ports:
      - "80:80"
      - "8080:8080"
    volumes:
      - "/var/run/docker.sock:/var/run/docker.sock:ro"

  whoami:
    image: "traefik/whoami"
    container_name: "simple-service"
    labels:
      - "traefik.enable=true"
      - "traefik.http.routers.whoami.rule=Host(`whoami.mydomain.com`)"
      - "traefik.http.routers.whoami.entrypoints=web"

The containers start without issues, but I can't access the link. I'm using Cloudflare and have it pointing to my IP address with a subdomain of whoami.mydomain.com. Do I need to set up specific records in Cloudflare, and should I also port forward port 80 in my router?

In general your setup looks pretty much like the simple Traefik example.

Of course Traefik can only work when traffic is received. When you put Cloudflare in front, then you need to set that up correctly. And when you want to reach Traefik at home behind your home DSL/fiber gateway, then you need to enable port forwarding.

Enable and check Traefik debug log and access log for what is happening internally.

This should mostly work, ive commented out the oauth, but when you get it working unauthenticated, add that in after, and configure it with your preferred email auth (i use google)

In cloudflare:

  1. get your API keys, and put whatever combo you need in the .env file
  2. create your *.domain.com address and add in your public IP address
  3. create some non-routable subdomains (i use *.disk.domain, and *.desktop.domain in this setup) and give them non-routable internal Ip addresses (ie your LAN ip addresses)

On your router

  1. ensure port 80 and 443 are NAT to your server hosting traefik
  2. Possibly setup auto refresh of public IP address in cloudflare

there is some logic here to allow you to access docker services internally with or without oauth with the middleware that is commented out, and the oauth service

.env <-- put this next to your docker-compose files)

DOMAINNAME=yourdomain.com

# Whatever combo you need here, put in the compose for acme
CF_DNS_API_TOKEN=Cloudflaretokens
CF_API_EMAIL=Cloudflaretokens
CF_API_KEY=Cloudflaretokens

# I use google, and it needs these for the middleware
GOOGLE_CLIENT_ID=someid.apps.googleusercontent.com
GOOGLE_CLIENT_SECRET=R-somesecret
GOOGLE_APP_ID=1234567890
OAUTH_SECRET=anythingyouwant
MY_EMAIL=papina@domain.com,anothergmail@gmail.com

docker-compose.yml

version: "3.7"
services:
  # This is a traefik setup for internet access
  traefik:
    image: traefik:latest
    container_name: "traefik"
    restart: unless-stopped
    volumes:
      # Traefik requires access to docker.sock to read docker labels
      - /var/run/docker.sock:/var/run/docker.sock:ro # Access to Docker
      - ./traefik/acme.json:/acme.json # SSL certificates
      - ./traefik/rules:/rules # Traefik configuration
      - ./traefik/logs:/logs # keep logs
    environment:
      # Required for Cloudflare, replace with whatever DNS providers API key are required
      # https://go-acme.github.io/lego/dns/cloudflare/
      - CF_API_EMAIL=${CF_API_EMAIL}
      - CF_DNS_API_TOKEN=${CF_DNS_API_TOKEN}
      - CF_API_KEY =${CF_API_KEY}
      # For file provider rules, we need a variable we can use
      - DOMAINNAME=${DOMAINNAME}
    ports:
      - 80:80 
      - 443:443
    command:
      # Traefik settings to get a Dashboard, and log settings
      - "--api.dashboard=true"
      - "--api=true"
      - "--api.insecure=true"
      - "--log.filePath=/logs/traefik.json"
      - "--log.format=json"
      - "--log.level=INFO"
      - "--serversTransport.insecureSkipVerify=true"
      # EntryPoints ports and redirecton to SSL
      - "--entryPoints.web.address=:80"
      - "--entryPoints.websecure.address=:443"
      - "--entryPoints.web.http.redirections.entryPoint.to=websecure"
      - "--entryPoints.web.http.redirections.entryPoint.scheme=https"
      # Use the Cloudflare ENV variables to Authenticate to Cloudflare and update DNS
      - "--certificatesResolvers.myresolver.acme.storage=/acme.json"
      - "--certificatesResolvers.myresolver.acme.email=your.email@gmail.com"
      - "--certificatesResolvers.myresolver.acme.dnsChallenge=true"
      - "--certificatesResolvers.myresolver.acme.dnschallenge.provider=cloudflare" # Using Cloudflare here
      # Attach SSL Certs to domains
      - "--entryPoints.websecure.http.tls.certresolver=myresolver"
      - "--entrypoints.websecure.http.tls.domains[0].main=${DOMAINNAME}"
      - "--entrypoints.websecure.http.tls.domains[0].sans=*.${DOMAINNAME}"
      - "--entrypoints.websecure.http.tls.domains[1].main=*.disk.${DOMAINNAME}"
      - "--entrypoints.websecure.http.tls.domains[2].main=*.desktop.${DOMAINNAME}"
      # Setup the docker provider, and basic rules to grab the docker service name as the host name
      - "--providers.docker=true"
      - "--providers.docker.endpoint=unix:///var/run/docker.sock"
      - "--providers.docker.exposedByDefault=true"
      - "--providers.docker.defaultRule=Host(`{{ index .Labels \"com.docker.compose.service\" }}.${DOMAINNAME}`) || Host(`{{ index .Labels \"com.docker.compose.service\" }}.disk.${DOMAINNAME}`)"
      # Setup the file provider so we can add dynamic rules as YAML files, eg OAUTH middleware chains
      - "--providers.file.directory=/rules"
      - "--providers.file.watch=true"
      # Now set some defaults entrypoint settings so we don't have to set them on any docker services
      # - "--entryPoints.websecure.http.middlewares=chain-oauth-auth@file"
    labels:
      - "traefik.http.routers.traefik-rtr.service=api@internal"

The static config (ie the traefik command line) you can put most of the docker logic negating the need to put anything in the docker labels, except for where they expose more than one port, then you just add the loadbalancer label, and that's generally all you need

    labels:
      # Gitea Image exposes multiple ports, but I only want this one
      - "traefik.http.services.gitea-svc.loadbalancer.server.port=3000"

whoami only exposes 1 port, it needs no labels with the correct traefik static config

  # whoami.domain.com
  whoami:
    image: "traefik/whoami"
    container_name: "whoami"

middleware-oauth.yml <-- put this in your /rules folder, its dynamic

http:
  middlewares:
    middlewares-oauth:
      forwardAuth:
        address: "http://oauth:4181"
        trustForwardHeader: true
        authResponseHeaders: 
          - "X-Forwarded-User"

Oauth example

  ## OAuth - Forward Authentication
  oauth:
    image: thomseddon/traefik-forward-auth
    container_name: "oauth"
    restart: unless-stopped
    networks:
      - t2_proxy
    command:
      # Limit Admin page to oauth
      - --rule.restrictpage.action=auth
      - --rule.restrictpage.rule=HostRegexp(`{adminpages:^(vault|gitlab|gitea)}.${DOMAINNAME}`) && PathPrefix(`/admin`)
      # Allow access to the API of sonarr/radarr for NZB360, but still require oauth for the web interface
      - --rule.sonarrapi.action=allow
      - --rule.sonarrapi.rule=HostRegexp(`{nzbapi:^(sonarr|radarr)}.${DOMAINNAME}`) && PathPrefix(`/api`)
      # Allow subdomain access to internal, non-routable IP addresses
      - --rule.bypasslocal.action=allow
      - --rule.bypasslocal.rule=HostRegexp(`{subdomain:[a-z0-9]+}.{subdomain:(disk|desktop)}.${DOMAINNAME}`)
      # Allow public access to vaultwarden,gitlab,gitea
      - --rule.bypassdomain.action=allow
      - --rule.bypassdomain.rule=HostRegexp(`{subdomain:^(hvault|vault|gitlab|gitea)}.${DOMAINNAME}`)
    environment:
      # Setup Google OAuth API Credentials for these values
      - PROVIDERS_GOOGLE_CLIENT_ID=${GOOGLE_CLIENT_ID}
      - PROVIDERS_GOOGLE_CLIENT_SECRET=${GOOGLE_CLIENT_SECRET}
      # API Credentials Authorised JavaScript origins values
      - AUTH_HOST=oauth.${DOMAINNAME}
      # API Credentials Authorised redirect URI value is appended to https://${DOMAINNAME}
      - URL_PATH=/_oauth
      # Whitelist this domain or email addresses
      - MATCH_WHITELIST_OR_DOMAIN=true
      - DOMAIN=${DOMAINNAME}
      - WHITELIST=${MY_EMAIL}
      # General OAuth Container settings
      - COOKIE_DOMAIN=${DOMAINNAME}
      - SECRET=${OAUTH_SECRET}
      - INSECURE_COOKIE="true"
      - LOG_FORMAT=pretty
      - LOG_LEVEL=error
    labels:
      - "traefik.http.services.oauth-svc.loadbalancer.server.port=4181"