Traefik
Traefik is the reverse proxy and TLS termination point for all services. It runs in a NixOS container on the host network (no private network).
Source: server/containers/traefik.nix
Network
| Property | Value |
|---|---|
| Private network | No (host network) |
| Ports | 80 (HTTP), 443 (HTTPS) |
| Subdomain | N/A (routes to other services) |
TLS Configuration
Certificate Resolution
Certificates are obtained from Let's Encrypt using the DNS-01 challenge via Cloudflare:
certificatesResolvers.letsencrypt.acme = {
email = "test.rearrange726@passfwd.com";
storage = "${config.services.traefik.dataDir}/acme.json";
dnsChallenge = {
provider = "cloudflare";
resolvers = [ "1.1.1.1:53" "8.8.8.8:53" ];
};
};The Cloudflare DNS API token is provided via agenix:
age.secrets.cloudflare_dns_token = {
mode = "440";
owner = "traefik";
group = "traefik";
file = ../secrets/cloudflare_dns_token.age;
};The secret is passed to Traefik via environmentFiles, which sets the CF_DNS_API_TOKEN environment variable.
TLS Versions
Two TLS profiles are defined:
Default (strict):
default = {
sniStrict = true;
minVersion = "VersionTLS13";
};Intermediate (for older clients):
intermediate = {
minVersion = "VersionTLS12";
cipherSuites = [
"TLS_ECDHE_RSA_WITH_CHACHA20_POLY1305"
"TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256"
"TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384"
"TLS_ECDHE_ECDSA_WITH_CHACHA20_POLY1305"
"TLS_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256"
"TLS_ECDHE_ECDSA_WITH_AES_256_GCM_SHA384"
];
};The default profile enforces TLS 1.3 with strict SNI. Services that need TLS 1.2 compatibility can reference the intermediate profile.
Entrypoints
entryPoints = {
web = {
address = ":80";
http.redirections.entrypoint = { to = "websecure"; scheme = "https"; };
};
websecure = {
address = ":443";
asDefault = true;
http.tls.certResolver = "letsencrypt";
};
};- Port 80 -- HTTP, always redirects to HTTPS.
- Port 443 -- HTTPS, default entrypoint with automatic TLS.
API & Dashboard
Both are disabled for security:
api = {
debug = false;
insecure = false;
dashboard = false;
};Dynamic Configuration
Traefik's dynamic configuration (routers, services, middlewares) is not defined in this file. Instead, each service module injects its own routing configuration:
# In containers/immich.nix:
containers.traefik.config.services.traefik.dynamicConfigOptions.http = {
services.immich.loadBalancer.servers = [{ url = "http://10.10.10.5:2283"; }];
routers.immich = {
rule = "Host(`photos.nemnix.site`)";
service = "immich";
entrypoints = [ "websecure" ];
middlewares = [ "authelia" ];
};
};NixOS module merging combines all these definitions into a single Traefik configuration. This is a powerful pattern that keeps routing config co-located with each service.
Route Summary
| Subdomain | Service | Authelia |
|---|---|---|
auth.nemnix.site | Authelia | No |
adguard.nemnix.site | AdGuard Home | Yes |
photos.nemnix.site | Immich | Yes |
vault.nemnix.site | Vaultwarden | No |
cloud.nemnix.site | OpenCloud | No |
Logging
log = {
level = "INFO";
format = "json";
};Structured JSON logging at INFO level. Access logs are not explicitly configured.