When building a home lab, security is always top of mind. You want to ensure that everything runs smoothly, but you also need to keep your services secure without a complex, time-consuming setup. Fortunately, Docker, combined with powerful tools like Authentik and Traefik, makes this task easier and more efficient. In this article, I’ll walk you through how I use these two tools to provide centralised authentication and seamless, secure routing across my entire home lab. Additionally, I’ll dive into the full stack of services that power my home lab, from databases to media management, monitoring tools, and more.
Security First: Protecting My Home Lab with Authentik and Traefik
Authentik: Centralised Authentication for Streamlined Security
At the heart of my home lab’s security is Authentik, an open-source Identity Provider that simplifies authentication across all my services. By consolidating all user access into a single point, Authentik allows me to manage permissions and ensure consistency in who can access which services.
Here’s how the Authentik stack is configured in my Docker setup:
services:
authentik_server:
image: ghcr.io/goauthentik/server:latest
container_name: authentik_server
restart: unless-stopped
command: server
networks:
- default
- t3_proxy
depends_on:
- postgresql
- redis
volumes:
- $DOCKERDIR/appdata/authentik/media:/media
- $DOCKERDIR/appdata/authentik/custom-templates:/templates
- $DOCKERDIR/appdata/authentik/css/custom.css:/web/dist/custom.css
environment:
AUTHENTIK_POSTGRESQL__HOST: 'postgresql'
AUTHENTIK_POSTGRESQL__NAME: 'authentik'
AUTHENTIK_POSTGRESQL__USER: 'authentik'
AUTHENTIK_POSTGRESQL__PASSWORD: 'file:///run/secrets/authentik_postgresql_password'
AUTHENTIK_REDIS__HOST: 'redis'
AUTHENTIK_SECRET_KEY: 'file:///run/secrets/authentik_secret_key'
AUTHENTIK_COOKIE_DOMAIN: '${AUTHENTIK_COOKIE_DOMAIN}'
AUTHENTIK_ERROR_REPORTING__ENABLED: '${AUTHENTIK_ERROR_REPORTING__ENABLED}'
AUTHENTIK_EMAIL__HOST: '${AUTHENTIK_EMAIL__HOST}'
AUTHENTIK_EMAIL__PORT: '${AUTHENTIK_EMAIL__PORT}'
AUTHENTIK_EMAIL__USERNAME: 'file:///run/secrets/authentik_smtp_user'
AUTHENTIK_EMAIL__PASSWORD: 'file:///run/secrets/authentik_smtp_password'
AUTHENTIK_EMAIL__USE_TLS: '${AUTHENTIK_EMAIL__USE_TLS}'
AUTHENTIK_EMAIL__USE_SSL: '${AUTHENTIK_EMAIL__USE_SSL}'
AUTHENTIK_EMAIL__TIMEOUT: '${AUTHENTIK_EMAIL__TIMEOUT}'
AUTHENTIK_EMAIL__FROM: '${AUTHENTIK_EMAIL__FROM}'
AUTHENTIK_LOG_LEVEL: '${AUTHENTIK_LOG_LEVEL}'
AUTHENTIK_GDPR_COMPLIANCE: 'true'
secrets:
- authentik_postgresql_password
- authentik_secret_key
- authentik_smtp_user
- authentik_smtp_password
labels:
- "traefik.enable=true"
- "traefik.http.routers.authentik-rtr.rule=Host(`authentik.$DOMAINNAME`)"
- "traefik.http.routers.authentik-rtr.entrypoints=websecure"
- "traefik.http.routers.authentik-rtr-outpost.rule=HostRegexp(`{subdomain:[a-z0-9-]+}.$DOMAINNAME`) && PathPrefix(`/outpost.goauthentik.io/`)"
- "traefik.http.routers.authentik-rtr-outpost.entrypoints=websecure"
- "traefik.http.routers.authentik-rtr.service=authentik-svc"
- "traefik.http.services.authentik-svc.loadBalancer.server.port=9000"
The authentik_server container acts as the main service for handling authentication requests, while the authentik_worker container processes background tasks. Both containers rely on PostgreSQL and Redis as dependencies for data storage and caching.
The Traefik integration ensures that my Authentik service is securely exposed to the web, and the dynamic routing and SSL management (using Let’s Encrypt) are handled automatically.
Additionally, the use of Docker secrets helps store sensitive information like passwords and API keys securely.
Whether it’s logging into a database or accessing one of my media servers, I rely on Authentik to enforce access control policies. It supports modern authentication protocols like OAuth 2.0, SAML, and OpenID Connect, giving me flexibility in how I secure my various containers. This centralised approach means that instead of managing multiple logins across all services, I can control access from one place, making it both more secure and more efficient.
Traefik: The Reverse Proxy That Automates Security
Once authentication is sorted, the next critical element of securing my home lab is routing and encrypting traffic. This is where Traefik steps in, acting as my reverse proxy to ensure that all external traffic is routed correctly to the appropriate container while keeping everything secure.
Here’s an overview of my Traefik configuration:
services:
# Traefik 3 - Reverse Proxy
traefik:
container_name: traefik
image: traefik:3.2
security_opt:
- no-new-privileges:true
restart: unless-stopped
networks:
t3_proxy:
ipv4_address: 192.168.90.254 # Static IP assignment
socket_proxy:
command:
- --global.checkNewVersion=true
- --global.sendAnonymousUsage=true
- --entrypoints.web.address=:80
- --entrypoints.websecure.address=:443
- --entrypoints.traefik.address=:8080
- --entrypoints.websecure.http.tls=true
- --entrypoints.web.http.redirections.entrypoint.to=websecure
- --entrypoints.web.http.redirections.entrypoint.scheme=https
- --entrypoints.web.http.redirections.entrypoint.permanent=true
- --api=true
- --api.dashboard=true
- --entrypoints.websecure.forwardedHeaders.trustedIPs=$CLOUDFLARE_IPS,$LOCAL_IPS
- --log=true
- --log.filePath=/logs/traefik.log
- --log.level=DEBUG
- --accessLog=true
- --accessLog.filePath=/logs/access.log
- --accessLog.bufferingSize=100
- --accessLog.filters.statusCodes=204-299,400-499,500-599
- --providers.docker=true
- --providers.docker.endpoint=tcp://socket-proxy:2375
- --providers.docker.exposedByDefault=false
- --providers.docker.network=t3_proxy
- --entrypoints.websecure.http.tls.options=tls-opts@file
- --entrypoints.websecure.http.tls.certresolver=dns-cloudflare
- --entrypoints.websecure.http.tls.domains[0].main=$DOMAINNAME
- --entrypoints.websecure.http.tls.domains[0].sans=*.$DOMAINNAME
- --providers.file.directory=/rules
- --providers.file.watch=true
- --certificatesResolvers.dns-cloudflare.acme.storage=/acme.json
- --certificatesResolvers.dns-cloudflare.acme.dnsChallenge.provider=cloudflare
- --certificatesResolvers.dns-cloudflare.acme.dnsChallenge.resolvers=1.1.1.1:53,1.0.0.1:53
- --certificatesResolvers.dns-cloudflare.acme.dnsChallenge.delayBeforeCheck=90
- --experimental.plugins.traefik-real-ip.modulename=github.com/soulbalz/traefik-real-ip
- --experimental.plugins.traefik-real-ip.version=v1.0.3
ports:
- target: 80
published: 80
protocol: tcp
mode: host
- target: 443
published: 443
protocol: tcp
mode: host
volumes:
- $DOCKERDIR/config/traefik3/rules/:/rules
- $DOCKERDIR/config/traefik3/acme/acme.json:/acme.json
- $DOCKERDIR/logs/traefik:/logs
environment:
- PUID=$PUID
- PGID=$PGID
- TZ=$TZ
- CF_DNS_API_TOKEN_FILE=/run/secrets/cf_dns_api_token
- HTPASSWD_FILE=/run/secrets/basic_auth_credentials
- DOMAINNAME
secrets:
- cf_dns_api_token
- basic_auth_credentials
labels:
- "traefik.enable=true"
- "traefik.http.routers.traefik-rtr.entrypoints=websecure"
- "traefik.http.routers.traefik-rtr.rule=Host(`traefik.$DOMAINNAME`)"
- "traefik.http.routers.traefik-rtr.service=api@internal"
- "traefik.http.routers.traefik-rtr.middlewares=chain-oauth@file"
In this configuration:
- Traefik is set up as my reverse proxy, with entrypoints for HTTP (port 80) and HTTPS (port 443).
- It automatically redirects HTTP traffic to HTTPS for secure communication.
- It uses Let’s Encrypt with Cloudflare DNS challenge for automatic SSL certificate management, and stores the certificates in
/acme.json
. - I’ve also configured a Traefik Dashboard, which can be accessed securely via traefik.$DOMAINNAME.
With this setup, Traefik takes care of SSL, routing, logging, and security headers with minimal effort. It integrates seamlessly with Docker containers and automatically provisions certificates for any service I expose through it.
One of the standout features of Traefik is its ability to automatically manage Let’s Encrypt SSL certificates. With just a few labels in the Docker configuration, Traefik handles SSL encryption automatically. No more manually configuring certificates or worrying about expired certs—Traefik takes care of it for me. Here’s a simple example of how I configured Portainer, my container management tool, with Traefik:
labels:
- "traefik.enable=true"
# HTTP Routers
- "traefik.http.routers.portainer-rtr.entrypoints=websecure"
- "traefik.http.routers.portainer-rtr.rule=Host(`portainer.$DOMAINNAME`)"
# Middlewares
- "traefik.http.routers.portainer-rtr.middlewares=chain-no-auth@file"
# HTTP Services
- "traefik.http.routers.portainer-rtr.service=portainer-svc"
- "traefik.http.services.portainer-svc.loadbalancer.server.port=9000"
This configuration ensures that:
- Routing is enabled for the Portainer service.
- HTTPS is enforced for secure communication.
- The “websecure” entrypoint is configured, ensuring secure traffic handling.
- Traefik automatically obtains and renews SSL certificates via Let’s Encrypt.
- Traffic is routed to portainer.DOMAINNAME.com, providing a seamless and secure user experience.
By adding a few simple labels to each container, I can easily scale my services while maintaining a high level of security.
Building a Powerful Home Lab with Docker Stacks
Beyond securing my home lab with Authentik and Traefik, I’ve built a comprehensive set of services using Docker to automate tasks, store data, manage media, monitor performance, and more. Here’s how I structure my home lab with various Docker stacks, all configured through a central Docker Compose setup:
########################### NETWORKS
networks:
default:
driver: bridge
socket_proxy:
name: socket_proxy
driver: bridge
ipam:
config:
- subnet: 192.168.91.0/24
t3_proxy:
name: t3_proxy
driver: bridge
ipam:
config:
- subnet: 192.168.90.0/24
########################### SECRETS
secrets:
cf_email:
file: $DOCKERDIR/secrets/cf_email
cf_api_key:
file: $DOCKERDIR/secrets/cf_api_key
cf_token:
file: $DOCKERDIR/secrets/cf_token
cf_dns_api_token:
file: $DOCKERDIR/secrets/cf_dns_api_token
basic_auth_credentials:
file: $DOCKERDIR/secrets/basic_auth_credentials
authentik_secret_key:
file: $DOCKERDIR/secrets/authentik_secret_key
authentik_postgresql_password:
file: $DOCKERDIR/secrets/authentik_postgresql_password
authentik_smtp_user:
file: $DOCKERDIR/secrets/authentik_smtp_user
authentik_smtp_password:
file: $DOCKERDIR/secrets/authentik_smtp_password
plex_claim:
file: $DOCKERDIR/secrets/plex_claim
include:
########################### SERVICES
# PREFIX milkyway = The MilkyWay
# HOSTNAME=milkyway - defined in .env
# CORE
- stacks/core/socket-proxy.yml
- stacks/core/traefik.yml
- stacks/core/portainer.yml
- stacks/core/authentik.yml
- stacks/core/whoami.yml
# Front End
- stacks/frontend/homepage.yml
# PVRS
- stacks/pvrs/sonarr.yml
- stacks/pvrs/radarr.yml
# Monitoring
- stacks/monitoring/dozzle.yml
- stacks/monitoring/grafana.yml
- stacks/monitoring/glance.yml
- stacks/monitoring/speedtest.yml
- stacks/monitoring/tej.yml
# Media
- stacks/media/plex.yml
- stacks/media/overseer.yml
- stacks/media/tautulli.yml
- stacks/media/wizarr.yml
# Maintenance
- stacks/maintenance/dockergc.yml
# Tools
- stacks/tools/certdumper.yml
# General
- stacks/general/notes.yml
- stacks/general/docmost.yml
- stacks/general/paperless.yml
# AI
- stacks/ai/n8n.yml
# Applications
- stacks/applications/p2g.yml
- stacks/applications/appsmith.yml
This Docker Compose file is the backbone of my environment, setting up multiple networks and organising secrets securely. The networks section defines different isolated networks for different service types (e.g., databases, proxies, media), ensuring proper communication between containers without compromising security. I also store sensitive information such as authentication secrets, API keys, and SMTP passwords in Docker secrets, adding an extra layer of security.
By including various stacks in this configuration, I can scale and manage my entire home lab effortlessly. Each service, from Traefik and Portainer to Sonarr and Plex, is modular, and I can update or add new services without disrupting the rest of the system.
Core Services: Managing Infrastructure and Security
The Core stack includes essential services for authentication, container management, and reverse proxy routing.
- Authentik: As previously mentioned, Authentik provides centralised authentication for all services, ensuring secure user access.
- Portainer: A simple yet powerful tool for managing my Docker containers, making it easy to monitor and control everything in my environment.
- Docker Socket Proxy: Ensures secure communication with Docker by proxying access to the Docker socket.
- Traefik: Handles routing and SSL management, ensuring all traffic is secure and correctly directed.
- Whoami: A small web service used to test Traefik’s routing, ensuring my network setup is functioning as expected.
These core services form the backbone of my home lab, ensuring everything is secure, efficient, and easy to manage.
AI Stack: Automating Tasks with N8n
One of the most exciting components of my Docker environment is the N8n automation tool. N8n allows me to create powerful workflows that automate tasks across different services. From syncing files between services to managing notifications, N8n makes it easier to run a variety of tasks automatically, saving me time and effort.
Database: Keeping Everything Organized
The Database stack handles all the persistent storage needs for my environment.
- Postgres: My primary relational database system for powering critical applications that need scalable storage.
- MariaDB: Another relational database option that supports a variety of services requiring a different database engine.
Both databases are containerised, providing reliable storage for everything from application data to logs.
Front End: A Custom Homepage
In my Front End stack, I have a homepage container that serves as the central hub for accessing all my services. This makes it easy to navigate between different parts of the system, from databases to media servers, with everything in one place.
Media: Streaming and Management Services
The Media stack is the entertainment hub of my home lab, providing services for managing, streaming, and organizing my media collection.
- Overseer: Helps manage the Plex server and keeps track of the media library’s status.
- Plex: Streams movies, TV shows, and music to devices around my home.
- Tautulli: Tracks Plex usage, providing insights into who’s watching what and when.
- Wizarr: Automates the process of searching for and adding new media to my Plex library.
Monitoring: Keeping an Eye on Performance
The Monitoring stack allows me to keep track of my home lab’s health and performance.
- Dozzle: Provides real-time logging of all my containers to identify any issues quickly.
- Grafana: Visualises performance metrics, including CPU usage, memory consumption, and more, integrating with Prometheus for detailed insights.
PVRs: Automating Media Collection
The PVRs stack automates the download and organisation of TV shows and movies.
- Sonarr: Automatically downloads TV shows based on my preferences.
- Radarr: Handles the automated downloading of movies.
- Prowlarr: Manages the indexers for both Sonarr and Radarr.
- AutoBR: Renames and organises downloaded content for easy access.
Tools: Ensuring Security and Efficiency
The Tools stack includes utilities that enhance security and efficiency in my home lab.
- Certdumper: Automatically pulls SSL certificates from Traefik, ensuring secure communication.
- Serge: Automates the deployment and configuration of containers.
- VPN: Provides secure remote access to my home lab, so I can safely access my services from anywhere.
Organising the Home Lab: A Deep Dive into My Docker Setup
Looking at the structure of my home lab stack, everything is neatly organised into directories, making it easy to manage and scale.
1. appdata
The appdata
directory contains all persistent application data for services. This ensures that even if containers are rebuilt or restarted, critical data like configurations, media files, and templates remain intact.
2. config
The config
directory holds configuration files for various services like Traefik, PostgreSQL, and others, centralising management and ensuring that service configurations are consistent across the stack.
3. secrets
The secrets
directory is essential for securely storing sensitive data such as API keys, passwords, and authentication tokens. It includes:
authentik_secret_key
andauthentik_postgresql_password
for secure connections.- Cloudflare API keys and Plex claim for managing external services securely.
These secrets are mounted as Docker secrets, ensuring they’re not exposed in the Dockerfile or containers.
4. services
The services
directory contains individual service configurations, organised into categories like core, monitoring, media, database, and more. This makes managing each stack simpler and more modular.
5. stacks
The stacks
directory contains the Docker Compose configurations for each service group, making it easy to deploy, scale, and manage the entire stack.
6. venv
This directory houses virtual environments for managing dependencies for Python scripts or other automation tasks, isolated from the Docker containers.
7. docker-compose.yml
The docker-compose.yml
file orchestrates everything, defining the services, networks, and volumes to ensure smooth integration and deployment.
Final Thoughts
Running a home lab with Docker has been a game-changer. By combining essential security tools like Authentik and Traefik with powerful services like N8n, Plex, and Grafana, I’ve built a highly efficient, secure, and easily manageable environment. Docker has allowed me to scale, automate, and monitor everything from a single interface, making my home lab more powerful and user-friendly than ever before.
If you’re considering building your own Docker-powered home lab, think about your needs and what tools will help streamline your tasks. Docker provides endless possibilities for automation, security, and customisation.
What Docker stacks do you use in your home lab? Any recommendations for tools or services to try next? Let me know your thoughts!