Skip to content

Traefik#

The Traefik reverse proxy is a powerful tool for managing web traffic to your Docker containers. It can automatically generate SSL certificates, route traffic to the correct container, and provide a secure way to access your services from anywhere in the world.

Warning

You must set up your router, custom domain, Google OAuth, CloudFlare, DuckDNS, and Traefik before you can start any other services. Setting up Traefik with everything requires a bit of time. Please follow the instructions in this section carefully to get started.

Once you have each of the pre-requisites set up, you can use the core-up command to start just the Traefik services and confirm that everything is working at https://traefik.yourdomain.com. Initial DNS propagation and certificate generation can take some time, so be patient on the first run.

See the Command Line documentation for more information on how to operate the application.

Acknowledgements

This configuration was inspired by, and immensely helped by the article at smarthomebeginner.com. Their massive home server setup on GitHub and amazing blog series inspired much of this project.

Prerequisites#

Personal Domain#

This guide assumes you have a personal domain name that you can use to access your services. You can purchase a domain name from a registrar like Cloudflare.

DuckDNS#

A free DuckDNS dynamic DNS subdomain can be set up here. DuckDNS will provide you with a token that you will use in the .env file. Behind the scenes, the duckdns service will update your IP address with DuckDNS every 5 minutes which makes it possible to reach your server from anywhere. You will provide CloudFlare with the DuckDNS subdomain to point to your server.

Port Forwarding#

In order to reach the outside world, you must forward ports 80 and 443 from your server IP address through your router. See your router's manual for Instructions. See this blog post for more information on port forwarding

Note

If you're comfortable with SSH configuration I also port forward the SSH service - this alongside your DuckDNS setup will allow you to access your server from anywhere via your dynamic DNS URL instead of your IP address.

CloudFlare#

This guide leverages CloudFlare for free DNS services. SmartHomeBeginner has a great guide on setting up CloudFlare here.

Once you get CloudFlare and DuckDNS set up, you will need to add some basic DNS configuration. CloudFlare lets you use a dynamic DNS address as a CNAME record which makes it easy to point your domain to your server.

Type Name Content TTL
CNAME example.com example.duckdns.org Auto
CNAME www example.com Auto
CNAME * example.com Auto

Google OAuth 2.0#

A helpful blog post on the Google Oauth 2.0 configuration can be found here. Essentially you must create a project in the Google Developer Console to enable the Google OAuth 2.0 service. You will share credentials with the oauth service in the .env file and manually whitelist users per email address.

File Configuration#

All services are configured via a .env file at the root of the project and a few secret files in the secrets directory. These files are used to define settings and credentials for all services that are deployed. You can copy the example files to get started:

cp docs/example.env .env
cp -r docs/example-secrets/ secrets/

.env#

The .env needs to be modified in order for the containers to be build properly. This is the entire configuration file for all applications. All relevant hints can be found within.

📄 .env

################################################################################
# ENVIRONMENT SETUP
#
# * commented out lines are the default
################################################################################

DOMAIN_NAME="example.com"
ADMIN_USER="admin"

# Optional Secondary Domain
# SECONDARY_DOMAIN_NAME="example.dev"

TZ="America/Denver"
PUID="1000" # id -u
PGID="1000" # id -g

# UNIVERSAL_RESTART_POLICY="unless-stopped"

# Uncomment to test with Let's Encrypt (HTTPS) Staging Environment
# LETS_ENCRYPT_ENV="https://acme-staging-v02.api.letsencrypt.org/directory"

##################################################################
# DIRECTORY SETUP
#
# * You must customize these paths to match your own setup
# * For my personal setup, I have a NAS mounted at /media/storage
##################################################################

DOCKER_DIRECTORY="/path/to/this/repo"

COMPLETED_DOWNLOADS="/media/storage/downloads"
INCOMPLETE_DOWNLOADS="/downloads/incomplete"
MOVIE_DIR="/media/storage/movies"
TV_DIR="/media/storage/tv"
BOOKS_DIR="/media/storage/books"
# PLEX_TRANSCODE_DIR="/tmp"

