{ config, lib, pkgs, ... }: let cfg = config.homelab.apps.traefik; port = 8080; in { options.homelab.apps.traefik = { enable = lib.mkEnableOption "Traefik Reverse Proxy"; sharedNetworkName = lib.mkOption { type = lib.types.str; default = "traefik"; description = "The name of the shared network to connect the container to."; }; dynamicConfigOptions = lib.mkOption { type = lib.types.attrs; default = {}; description = "Dynamic configuration options to write to file and mount into Traefik."; }; }; config = lib.mkIf cfg.enable { homelab.virtualisation.containers.enable = true; # Make sure the Docker network exists. homelab.dockerNetworks."${cfg.sharedNetworkName}" = { requiredBy = [ "docker-traefik.service" ]; }; virtualisation.oci-containers.containers.traefik = { hostname = "traefik"; image = "traefik:v3.4.3"; autoStart = true; ports = [ "80:80/tcp" "443:443/tcp" "${toString port}:${toString port}/tcp" # Web UI (enabled by --api.insecure=true) ]; extraOptions = [ "--network=${cfg.sharedNetworkName}" "--add-host=host.docker.internal:host-gateway" ]; environmentFiles = [ /home/admin/.cloudflare.secret ]; cmd = [ "--api.insecure=true" # Add Docker provider "--providers.docker=true" "--providers.docker.exposedByDefault=false" # Add File provider "--providers.file.filename=/etc/traefik/dynamic_conf.yml" "--providers.file.watch=true" # Add web entrypoint "--entrypoints.web.address=:80/tcp" "--entrypoints.web.http.redirections.entrypoint.to=websecure" "--entrypoints.web.http.redirections.entrypoint.scheme=https" # Add websecure entrypoint "--entrypoints.websecure.address=:443/tcp" "--entrypoints.websecure.http.tls=true" "--entrypoints.websecure.http.tls.certResolver=letsencrypt" "--entrypoints.websecure.http.tls.domains[0].main=depeuter.dev" "--entrypoints.websecure.http.tls.domains[0].sans=*.depeuter.dev" "--entrypoints.websecure.http.tls.domains[1].sans=*.${config.networking.hostName}.depeuter.dev" # Certificates "--certificatesresolvers.letsencrypt.acme.dnschallenge=true" "--certificatesresolvers.letsencrypt.acme.dnschallenge.provider=cloudflare" "--certificatesresolvers.letsencrypt.acme.email=tibo.depeuter@telenet.be" "--certificatesresolvers.letsencrypt.acme.storage=/letsencrypt/acme.json" ]; volumes = let dynamicConfFormat = pkgs.formats.yaml { }; dynamicConfFile = dynamicConfFormat.generate "traefik-dynamic-conf.yml" cfg.dynamicConfigOptions; in [ "letsencryp:/letsencrypt" "/var/run/docker.sock:/var/run/docker.sock:ro" "${dynamicConfFile}:/etc/traefik/dynamic_conf.yml:ro" ]; labels = { "traefik.enable" = "true"; "traefik.http.routers.traefik.rule" = "Host(`traefik.${config.networking.hostName}.depeuter.dev`)"; "traefik.http.services.traefik.loadbalancer.server.port" = toString port; }; }; }; }