How to use Traefik for exposing Self Hosted Applications from your homelab

How to use Traefik for exposing Self Hosted Applications from your homelab
Photo by Taylor Vick / Unsplash

I will probably forget it so i will make a post so i can find it when i will forget it.

What you will need

  1. A VPS
  2. A VM/CT in your homelab
  3. Time

Login To Your VPS

  1. Use root@198.51.100.52 to login into your server.
  2. Make a folder with
mkdir -p compose/traefik
  1. Make a file named docker-compose.yml
  2. Paste in the file the following contents:
services:
  traefik:
    image: traefik:v3.5.2
    restart: always
    command:
      - --configFile=/etc/traefik/traefik.yml
      - --providers.file.directory=/etc/traefik/dynamic
      - --log.level=INFO
    ports:
      - "80:80"
      - "443:443"
    volumes:
      - /var/run/docker.sock:/var/run/docker.sock
      - ./acme.json:/acme.json
      - ./conf/traefik.yml:/etc/traefik/traefik.yml:ro
      - ./conf/dynamic:/etc/traefik/dynamic:ro # Mount the entire dynamic directory
    container_name: traefik
    networks:
      - traefik
    labels:
      - "traefik.enable=true"
      - "traefik.http.routers.api.rule=Host(`traefik.voorbeeld.nl`)"
      - "traefik.http.routers.api.service=api@internal"
      - "traefik.http.routers.api.entrypoints=websecure"
      - "traefik.http.routers.api.tls=true"
      - "traefik.http.routers.api.tls.certresolver=myresolver"

networks:
  traefik:
    external: true

Change joudomein.nl to your own domain.

  1. Make a conf folder with
mkdir -p conf
  1. Make in that folder two folders named rules and dynamic.
mkdir rules
mkdir dynamic
  1. Create a traefik.yml file with the following contents. this file should in the rules folder.
# Entry points are the ports that Traefik listens on
entryPoints:
  web:
    address: ":80" # HTTP port
  websecure:
    address: ":443" # HTTPS port

# certificatesResolvers are used to generate SSL certificates
certificatesResolvers:
  myresolver:
    acme:
      email: jij@voorbeeld.nl
      storage: acme.json
      httpChallenge:
        entryPoint: web

# This enables the webinterface and API for Traefik
api:
  dashboard: true
  insecure: true

# Providers are used to configure the dynamic configuration of Traefik
providers:
  docker:
    endpoint: "unix:///var/run/docker.sock"
    exposedByDefault: false
  file:
    directory: /etc/traefik/dynamic # Directory for dynamic config files
    watch: true

# Log configuration
log:
  level: INFO

accessLog: {}
  1. In the dynamic folder create a file named tcp-routers.yml
tcp:
  routers:
    hl-traefik-rtr:
      entryPoints:
        - "websecure"
      rule: "HostSNIRegexp(`hl.voorbeeld.nl`) || HostSNIRegexp(`{subdomain:[a-z0-9]+}.hl.voorbeeld.nl`)"
      service: hl-traefik-svc
      tls:
        passthrough: true

    test-traefik-rtr:
      entryPoints:
        - "websecure"
      rule: "HostSNIRegexp(`test.voorbeeld.nl`) || HostSNIRegexp(`{subdomain:[a-z0-9]+}.test.voorbeeld.nl`)"
      service: test-traefik-svc
      tls:
        passthrough: true

  services:
    hl-traefik-svc:
      loadBalancer:
        servers:
          - address: "192.0.2.2:443"

    test-traefik-svc:
      loadBalancer:
        servers:
          - address: "192.0.2.5:443"
  1. Create a file named acme.json
touch acme.json
  1. Change the permissions to 600
chmod 600
  1. Run the docker compose stack with
docker compose up -d

You are now done with the VPS side.

  1. Make a CT or VM on promxox.
  2. login with:
ssh user@192.0.2.5
  1. Install Docker

A.

apt install curl
dnf install curl
yum install curl

B.

curl -fsSL https://get.docker.com | sh
  1. Make a folder named traefik
  2. Create a file named docker-compose.yml and paste the following contents in the file.
version: '3.8'