##################################################################
# NETWORKING VARIABLES
#
# * You must customize these variables to match your own setup
# * These are the default values for my local network
##################################################################

PHYSICAL_SERVER_IP="192.168.1.55" # ip route get 1 | awk '{print $7}')
PHYSICAL_SERVER_NETWORK="192.168.1.0/24"

DUCKDNS_TOKEN=XXXXXX-XXX-XXXXX-XXXXX
DUCKDNS_SUBDOMAIN=example
CLOUDFLARE_EMAIL=[email protected]

##################################################################
# APP VARIABLES
##################################################################

### SHARED DATABASE (POSTGRES)
# POSTGRES_USER="postgres"
# POSTGRES_USER="postgres"
POSTGRES_PASSWORD="SharedDatabasePassword"
# POSTGRES_MULTIPLE_DATABASES="umami"

### TRANSMISSION (VPN + TORRENTS)
OPENVPN_PROVIDER=NORDVPN
OPENVPN_USERNAME=[email protected]
OPENVPN_PASSWORD=XXXXXXXXXXXXXX

### PLEX (CLAIM TOKEN)
PLEX_CLAIM=XXXXXXXXXXXXXX

### LLM (NEXT-WEB + SLACK BOT)
OPENAI_API_KEY="sk-XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX"
ANTHROPIC_API_KEY="sk-ant-XXXXXXXXXXXXXXXXXXXXXXXXXX"
SLACK_BOT_TOKEN=xoxb-XXXXXXXXXXX-XXXXXXXXXXX-XXXXXXXXXXX
SLACK_APP_TOKEN=xapp-1-XXXXXXXXXX-XXXXXXXXXX-XXXXXXXXXX
# SLACK_APP_LOG_LEVEL="INFO"

Once you're done with all of the steps in this process you should have the following fields filled out in the .env file:

DUCKDNS_TOKEN=XXXXXX-XXX-XXXXX-XXXXX
DUCKDNS_SUBDOMAIN=example
[email protected]

And you should also update the secrets files:

providers.google.client-id=GoogleClientID
providers.google.client-secret=GoogleClientSecret
secret=RandomSecretString
[email protected]
[email protected]
[email protected]
xxxxxxxx-xxxxxxxxxxxxx-xxxxxxxxxx

acme.json#

You will need to create an empty acme.json file for the application to work and generate an SSL Certificate through LetsEncrypt. However, while initially setting up it will be useful to remove and recreate the file to force certificate recreation. Keep in mind that certificate creation and registration can take some tie. uncomment the LETS_ENCRYPT_ENV setting on the .env file to test with the LetsEncrypt staging environment.

  • file location: traefik/core/config/acme/acme.json
  • file permissions (chmod): 600
mkdir -p appdata/traefik/acme/ && \
  rm -f appdata/traefik/acme/acme.json && \
  touch appdata/traefik/acme/acme.json && \
  chmod 600 appdata/traefik/acme/acme.json

Note

If you're comfortable with the Makefile at the root of the project, you can run make config-acme to create the acme.json as described above.

See the Command Line documentation for more information.

Containers in Core Profile#

Creating New Services#

The below example (which has already been done) shows you how to create a new service in the docker compose stack and make it accessible via Traefik. In this example, we are creating a LibreOffice service that can be accessed at https://libreoffice.example.com. If possible, I recommend using a LinuxServer image as they are well maintained and have a common configuration. Once the libreoffice.yaml file is created, you can reference it in the root docker-compose.yaml (by uncommenting the reference to apps/libreoffice.yaml line) and then run docker compose up --profile all -d.

# yaml-language-server: $schema=https://raw.githubusercontent.com/compose-spec/compose-spec/master/schema/compose-spec.json
#######################################
# LIBREOFFICE (OFFICE SUITE)
#######################################

