mattermost-traefik-letsencrypt-docker-compose.yml
Rama main · réplica de lectura del repositorio original.
# Image pins live HERE, as interpolation defaults, so `git pull` (or
# re-downloading this file) delivers the exact version combination this
# repository has tested — no .env editing required. Setting the matching
# variable in .env overrides the default for anyone who wants to pin
# differently; .env itself carries only secrets and deliberate overrides.
# CI (Trivy scans, check-pin-freshness, the ephemeral test .env) reads
# the pins from these lines — this block is the single source of truth.
x-images:
traefik: &traefik-image ${TRAEFIK_IMAGE_TAG:-traefik:${TRAEFIK_IMAGE_VERSION:-3.7@sha256:f4bb143d1135a579b630ed491e17ec50f6f7c9c6a30585ff3d385471d2a86f5a}}
postgres: &postgres-image ${MATTERMOST_POSTGRES_IMAGE_TAG:-postgres:${MATTERMOST_POSTGRES_IMAGE_VERSION:-16@sha256:f9a7b9ebf6f56201f949db745e47bc423c0f0f8294bafe991cc10bf74b029657}}
mattermost: &mattermost-image ${MATTERMOST_IMAGE_TAG:-mattermost/mattermost-team-edition:${MATTERMOST_IMAGE_VERSION:-12.0.2@sha256:0e435f3397390c36797d0c09b6fcfd73841ebf89bb292d6c6e109082a21ad7b6}}
networks:
mattermost-network:
external: true
traefik-network:
external: true
volumes:
mattermost-data:
mattermost-config:
mattermost-logs:
mattermost-plugins:
mattermost-client-plugins:
mattermost-bleve-indexes:
mattermost-postgres:
mattermost-postgres-backup:
mattermost-data-backups:
mattermost-database-backups:
traefik-certificates:
services:
postgres:
image: *postgres-image
# Hardening: no privilege escalation via setuid binaries
security_opt:
- no-new-privileges:true
# Drop every Linux capability; add back only what this image's
# entrypoint needs (bind :80/:443, chown its data directory, drop to
# its service user).
cap_drop:
- ALL
cap_add:
- CHOWN
- DAC_READ_SEARCH
- FOWNER
- SETGID
- SETUID
# Resource limits as .env-overridable defaults (e.g. MATTERMOST_POSTGRES_MEMORY_LIMIT=2g);
# the override survives every git pull.
# Docker's default is ten seconds, then SIGKILL. That is not always enough
# for this image to finish shutting down: PostgreSQL has a checkpoint to
# write, MariaDB has InnoDB to flush, and Redis saves its dataset on the way
# out. Killed halfway, the next start does crash recovery, and a Redis that
# held another application's file locks leaves them behind for a person to
# clear by hand. Sixty seconds, overridable in .env.
stop_grace_period: ${MATTERMOST_POSTGRES_STOP_GRACE_PERIOD:-60s}
deploy:
resources:
limits:
memory: ${MATTERMOST_POSTGRES_MEMORY_LIMIT:-1g}
cpus: "${MATTERMOST_POSTGRES_CPU_LIMIT:-1.0}"
reservations:
memory: ${MATTERMOST_POSTGRES_MEMORY_RESERVATION:-128m}
cpus: "${MATTERMOST_POSTGRES_CPU_RESERVATION:-0.25}"
volumes:
- mattermost-postgres:/var/lib/postgresql/data
environment:
POSTGRES_DB: ${MATTERMOST_DB_NAME:-mattermostdb}
POSTGRES_USER: ${MATTERMOST_DB_USER:-mattermostdbuser}
POSTGRES_PASSWORD: ${MATTERMOST_DB_PASSWORD:?set in .env - see .env.example for generation command}
networks:
- mattermost-network
healthcheck:
test: [ "CMD", "pg_isready", "-q", "-d", "${MATTERMOST_DB_NAME:-mattermostdb}", "-U", "${MATTERMOST_DB_USER:-mattermostdbuser}" ]
interval: 10s
timeout: 5s
retries: 3
start_period: 60s
restart: unless-stopped
mattermost:
image: *mattermost-image
# Resource limits as .env-overridable defaults (e.g. MATTERMOST_MEMORY_LIMIT=2g);
# the override survives every git pull.
deploy:
resources:
limits:
memory: ${MATTERMOST_MEMORY_LIMIT:-1g}
cpus: "${MATTERMOST_CPU_LIMIT:-1.0}"
pids: 200
reservations:
memory: ${MATTERMOST_MEMORY_RESERVATION:-128m}
cpus: "${MATTERMOST_CPU_RESERVATION:-0.25}"
volumes:
- mattermost-data:${DATA_PATH:-/mattermost/data}
- mattermost-config:/mattermost/config
- mattermost-logs:/mattermost/logs
- mattermost-plugins:/mattermost/plugins
- mattermost-client-plugins:/mattermost/client/plugins
- mattermost-bleve-indexes:/mattermost/bleve-indexes
environment:
TZ: ${MATTERMOST_TIMEZONE:-UTC}
MM_SQLSETTINGS_DRIVERNAME: ${MATTERMOST_DB_TYPE:-postgres}
MM_SQLSETTINGS_DATASOURCE: postgres://${MATTERMOST_DB_USER:-mattermostdbuser}:${MATTERMOST_DB_PASSWORD}@postgres:5432/${MATTERMOST_DB_NAME:-mattermostdb}?sslmode=disable&connect_timeout=10
MM_BLEVESETTINGS_INDEXDIR: /mattermost/bleve-indexes
DOMAIN: ${MATTERMOST_HOSTNAME:?set in .env - see .env.example}
MM_SERVICESETTINGS_SITEURL: ${MATTERMOST_URL:?set in .env - see .env.example}
MM_SERVICESETTINGS_FORWARD80TO443: 'false'
MATTERMOST_CONTAINER_READONLY: 'false'
networks:
- mattermost-network
- traefik-network
security_opt:
- no-new-privileges:true
read_only: 'false'
tmpfs:
- /tmp
labels:
# Enable Traefik for this container
- "traefik.enable=true"
# Match incoming requests on a specific hostname
- "traefik.http.routers.mattermost.rule=Host(`${MATTERMOST_HOSTNAME}`)"
# Assign the router to a named Traefik service
- "traefik.http.routers.mattermost.service=mattermost"
# Use the 'websecure' (HTTPS) entry point
- "traefik.http.routers.mattermost.entrypoints=websecure"
# Define the internal container port for routing
- "traefik.http.services.mattermost.loadbalancer.server.port=8065"
# Enable TLS on this router
- "traefik.http.routers.mattermost.tls=true"
# Use Let's Encrypt for certificate management
- "traefik.http.routers.mattermost.tls.certresolver=letsencrypt"
# Pass the original Host header to the container
- "traefik.http.services.mattermost.loadbalancer.passhostheader=true"
# Apply a compression middleware
- "traefik.http.routers.mattermost.middlewares=compresstraefik"
# Define settings for the compression middleware
- "traefik.http.middlewares.compresstraefik.compress=true"
# Assign the Zabbix UDP router to a named Traefik service
- "traefik.udp.routers.mm-call-rtr.service=mm-call-svc"
# Use the 'zabbix-udp' (custom) entry point
- "traefik.udp.routers.mm-call-rtr.entrypoints=calls"
# Define the internal container port for routing to the Zabbix UDP service
- "traefik.udp.services.mm-call-svc.loadBalancer.server.port=8443"
# Specify which Docker network Traefik should use for routing
- "traefik.docker.network=traefik-network"
restart: unless-stopped
depends_on:
postgres:
condition: service_healthy
traefik:
condition: service_healthy
traefik:
image: *traefik-image
# Hardening: no privilege escalation via setuid binaries
security_opt:
- no-new-privileges:true
# Drop every Linux capability; add back only what this image's
# entrypoint needs (bind :80/:443, chown its data directory, drop to
# its service user).
cap_drop:
- ALL
cap_add:
- NET_BIND_SERVICE
# Resource limits as .env-overridable defaults (e.g. TRAEFIK_MEMORY_LIMIT=2g);
# the override survives every git pull.
deploy:
resources:
limits:
memory: ${TRAEFIK_MEMORY_LIMIT:-256m}
cpus: "${TRAEFIK_CPU_LIMIT:-0.5}"
reservations:
memory: ${TRAEFIK_MEMORY_RESERVATION:-64m}
cpus: "${TRAEFIK_CPU_RESERVATION:-0.1}"
command:
# Set the log level (DEBUG, INFO, WARN, ERROR)
- "--log.level=${TRAEFIK_LOG_LEVEL:-WARN}"
# Enable the built-in API and web-based dashboard
- "--api.dashboard=true"
# Enable the /ping endpoint so we can health-check Traefik
- "--ping=true"
# Assign the /ping endpoint to a dedicated entry point on port 8082
- "--ping.entrypoint=ping"
- "--entrypoints.ping.address=:8082"
# Define the primary HTTP entry point on port 80
- "--entrypoints.web.address=:80"
# Define the secure (HTTPS) entry point on port 443
- "--entrypoints.websecure.address=:443"
# Timeouts on the HTTPS entry point. Unset, they are Traefik's own
# defaults; set them in .env to tune (see .env.example).
- "--entrypoints.websecure.transport.respondingTimeouts.readTimeout=${TRAEFIK_READ_TIMEOUT:-60s}"
- "--entrypoints.websecure.transport.respondingTimeouts.writeTimeout=${TRAEFIK_WRITE_TIMEOUT:-0s}"
- "--entrypoints.websecure.transport.respondingTimeouts.idleTimeout=${TRAEFIK_IDLE_TIMEOUT:-180s}"
# HTTP -> HTTPS redirect at entrypoint level
- "--entrypoints.web.http.redirections.entrypoint.to=websecure"
- "--entrypoints.web.http.redirections.entrypoint.scheme=https"
# Define the Mattermost UDP entry point on port 8443
- "--entryPoints.calls.address=:8443/udp"
# Enable the Docker provider to detect containers and their labels
- "--providers.docker=true"
# Point Traefik to the Docker socket
- "--providers.docker.endpoint=unix:///var/run/docker.sock"
# Prevent automatic exposure of all containers; only expose containers
# with "traefik.enable=true"
- "--providers.docker.exposedbydefault=false"
# Use ACME (Let's Encrypt) to generate/renew certificates via TLS challenge
- "--certificatesresolvers.letsencrypt.acme.tlschallenge=true"
# The email address used by Let's Encrypt for renewal notices
- "--certificatesresolvers.letsencrypt.acme.email=${TRAEFIK_ACME_EMAIL:?set in .env - see .env.example}"
# The file where ACME certificates are stored inside the container
- "--certificatesresolvers.letsencrypt.acme.storage=/etc/traefik/acme/acme.json"
# Enable Prometheus metrics
- "--metrics.prometheus=true"
# Configure Prometheus histogram buckets
- "--metrics.prometheus.buckets=0.1,0.3,1.2,5.0"
# Check for newer Traefik versions and optionally log that info
- "--global.checknewversion=true"
# Disable sending anonymous usage data to the Traefik maintainers
- "--global.sendanonymoususage=false"
volumes:
- /var/run/docker.sock:/var/run/docker.sock:ro
- traefik-certificates:/etc/traefik/acme
networks:
- traefik-network
ports:
- "80:80"
- "443:443"
- "8443:8443/udp"
healthcheck:
test: ["CMD", "wget", "http://localhost:8082/ping","--spider"]
interval: 10s
timeout: 5s
retries: 3
start_period: 5s
labels:
# Enable Traefik for this container
- "traefik.enable=true"
# A router to expose the Traefik dashboard
- "traefik.http.routers.dashboard.rule=Host(`${TRAEFIK_HOSTNAME:?set in .env - see .env.example}`)"
- "traefik.http.routers.dashboard.entrypoints=websecure"
- "traefik.http.routers.dashboard.tls=true"
- "traefik.http.routers.dashboard.tls.certresolver=letsencrypt"
- "traefik.http.routers.dashboard.service=api@internal"
# Basic Authentication for the Traefik dashboard
- "traefik.http.routers.dashboard.middlewares=authtraefik"
- "traefik.http.middlewares.authtraefik.basicauth.users=${TRAEFIK_BASIC_AUTH:?set in .env - see .env.example for htpasswd generation}"
# Specify the internal server port to the dashboard service
- "traefik.http.services.dashboard.loadbalancer.server.port=8080"
# Pass the original Host header to the backend
- "traefik.http.services.dashboard.loadbalancer.passhostheader=true"
restart: unless-stopped
backups:
image: *postgres-image
# Hardening: no privilege escalation via setuid binaries
security_opt:
- no-new-privileges:true
# Drop every Linux capability; add back only what this image's
# entrypoint needs (bind :80/:443, chown its data directory, drop to
# its service user).
cap_drop:
- ALL
cap_add:
- CHOWN
- DAC_OVERRIDE
- FOWNER
# Resource limits as .env-overridable defaults (e.g. MATTERMOST_BACKUPS_MEMORY_LIMIT=2g);
# the override survives every git pull.
deploy:
resources:
limits:
memory: ${MATTERMOST_BACKUPS_MEMORY_LIMIT:-256m}
cpus: "${MATTERMOST_BACKUPS_CPU_LIMIT:-0.5}"
reservations:
memory: ${MATTERMOST_BACKUPS_MEMORY_RESERVATION:-32m}
cpus: "${MATTERMOST_BACKUPS_CPU_RESERVATION:-0.1}"
command: >-
bash -c 'set -o pipefail;
sleep "$$BACKUP_INIT_SLEEP";
while true; do
STAMP="$$(date +%Y-%m-%d_%H-%M)";
DB_FILE="$$POSTGRES_BACKUPS_PATH/$$POSTGRES_BACKUP_NAME-$$STAMP.gz";
echo "[$$(date -Iseconds)] Starting database backup to $$DB_FILE";
if pg_dump -h postgres -p 5432 -d $$MATTERMOST_DB_NAME -U $$MATTERMOST_DB_USER | gzip > "$$DB_FILE.partial"; then
mv "$$DB_FILE.partial" "$$DB_FILE";
echo "[$$(date -Iseconds)] Database backup OK: $$DB_FILE ($$(stat -c %s "$$DB_FILE") bytes)";
else
echo "[$$(date -Iseconds)] Database backup FAILED - partial file kept as $$DB_FILE.failed for diagnosis" >&2;
mv "$$DB_FILE.partial" "$$DB_FILE.failed" 2>/dev/null || true;
fi;
DATA_FILE="$$DATA_BACKUPS_PATH/$$DATA_BACKUP_NAME-$$STAMP.tar.gz";
echo "[$$(date -Iseconds)] Starting data backup to $$DATA_FILE";
# GNU tar exits 1 when a live application changed a file mid-read;
# the archive is complete - only exit 2 is a real failure. But an exit
# code has never been a statement about whether the file it produced
# opens, so the archive is read back before it is renamed into place.
# One tar -tzf a day is not a cost worth arguing about, and it is the
# difference between a backup and a file named like one. No GNU-only
# flags: alpine images ship BusyBox tar.
tar -zcpf "$$DATA_FILE.partial" $$DATA_PATH; RC=$$?;
if { [ "$$RC" -eq 0 ] || [ "$$RC" -eq 1 ]; } && tar -tzf "$$DATA_FILE.partial" > /dev/null 2>&1; then
mv "$$DATA_FILE.partial" "$$DATA_FILE";
echo "[$$(date -Iseconds)] Data backup OK: $$DATA_FILE ($$(stat -c %s "$$DATA_FILE") bytes)$$([ "$$RC" -eq 1 ] && echo " - some files changed while being read")";
else
echo "[$$(date -Iseconds)] Data backup FAILED - partial file kept as $$DATA_FILE.failed for diagnosis" >&2;
mv "$$DATA_FILE.partial" "$$DATA_FILE.failed" 2>/dev/null || true;
fi;
if [ "$${POSTGRES_BACKUP_PRUNE_DAYS:-0}" -ge 1 ]; then
find "$$POSTGRES_BACKUPS_PATH" -type f -name "$$POSTGRES_BACKUP_NAME-*" -mtime "+$$POSTGRES_BACKUP_PRUNE_DAYS" -delete;
fi;
if [ "$${DATA_BACKUP_PRUNE_DAYS:-0}" -ge 1 ]; then
find "$$DATA_BACKUPS_PATH" -type f -name "$$DATA_BACKUP_NAME-*" -mtime "+$$DATA_BACKUP_PRUNE_DAYS" -delete;
fi;
sleep "$$BACKUP_INTERVAL";
done'
volumes:
- mattermost-postgres-backup:/var/lib/postgresql/data
- mattermost-data:${DATA_PATH:-/mattermost/data}
- mattermost-data-backups:${DATA_BACKUPS_PATH:-/srv/mattermost-application-data/backups}
- mattermost-database-backups:${POSTGRES_BACKUPS_PATH:-/srv/mattermost-postgres/backups}
environment:
MATTERMOST_DB_NAME: ${MATTERMOST_DB_NAME:-mattermostdb}
MATTERMOST_DB_USER: ${MATTERMOST_DB_USER:-mattermostdbuser}
PGPASSWORD: ${MATTERMOST_DB_PASSWORD}
BACKUP_INIT_SLEEP: ${BACKUP_INIT_SLEEP:-30m}
BACKUP_INTERVAL: ${BACKUP_INTERVAL:-24h}
POSTGRES_BACKUP_PRUNE_DAYS: ${POSTGRES_BACKUP_PRUNE_DAYS:-7}
DATA_BACKUP_PRUNE_DAYS: ${DATA_BACKUP_PRUNE_DAYS:-7}
POSTGRES_BACKUPS_PATH: ${POSTGRES_BACKUPS_PATH:-/srv/mattermost-postgres/backups}
DATA_BACKUPS_PATH: ${DATA_BACKUPS_PATH:-/srv/mattermost-application-data/backups}
DATA_PATH: ${DATA_PATH:-/mattermost/data}
POSTGRES_BACKUP_NAME: ${POSTGRES_BACKUP_NAME:-mattermost-postgres-backup}
DATA_BACKUP_NAME: ${DATA_BACKUP_NAME:-mattermost-application-data-backup}
networks:
- mattermost-network
restart: unless-stopped
depends_on:
postgres:
condition: service_healthy