338 lines
14 KiB
YAML
338 lines
14 KiB
YAML
########################
|
|
# Networks & Volumes
|
|
########################
|
|
networks:
|
|
traefik_proxy:
|
|
name: traefik_proxy
|
|
|
|
volumes:
|
|
traefik_letsencrypt:
|
|
traefik_logs:
|
|
portainer_data:
|
|
uptime_kuma_data:
|
|
umami_data:
|
|
pgadmin_data:
|
|
authelia_config:
|
|
authelia_db_data:
|
|
grafana_data:
|
|
prometheus_data:
|
|
alertmanager_data:
|
|
|
|
########################
|
|
# Services
|
|
########################
|
|
services:
|
|
|
|
## ─────────────────────────────────────────────
|
|
## Traefik — edge router + ACME (HTTP-01)
|
|
## ─────────────────────────────────────────────
|
|
traefik:
|
|
image: traefik:latest
|
|
container_name: traefik
|
|
restart: unless-stopped
|
|
ports:
|
|
- "80:80"
|
|
- "443:443"
|
|
# Mail protocol ports for MailCow integration
|
|
- "25:25" # SMTP
|
|
- "465:465" # SMTPS
|
|
- "587:587" # Submission
|
|
- "143:143" # IMAP
|
|
- "993:993" # IMAPS
|
|
- "110:110" # POP3
|
|
- "995:995" # POP3S
|
|
- "4190:4190" # ManageSieve
|
|
networks: [traefik_proxy]
|
|
environment:
|
|
TZ: "${TZ}"
|
|
command:
|
|
# Experimental plugins
|
|
- --experimental.plugins.traefik-umami-plugin.modulename=github.com/1cedsoda/traefik-umami-plugin
|
|
- --experimental.plugins.traefik-umami-plugin.version=v1.0.3
|
|
|
|
# Providers
|
|
- --providers.docker=true
|
|
- --providers.docker.exposedbydefault=false
|
|
|
|
# Entrypoints
|
|
- --entrypoints.web.address=:80
|
|
- --entrypoints.web.http.redirections.entrypoint.to=websecure
|
|
- --entrypoints.web.http.redirections.entrypoint.scheme=https
|
|
- --entrypoints.websecure.address=:443
|
|
- --entrypoints.web.forwardedheaders.insecure=true
|
|
- --entrypoints.websecure.forwardedheaders.insecure=true
|
|
|
|
# Mail protocol entrypoints for MailCow integration
|
|
- --entrypoints.smtp.address=:25
|
|
- --entrypoints.smtps.address=:465
|
|
- --entrypoints.submission.address=:587
|
|
- --entrypoints.imap.address=:143
|
|
- --entrypoints.imaps.address=:993
|
|
- --entrypoints.pop3.address=:110
|
|
- --entrypoints.pop3s.address=:995
|
|
- --entrypoints.sieve.address=:4190
|
|
|
|
# Dashboard/API (internal)
|
|
- --api=true
|
|
- --api.dashboard=true
|
|
|
|
# ACME via HTTP-01 (no registrar API needed)
|
|
- --certificatesresolvers.le.acme.email=${ACME_EMAIL}
|
|
- --certificatesresolvers.le.acme.storage=/letsencrypt/acme.json
|
|
- --certificatesresolvers.le.acme.httpchallenge=true
|
|
- --certificatesresolvers.le.acme.httpchallenge.entrypoint=web
|
|
# (Alt) Use TLS-ALPN-01 if port 80 is blocked:
|
|
# - --certificatesresolvers.le.acme.tlschallenge=true
|
|
|
|
# Global timeouts for slow backends
|
|
- --serversTransport.forwardingTimeouts.dialTimeout=30s
|
|
- --serversTransport.forwardingTimeouts.responseHeaderTimeout=60s
|
|
- --serversTransport.forwardingTimeouts.idleConnTimeout=180s
|
|
|
|
# Logs
|
|
- --accesslog.filepath=/var/log/traefik/access.log
|
|
- --accesslog.bufferingsize=100
|
|
- --log.level=INFO
|
|
- --metrics.prometheus=true
|
|
volumes:
|
|
- /var/run/docker.sock:/var/run/docker.sock:ro
|
|
- traefik_letsencrypt:/letsencrypt
|
|
- traefik_logs:/var/log/traefik
|
|
labels:
|
|
- traefik.enable=true
|
|
|
|
# Reusable security headers
|
|
- traefik.http.middlewares.security-headers.headers.stsSeconds=31536000
|
|
- traefik.http.middlewares.security-headers.headers.stsIncludeSubdomains=true
|
|
- traefik.http.middlewares.security-headers.headers.stsPreload=true
|
|
- traefik.http.middlewares.security-headers.headers.browserXssFilter=true
|
|
- traefik.http.middlewares.security-headers.headers.contentTypeNosniff=true
|
|
- traefik.http.middlewares.security-headers.headers.referrerPolicy=no-referrer-when-downgrade
|
|
|
|
# # Basic Auth middleware
|
|
# - traefik.http.middlewares.basic-auth.basicauth.users=${BASIC_AUTH_USERS}
|
|
|
|
# Authelia middleware
|
|
- traefik.http.middlewares.authelia.forwardAuth.address=http://authelia:9091/api/authz/forward-auth
|
|
- traefik.http.middlewares.authelia.forwardAuth.trustForwardHeader=true
|
|
- traefik.http.middlewares.authelia.forwardAuth.authResponseHeaders=Remote-User,Remote-Groups,Remote-Email,Remote-Name
|
|
|
|
# Traefik dashboard (protected)
|
|
- traefik.http.routers.traefik.rule=Host(`traefik.gate.${DOMAIN}`)
|
|
- traefik.http.routers.traefik.entrypoints=websecure
|
|
- traefik.http.routers.traefik.tls.certresolver=le
|
|
- traefik.http.routers.traefik.service=api@internal
|
|
- traefik.http.routers.traefik.middlewares=authelia@docker,security-headers
|
|
|
|
## ─────────────────────────────────────────────
|
|
## Authelia — authentication and authorization
|
|
## ─────────────────────────────────────────────
|
|
authelia:
|
|
image: authelia/authelia:latest
|
|
container_name: authelia
|
|
restart: unless-stopped
|
|
networks: [traefik_proxy]
|
|
volumes:
|
|
- authelia_config:/config
|
|
environment:
|
|
TZ: "${TZ}"
|
|
AUTHELIA_DB_PASSWORD: "${AUTHELIA_DB_PASSWORD}"
|
|
AUTHELIA_JWT_SECRET: "${AUTHELIA_JWT_SECRET}"
|
|
AUTHELIA_SESSION_SECRET: "${AUTHELIA_SESSION_SECRET}"
|
|
depends_on:
|
|
- authelia-db
|
|
labels:
|
|
- traefik.enable=true
|
|
- traefik.http.routers.authelia.rule=Host(`auth.gate.${DOMAIN}`)
|
|
- traefik.http.routers.authelia.entrypoints=websecure
|
|
- traefik.http.routers.authelia.tls.certresolver=le
|
|
- traefik.http.services.authelia.loadbalancer.server.port=9091
|
|
|
|
## ─────────────────────────────────────────────
|
|
## Authelia Database — PostgreSQL
|
|
## ─────────────────────────────────────────────
|
|
authelia-db:
|
|
image: postgres:15-alpine
|
|
container_name: authelia-db
|
|
restart: unless-stopped
|
|
networks: [traefik_proxy]
|
|
environment:
|
|
POSTGRES_DB: authelia
|
|
POSTGRES_USER: authelia
|
|
POSTGRES_PASSWORD: ${AUTHELIA_DB_PASSWORD}
|
|
volumes:
|
|
- authelia_db_data:/var/lib/postgresql/data
|
|
|
|
## ─────────────────────────────────────────────
|
|
## Portainer — Docker control plane
|
|
## ─────────────────────────────────────────────
|
|
portainer:
|
|
image: portainer/portainer-ce:latest
|
|
container_name: portainer
|
|
restart: unless-stopped
|
|
networks: [traefik_proxy]
|
|
volumes:
|
|
- /var/run/docker.sock:/var/run/docker.sock
|
|
- portainer_data:/data
|
|
labels:
|
|
- traefik.enable=true
|
|
- traefik.http.routers.portainer.rule=Host(`portainer.gate.${DOMAIN}`)
|
|
- traefik.http.routers.portainer.entrypoints=websecure
|
|
- traefik.http.routers.portainer.tls.certresolver=le
|
|
- traefik.http.routers.portainer.middlewares=security-headers
|
|
- traefik.http.services.portainer.loadbalancer.server.port=9000
|
|
|
|
## ─────────────────────────────────────────────
|
|
## Uptime Kuma — status page / checks
|
|
## ─────────────────────────────────────────────
|
|
uptime-kuma:
|
|
image: louislam/uptime-kuma:latest
|
|
container_name: uptime-kuma
|
|
restart: unless-stopped
|
|
volumes:
|
|
- uptime_kuma_data:/app/data
|
|
networks: [traefik_proxy]
|
|
labels:
|
|
- traefik.enable=true
|
|
- traefik.http.routers.kuma.rule=Host(`uptime.gate.${DOMAIN}`)
|
|
- traefik.http.routers.kuma.entrypoints=websecure
|
|
- traefik.http.routers.kuma.tls.certresolver=le
|
|
- traefik.http.routers.kuma.middlewares=security-headers
|
|
- traefik.http.services.kuma.loadbalancer.server.port=3001
|
|
|
|
## ─────────────────────────────────────────────
|
|
## Umami — web analytics
|
|
## ─────────────────────────────────────────────
|
|
umami:
|
|
image: ghcr.io/umami-software/umami:postgresql-latest
|
|
container_name: umami
|
|
restart: unless-stopped
|
|
networks: [traefik_proxy]
|
|
environment:
|
|
DATABASE_URL: postgresql://${UMAMI_DB_USER}:${UMAMI_DB_PASS}@umami-db:5432/${UMAMI_DB_NAME}
|
|
DATABASE_TYPE: postgresql
|
|
APP_SECRET: ${UMAMI_APP_SECRET}
|
|
depends_on:
|
|
- umami-db
|
|
labels:
|
|
- traefik.enable=true
|
|
- traefik.http.routers.umami.rule=Host(`umami.gate.${DOMAIN}`)
|
|
- traefik.http.routers.umami.entrypoints=websecure
|
|
- traefik.http.routers.umami.tls.certresolver=le
|
|
- traefik.http.routers.umami.middlewares=security-headers
|
|
- traefik.http.services.umami.loadbalancer.server.port=3000
|
|
|
|
## ─────────────────────────────────────────────
|
|
## Umami Database — PostgreSQL
|
|
## ─────────────────────────────────────────────
|
|
umami-db:
|
|
image: postgres:15-alpine
|
|
container_name: umami-db
|
|
restart: unless-stopped
|
|
networks: [traefik_proxy]
|
|
environment:
|
|
POSTGRES_DB: ${UMAMI_DB_NAME}
|
|
POSTGRES_USER: ${UMAMI_DB_USER}
|
|
POSTGRES_PASSWORD: ${UMAMI_DB_PASS}
|
|
volumes:
|
|
- umami_data:/var/lib/postgresql/data
|
|
|
|
# ─────────────────────────────────────────────
|
|
# pgAdmin — PostgreSQL administration
|
|
# ─────────────────────────────────────────────
|
|
pgadmin:
|
|
image: dpage/pgadmin4:latest
|
|
container_name: pgadmin
|
|
restart: unless-stopped
|
|
networks: [traefik_proxy]
|
|
environment:
|
|
PGADMIN_DEFAULT_EMAIL: ${PGADMIN_DEFAULT_EMAIL}
|
|
PGADMIN_DEFAULT_PASSWORD: ${PGADMIN_DEFAULT_PASSWORD}
|
|
PGADMIN_CONFIG_SERVER_MODE: 'True'
|
|
PGADMIN_CONFIG_MASTER_PASSWORD_REQUIRED: 'False'
|
|
# Fix CSRF issues behind reverse proxy
|
|
PGADMIN_CONFIG_WTF_CSRF_CHECK_DEFAULT: 'False'
|
|
PGADMIN_CONFIG_WTF_CSRF_TIME_LIMIT: 'None'
|
|
PGADMIN_CONFIG_ENHANCED_COOKIE_PROTECTION: 'False'
|
|
# Trust proxy headers
|
|
PGADMIN_CONFIG_PROXY_X_HOST_COUNT: '1'
|
|
PGADMIN_CONFIG_PROXY_X_PREFIX_COUNT: '1'
|
|
volumes:
|
|
- pgadmin_data:/var/lib/pgadmin
|
|
labels:
|
|
- traefik.enable=true
|
|
- traefik.http.routers.pgadmin.rule=Host(`pgadmin.gate.${DOMAIN}`)
|
|
- traefik.http.routers.pgadmin.entrypoints=websecure
|
|
- traefik.http.routers.pgadmin.tls.certresolver=le
|
|
- traefik.http.services.pgadmin.loadbalancer.server.port=80
|
|
|
|
## ─────────────────────────────────────────────
|
|
## Prometheus — monitoring
|
|
## ─────────────────────────────────────────────
|
|
prometheus:
|
|
image: prom/prometheus:latest
|
|
container_name: prometheus
|
|
restart: unless-stopped
|
|
networks: [traefik_proxy]
|
|
volumes:
|
|
- ./prometheus.yml:/etc/prometheus/prometheus.yml
|
|
- ./rules.yml:/etc/prometheus/rules.yml
|
|
- prometheus_data:/prometheus
|
|
- /var/run/docker.sock:/var/run/docker.sock:ro
|
|
command:
|
|
- '--config.file=/etc/prometheus/prometheus.yml'
|
|
- '--storage.tsdb.path=/prometheus'
|
|
- '--web.console.libraries=/etc/prometheus/console_libraries'
|
|
- '--web.console.templates=/etc/prometheus/consoles'
|
|
- '--storage.tsdb.retention.time=200h'
|
|
- '--web.enable-lifecycle'
|
|
labels:
|
|
- traefik.enable=true
|
|
- traefik.http.routers.prometheus.rule=Host(`prometheus.gate.${DOMAIN}`)
|
|
- traefik.http.routers.prometheus.entrypoints=websecure
|
|
- traefik.http.routers.prometheus.tls.certresolver=le
|
|
- traefik.http.routers.prometheus.middlewares=authelia@docker,security-headers
|
|
- traefik.http.services.prometheus.loadbalancer.server.port=9090
|
|
|
|
## ─────────────────────────────────────────────
|
|
## Grafana — visualization
|
|
## ─────────────────────────────────────────────
|
|
grafana:
|
|
image: grafana/grafana:latest
|
|
container_name: grafana
|
|
restart: unless-stopped
|
|
networks: [traefik_proxy]
|
|
environment:
|
|
GF_SECURITY_ADMIN_PASSWORD: ${GRAFANA_ADMIN_PASSWORD}
|
|
volumes:
|
|
- grafana_data:/var/lib/grafana
|
|
labels:
|
|
- traefik.enable=true
|
|
- traefik.http.routers.grafana.rule=Host(`grafana.gate.${DOMAIN}`)
|
|
- traefik.http.routers.grafana.entrypoints=websecure
|
|
- traefik.http.routers.grafana.tls.certresolver=le
|
|
- traefik.http.routers.grafana.middlewares=authelia@docker,security-headers
|
|
- traefik.http.services.grafana.loadbalancer.server.port=3000
|
|
|
|
## ─────────────────────────────────────────────
|
|
## Alertmanager — alert handling
|
|
## ─────────────────────────────────────────────
|
|
alertmanager:
|
|
image: prom/alertmanager:latest
|
|
container_name: alertmanager
|
|
restart: unless-stopped
|
|
networks: [traefik_proxy]
|
|
volumes:
|
|
- ./alertmanager.yml:/etc/alertmanager/alertmanager.yml
|
|
- alertmanager_data:/alertmanager
|
|
command:
|
|
- '--config.file=/etc/alertmanager/alertmanager.yml'
|
|
- '--storage.path=/alertmanager'
|
|
labels:
|
|
- traefik.enable=true
|
|
- traefik.http.routers.alertmanager.rule=Host(`alertmanager.gate.${DOMAIN}`)
|
|
- traefik.http.routers.alertmanager.entrypoints=websecure
|
|
- traefik.http.routers.alertmanager.tls.certresolver=le
|
|
- traefik.http.routers.alertmanager.middlewares=authelia@docker,security-headers
|
|
- traefik.http.services.alertmanager.loadbalancer.server.port=9093
|