4e43a6f713
All image references in docker-compose.yml and docker-compose.local.yml
are replaced with ${IMAGE_VAR:-default} env var syntax, so compose files
work standalone without .env while deploy.sh writes resolved image paths.
deploy.sh gains two new prompts:
- Custom registry prefix (prepended to all image names)
- Hardened images from dhi.io for Redis/PostgreSQL/Caddy (takes priority
over custom registry for those three)
Compared to PR #10: interactive prompts instead of hardcoded vars,
no sed-based compose file mutation, all 14 images covered (PR #10 missed
element-admin, element-call, lk-jwt-service, and all 3 bridges), and
standalone compose usage is preserved via :-default fallbacks.
SETUP.md and README.md document the feature including a note on
pull-through cache registries (Harbor/Artifactory/Nexus) that require
the full docker.io/ path prefix in image names.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
288 lines
8.5 KiB
YAML
288 lines
8.5 KiB
YAML
# Production docker-compose file for Matrix stack
|
|
# This configuration:
|
|
# - Uses real domain names with Let's Encrypt SSL certificates
|
|
# - Makes Authelia optional (use --profile authelia to enable)
|
|
# - Makes Caddy optional (use --profile single-machine for all-in-one deployment)
|
|
# - Configures all services for production use
|
|
#
|
|
# Deployment Modes:
|
|
# 1. Multi-machine (default): docker compose -f docker-compose.production.yml up -d
|
|
# - Runs: Synapse, MAS, Element, PostgreSQL only
|
|
# - Caddy and Authelia run on separate machines
|
|
# 2. Single-machine with Authelia: docker compose -f docker-compose.production.yml --profile single-machine --profile authelia up -d
|
|
# - Runs everything on one machine with Caddy + Authelia
|
|
# 3. Single-machine without Authelia: docker compose -f docker-compose.production.yml --profile single-machine up -d
|
|
# - Runs everything on one machine with Caddy, no Authelia
|
|
|
|
services:
|
|
# PostgreSQL Database
|
|
postgres:
|
|
image: ${POSTGRES_IMAGE:-postgres:16-alpine}
|
|
container_name: matrix-postgres
|
|
restart: unless-stopped
|
|
environment:
|
|
POSTGRES_DB: synapse
|
|
POSTGRES_USER: synapse
|
|
POSTGRES_PASSWORD: ${POSTGRES_PASSWORD}
|
|
POSTGRES_INITDB_ARGS: --encoding=UTF-8 --lc-collate=C --lc-ctype=C
|
|
volumes:
|
|
- ./postgres/data:/var/lib/postgresql/data
|
|
- ./postgres/init:/docker-entrypoint-initdb.d
|
|
networks:
|
|
- matrix-network
|
|
healthcheck:
|
|
test: ["CMD-SHELL", "pg_isready -U synapse"]
|
|
interval: 10s
|
|
timeout: 5s
|
|
retries: 5
|
|
|
|
# Matrix Synapse Server
|
|
synapse:
|
|
image: ${SYNAPSE_IMAGE:-matrixdotorg/synapse:latest}
|
|
container_name: matrix-synapse
|
|
restart: unless-stopped
|
|
environment:
|
|
SYNAPSE_CONFIG_PATH: /data/homeserver.yaml
|
|
volumes:
|
|
- ./synapse/data:/data
|
|
- ./bridges:/bridges:ro
|
|
- ./appservices:/appservices:ro
|
|
# Ports published to host for multi-machine deployment
|
|
ports:
|
|
- "8008:8008"
|
|
- "8448:8448" # Federation port
|
|
networks:
|
|
- matrix-network
|
|
depends_on:
|
|
postgres:
|
|
condition: service_healthy
|
|
healthcheck:
|
|
test: ["CMD", "curl", "-f", "http://localhost:8008/health"]
|
|
interval: 30s
|
|
timeout: 10s
|
|
retries: 3
|
|
|
|
# Element Web Client
|
|
element:
|
|
image: ${ELEMENT_IMAGE:-vectorim/element-web:latest}
|
|
container_name: matrix-element
|
|
restart: unless-stopped
|
|
volumes:
|
|
- ./element/config/config.json:/app/config.json:ro
|
|
# Port published to host for multi-machine deployment
|
|
ports:
|
|
- "8090:80" # Map container port 80 to host port 8090
|
|
networks:
|
|
- matrix-network
|
|
depends_on:
|
|
- synapse
|
|
|
|
# Element Admin - Web UI for managing users/rooms via MAS
|
|
element-admin:
|
|
image: ${ELEMENT_ADMIN_IMAGE:-oci.element.io/element-admin:latest}
|
|
container_name: matrix-element-admin
|
|
restart: unless-stopped
|
|
environment:
|
|
SERVER_NAME: "${MATRIX_DOMAIN}"
|
|
OIDC_CLIENT_ID: "01ADMN00000000000000000000"
|
|
OIDC_ISSUER: "https://${AUTH_DOMAIN}/"
|
|
# Port published to host for multi-machine deployment
|
|
ports:
|
|
- "8091:8080" # element-admin listens on 8080 (changed from 80 in recent versions)
|
|
networks:
|
|
- matrix-network
|
|
depends_on:
|
|
- synapse
|
|
- mas
|
|
|
|
# Redis for Authelia session storage
|
|
redis:
|
|
profiles:
|
|
- authelia # Only started when Authelia profile is active
|
|
image: ${REDIS_IMAGE:-redis:7-alpine}
|
|
container_name: matrix-redis
|
|
restart: unless-stopped
|
|
networks:
|
|
- matrix-network
|
|
healthcheck:
|
|
test: ["CMD", "redis-cli", "ping"]
|
|
interval: 10s
|
|
timeout: 5s
|
|
retries: 5
|
|
|
|
# Matrix Authentication Service (MAS)
|
|
mas:
|
|
image: ${MAS_IMAGE:-ghcr.io/element-hq/matrix-authentication-service:latest}
|
|
container_name: matrix-mas
|
|
restart: unless-stopped
|
|
environment:
|
|
MAS_CONFIG: /config/config.yaml
|
|
volumes:
|
|
- ./mas/config:/config:ro
|
|
- ./mas/data:/data
|
|
# Ports published to host for multi-machine deployment
|
|
ports:
|
|
- "8080:8080"
|
|
- "8081:8081"
|
|
networks:
|
|
- matrix-network
|
|
depends_on:
|
|
postgres:
|
|
condition: service_healthy
|
|
# Note: Redis/Authelia dependency removed - works with or without Authelia
|
|
# Healthcheck disabled: MAS uses distroless image without curl
|
|
# healthcheck:
|
|
# test: ["CMD", "curl", "-f", "http://localhost:8081/health"]
|
|
# interval: 30s
|
|
# timeout: 10s
|
|
# retries: 3
|
|
|
|
# mautrix-telegram Bridge
|
|
mautrix-telegram:
|
|
image: ${TELEGRAM_IMAGE:-dock.mau.dev/mautrix/telegram:latest}
|
|
container_name: matrix-bridge-telegram
|
|
restart: unless-stopped
|
|
volumes:
|
|
- ./bridges/telegram/config:/data
|
|
networks:
|
|
- matrix-network
|
|
depends_on:
|
|
synapse:
|
|
condition: service_healthy
|
|
|
|
# mautrix-whatsapp Bridge
|
|
mautrix-whatsapp:
|
|
image: ${WHATSAPP_IMAGE:-dock.mau.dev/mautrix/whatsapp:latest}
|
|
container_name: matrix-bridge-whatsapp
|
|
restart: unless-stopped
|
|
volumes:
|
|
- ./bridges/whatsapp/config:/data
|
|
networks:
|
|
- matrix-network
|
|
depends_on:
|
|
synapse:
|
|
condition: service_healthy
|
|
|
|
# mautrix-signal Bridge
|
|
mautrix-signal:
|
|
image: ${SIGNAL_IMAGE:-dock.mau.dev/mautrix/signal:latest}
|
|
container_name: matrix-bridge-signal
|
|
restart: unless-stopped
|
|
volumes:
|
|
- ./bridges/signal/config:/data
|
|
networks:
|
|
- matrix-network
|
|
depends_on:
|
|
synapse:
|
|
condition: service_healthy
|
|
|
|
# LiveKit SFU — media server for Element Call (Optional - use profile "element-call" to enable)
|
|
livekit:
|
|
profiles:
|
|
- element-call
|
|
image: ${LIVEKIT_IMAGE:-livekit/livekit-server:latest}
|
|
container_name: matrix-livekit
|
|
restart: unless-stopped
|
|
ports:
|
|
- "7880:7880"
|
|
- "7881:7881/tcp"
|
|
- "50100-50200:50100-50200/udp"
|
|
volumes:
|
|
- ./livekit/livekit.yaml:/livekit.yaml
|
|
command: --config /livekit.yaml
|
|
networks:
|
|
- matrix-network
|
|
|
|
# LiveKit JWT Service — issues LiveKit tokens to authenticated Matrix users
|
|
lk-jwt-service:
|
|
profiles:
|
|
- element-call
|
|
image: ${LK_JWT_IMAGE:-ghcr.io/element-hq/lk-jwt-service:latest}
|
|
container_name: matrix-lk-jwt
|
|
restart: unless-stopped
|
|
ports:
|
|
- "8082:8080"
|
|
environment:
|
|
- LIVEKIT_URL=wss://${RTC_DOMAIN}/livekit/sfu
|
|
- LIVEKIT_KEY=livekit-key
|
|
- LIVEKIT_SECRET=${LIVEKIT_SECRET}
|
|
- LIVEKIT_FULL_ACCESS_HOMESERVERS=${MATRIX_DOMAIN}
|
|
networks:
|
|
- matrix-network
|
|
depends_on:
|
|
- livekit
|
|
|
|
# Element Call frontend — self-hosted web app for video/voice calls
|
|
# Reads livekit_service_url from .well-known/matrix/client (rtc_foci) automatically.
|
|
# No extra configuration needed: homeserver details are passed as URL params by Element Web.
|
|
element-call:
|
|
profiles:
|
|
- element-call
|
|
image: ${ELEMENT_CALL_IMAGE:-ghcr.io/element-hq/element-call:latest}
|
|
container_name: matrix-element-call
|
|
restart: unless-stopped
|
|
ports:
|
|
- "8083:8080"
|
|
networks:
|
|
- matrix-network
|
|
|
|
# Authelia SSO (Optional - use profile "authelia" to enable)
|
|
authelia:
|
|
profiles:
|
|
- authelia # Only started when Authelia profile is active
|
|
image: ${AUTHELIA_IMAGE:-authelia/authelia:latest}
|
|
container_name: matrix-authelia
|
|
restart: unless-stopped
|
|
environment:
|
|
TZ: ${TZ:-UTC}
|
|
AUTHELIA_SESSION_SECRET: ${AUTHELIA_SESSION_SECRET}
|
|
AUTHELIA_STORAGE_ENCRYPTION_KEY: ${AUTHELIA_STORAGE_ENCRYPTION_KEY}
|
|
AUTHELIA_JWT_SECRET: ${AUTHELIA_JWT_SECRET}
|
|
POSTGRES_PASSWORD: ${POSTGRES_PASSWORD}
|
|
volumes:
|
|
- ./authelia/config:/config
|
|
# Accessed via Caddy
|
|
expose:
|
|
- "9091"
|
|
networks:
|
|
- matrix-network
|
|
depends_on:
|
|
postgres:
|
|
condition: service_healthy
|
|
redis:
|
|
condition: service_healthy
|
|
|
|
# Caddy Reverse Proxy (HTTPS termination with Let's Encrypt)
|
|
# For multi-machine deployments: Don't use this profile (Caddy runs separately)
|
|
# For single-machine deployments: Use --profile single-machine
|
|
caddy:
|
|
profiles:
|
|
- single-machine # Only started for single-machine deployments
|
|
image: ${CADDY_IMAGE:-caddy:2-alpine}
|
|
container_name: matrix-caddy
|
|
restart: unless-stopped
|
|
ports:
|
|
- "443:443"
|
|
- "80:80"
|
|
- "2019:2019" # Admin API
|
|
volumes:
|
|
- ./caddy/Caddyfile:/etc/caddy/Caddyfile:ro
|
|
- ./caddy/data:/data
|
|
- ./caddy/config:/config
|
|
networks:
|
|
- matrix-network
|
|
depends_on:
|
|
- synapse
|
|
- element
|
|
- mas
|
|
# Note: Authelia dependency removed - works with or without Authelia
|
|
|
|
networks:
|
|
matrix-network:
|
|
driver: bridge
|
|
|
|
volumes:
|
|
postgres-data:
|
|
synapse-data:
|
|
mas-data:
|