services:
    libreoffice:
        container_name: libreoffice
        image: lscr.io/linuxserver/libreoffice:latest
        profiles: ["miscellaneous", "all"]
        environment:
            - PUID=${PUID}
            - PGID=${PGID}
            - TZ=${TZ}
        volumes:
            - ${DOCKER_DIRECTORY}/appdata/libreoffice:/config
        security_opt:
            - no-new-privileges:true
        networks:
            traefik:
        labels:
            traefik.enable: true
            traefik.http.routers.libreoffice-rtr.rule: Host(`libreoffice.${DOMAIN_NAME}`)
            traefik.http.routers.libreoffice-rtr.service: libreoffice-svc
            traefik.http.services.libreoffice-svc.loadbalancer.server.port: 8888
            traefik.http.routers.libreoffice-rtr.entrypoints: websecure
            traefik.http.routers.libreoffice-rtr.middlewares: chain-oauth-google@file
################################################################################
# HOMELAB
################################################################################

include:
    #############################################################################
    # TRAEFIK (CORE)
    #############################################################################

    - apps/traefik/docker-compose.yaml # Traefik (Reverse Proxy)
    - apps/duckdns.yaml # DuckDNS (Dynamic DNS)
    - apps/oauth.yaml # OAuth (Authentication)
    - apps/socket-proxy.yaml # Docker Socket Proxy (Security)

    #############################################################################
    # MEDIA
    #############################################################################

    - apps/heimdall.yaml # Heimdall (Landing Page)
    - apps/plex.yaml # Plex (Media Server)
    - apps/sonarr.yaml # Sonarr (TV Show Downloads)
    - apps/radarr.yaml # Radarr (Movie Downloads)
    - apps/prowlarr.yaml # Prowlarr (Indexer)
    - apps/overseerr.yaml # Overseerr (Media Requests)
    - apps/tautulli.yaml # Tautulli (Plex Analytics)
    - apps/transmission.yaml # Transmission (Torrents Behind VPN)
    - apps/sabnzbd.yaml # SABnzbd (Usenet Downloading)
    - apps/nzbhydra.yaml # NZBHydra2 (Usenet Browsing)
    # - apps/readarr.yaml # Readarr (Ebook Downloads)
    # - apps/calibre.yaml # Calibre (Books Management)
    # - apps/calibre-web.yaml # Calibre Web-UI

    #############################################################################
    # UTILITIES
    #############################################################################

    - apps/watchtower.yaml #  Watchtower (Container Updating)
    # - apps/postgres.yaml # PostgreSQL (Shared Database)
    # - apps/sftpgo.yaml # SFTPGo (File Management)
    # - apps/pihole.yaml # Pi-hole (DNS Ad-Blocking)
    # - apps/portainer.yaml # Portainer (Container Management)

    #############################################################################
    # MISCELLANEOUS
    #
    # * Note the existing services that are disabled by default.
    #############################################################################

    # - apps/chat-gpt-next-web.yaml # ChatGPT Next Web
    # - apps/chatgpt-in-slack.yaml # ChatGPT Slack Bot
    # - apps/libreoffice.yaml # LibreOffice (Office Suite)
    # - apps/umami.yaml # Umami Analytics
    # - apps/homeassistant/docker-compose.yaml # Home Assistant (Smart Home)
    # - apps/healthchecks.yaml # Healthchecks (Monitoring)

################################################################################
# NETWORK CONFIGURATION
################################################################################

networks:
    traefik:
        driver: bridge
        ipam:
            config:
                - subnet: 192.168.90.0/24
    docker:
        driver: bridge
        ipam:
            config:
                - subnet: 192.168.91.0/24
    internal:
        driver: bridge
        ipam:
            config:
                - subnet: 192.168.92.0/24
  • traefik.enable
    • Allows Traefik to interact with this application
  • traefik.http.routers.libreoffice-rtr.rule
    • Creates a router, "libreoffice-rtr", that can be accessed @ libreoffice.example.com
  • traefik.http.routers.libreoffice-rtr.service
    • Attaches a load balancing service, "libreoffice-svc",to the router
  • traefik.http.services.libreoffice-svc.loadbalancer.server.port
    • Instructs the load balancer to operate on port 8888 (the exposed port of the application)
  • traefik.http.routers.libreoffice-rtr.entrypoints
  • traefik.http.routers.libreoffice-rtr.middlewares:
    • Instructs the router to use the middleware service, chain-oauth-google@file which requires Google OAuth for access