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:
.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]
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
- Instructs the router to use the "websecure" entrypoint (https://libreoffice.example.com)
traefik.http.routers.libreoffice-rtr.middlewares:
- Instructs the router to use the middleware service,
chain-oauth-google@file
which requires Google OAuth for access
- Instructs the router to use the middleware service,