services:
  traefik:
    image: traefik:latest
    container_name: traefik
    restart: unless-stopped
    security_opt:
      - no-new-privileges:true
    ports:
      - "80:80"
      - "443:443"
      - "8080:8080"  # Dashboard (optional, secure it in production)
    volumes:
      # Docker socket to discover containers
      - /var/run/docker.sock:/var/run/docker.sock:ro
      # Traefik configuration
      - ./traefik.yml:/etc/traefik/traefik.yml:ro
      # Dynamic configuration (optional)
      - ./dynamic:/etc/traefik/dynamic:ro
      # ACME certificates storage
      - ./acme.json:/acme.json
      # Access logs (optional)
      - ./logs:/logs
    environment:
      # Cloudflare credentials - preferred method
      - CF_DNS_API_TOKEN=apikey

      # Alternative: API Key + Email (if you don't use tokens)
      # - CF_API_EMAIL=${CF_API_EMAIL}
      # - CF_API_KEY=${CF_API_KEY}

      # Timezone
      - TZ=UTC
    networks:
      - traefik-public

    # Enable Traefik dashboard (optional - secure with basic auth)
    labels:
      - "traefik.enable=true"
      # HTTP to HTTPS redirect
      - "traefik.http.routers.http-catchall.rule=HostRegexp(`{host:.+}`)"
      - "traefik.http.routers.http-catchall.entrypoints=web"
      - "traefik.http.routers.http-catchall.middlewares=redirect-https"
      - "traefik.http.middlewares.redirect-https.redirectscheme.scheme=https"
      - "traefik.http.middlewares.redirect-https.redirectscheme.permanent=true"

      # Dashboard router
      - "traefik.http.routers.dashboard.rule=Host(`traefik.joudomein.nl`)"
      - "traefik.http.routers.dashboard.entrypoints=websecure"
      - "traefik.http.routers.dashboard.tls.certresolver=cloudflare"
      - "traefik.http.routers.dashboard.service=api@internal"

      ## Basic auth middleware for dashboard (optional but recommended)
      #- "traefik.http.routers.dashboard.middlewares=dashboard-auth"
      #- "traefik.http.middlewares.dashboard-auth.basicauth.users=admin:$$2y$$05$$examplehash"  # Generate with: htpasswd -nb admin yourpassword

networks:
  traefik-public:
    external: true
  1. Create a file named traefik.yml and paste the following contents.
# Global configuration
global:
  checkNewVersion: false
  sendAnonymousUsage: false

# Entry points
entryPoints:
  web:
    address: ":80"
    # Automatic HTTP to HTTPS redirect
    http:
      redirections:
        entryPoint:
          to: websecure
          scheme: https
          permanent: true

  websecure:
    address: ":443"
    http:
      tls:
        certResolver: cloudflare  # Default cert resolver
        domains:
          - main: "bbb.matinise.com"  # Replace with your domain
            sans:
              - "*.bbb.matinise.com"

  traefik:
    address: ":8080"

# API and Dashboard
api:
  dashboard: true
  debug: false

# Logging
log:
  level: INFO  # Set to DEBUG for troubleshooting
  filePath: "/logs/traefik.log"
  format: json

# Access logs
accessLog:
  filePath: "/logs/access.log"
  bufferingSize: 100
  filters:
    statusCodes:
      - "200-299"
      - "300-399"
      - "400-499"
      - "500-599"

# Providers
providers:
  docker:
    endpoint: "unix:///var/run/docker.sock"
    exposedByDefault: false
    watch: true
    network: "traefik-public"



  # File provider for dynamic configuration (optional)
  file:
    directory: "/etc/traefik/dynamic"
    watch: true

# ACME Configuration
certificatesResolvers:
  cloudflare:
    acme:
      email: "jij@voorbeeld.nl"  # Replace with your email
      storage: "acme.json"
      # Use production server for real certificates
      # caServer: "https://acme-v02.api.letsencrypt.org/directory"

      # Use staging server for testing to avoid rate limits
      # caServer: "https://acme-staging-v02.api.letsencrypt.org/directory"

      # DNS Challenge configuration
      dnsChallenge:
        provider: "cloudflare"

        # DNS resolvers for propagation checking
        resolvers:
          - "1.1.1.1:53"
          - "1.0.0.1:53"
          - "8.8.8.8:53"
          - "8.8.4.4:53"

        # Optional: Delay before checking propagation (in seconds)
        delayBeforeCheck: 30

        # Optional: Disable Traefik's DNS propagation check
        # (Use if you have network issues, but be careful)
        # disablePropagationCheck: true

# Servers transport configuration
serversTransport:
  insecureSkipVerify: false

# TLS options
tls:
  options:
    default:
      minVersion: VersionTLS12
      cipherSuites:
        - TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256
        - TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384
        - TLS_ECDHE_RSA_WITH_CHACHA20_POLY20_SHA256
      curvePreferences:
        - CurveP521
        - CurveP384
  1. Change the examples to your own values.
  2. After you are done do
docker compose up -d

You are now done with the setup.