digitalboard.core/roles/ess_pro_compose/templates/compose.yml.j2
Tobias Wüst 32eca6b923 feat(ess-pro/compose): deploy Element Server Suite Pro via Compose
initial commit of the converted role from helm charts for qubernetis to compose ansible role
2026-06-04 10:52:05 +02:00

304 lines
13 KiB
Django/Jinja

# {{ ansible_managed }}
# ESS Pro v{{ ess_chart_version }} on docker compose — rendered by ess_pro_compose.
# Topology mirrors the Helm chart: HAProxy fronts all Synapse traffic,
# synapse-main is the Python homeserver, synapse-fed-reader is the Rust Pro
# worker handling federation reads, MAS handles all auth, LiveKit + lk-jwt
# serve Element Call.
name: {{ ess_compose_project_name }}
networks:
{{ ess_compose_traefik_network }}:
external: true
{{ ess_compose_internal_network }}:
driver: bridge
volumes:
postgres_data:
synapse_media:
services:
# ===========================================================================
# Data plane
# ===========================================================================
postgres:
image: {{ ess_images.postgres }}
container_name: postgres
restart: unless-stopped
networks: [ {{ ess_compose_internal_network }} ]
environment:
LC_COLLATE: "C"
LC_CTYPE: "C"
PGDATA: /var/lib/postgresql/data/pgdata
POSTGRES_INITDB_ARGS: "-E UTF8"
POSTGRES_PASSWORD_FILE: /secrets/ess-generated/POSTGRES_ADMIN_PASSWORD
command:
- postgres
- "-c"
- "max_connections={{ ess_postgres_max_connections }}"
- "-c"
- "shared_buffers={{ ess_postgres_shared_buffers }}"
- "-c"
- "effective_cache_size={{ ess_postgres_effective_cache_size }}"
volumes:
- postgres_data:/var/lib/postgresql/data
- {{ ess_compose_secrets_dir }}:/secrets/ess-generated:ro
- {{ ess_compose_conf_dir }}/postgres/configure-dbs.sh:/docker-entrypoint-initdb.d/init-ess-dbs.sh:ro
healthcheck:
test: ["CMD-SHELL", "pg_isready -U postgres"]
interval: 10s
timeout: 5s
retries: 10
redis:
image: {{ ess_images.redis }}
container_name: redis
restart: unless-stopped
networks: [ {{ ess_compose_internal_network }} ]
command: ["/usr/local/etc/redis/redis.conf"]
volumes:
- {{ ess_compose_conf_dir }}/redis/redis.conf:/usr/local/etc/redis/redis.conf:ro
healthcheck:
test: ["CMD", "redis-cli", "ping"]
interval: 10s
timeout: 3s
retries: 5
# ===========================================================================
# Synapse (Python main + Rust federation-reader worker)
# ===========================================================================
synapse-main:
image: {{ ess_images.synapse }}
container_name: synapse-main
restart: unless-stopped
depends_on:
postgres: { condition: service_healthy }
redis: { condition: service_healthy }
networks: [ {{ ess_compose_internal_network }} ]
command: ["python3", "-m", "synapse.app.homeserver", "-c", "/conf/homeserver.yaml"]
volumes:
- {{ ess_compose_conf_dir }}/synapse/homeserver.yaml:/conf/homeserver.yaml:ro
- {{ ess_compose_conf_dir }}/synapse/log_config.yaml:/conf/log_config.yaml:ro
- {{ ess_compose_secrets_dir }}:/secrets/ess-generated:ro
- synapse_media:/media
healthcheck:
test: ["CMD", "curl", "-fsS", "http://localhost:8080/health"]
interval: 10s
timeout: 5s
retries: 30
start_period: 60s
{% for i in range(ess_synapse_fed_reader_replicas | int) %}
synapse-fed-reader-{{ i }}:
image: {{ ess_images.synapse_pro_worker }}
container_name: synapse-fed-reader-{{ i }}
restart: unless-stopped
depends_on:
synapse-main: { condition: service_healthy }
networks: [ {{ ess_compose_internal_network }} ]
environment:
APP_CONFIG_FILEPATH: /conf/federation-reader.yaml
volumes:
- {{ ess_compose_conf_dir }}/synapse/federation-reader.yaml:/conf/federation-reader.yaml:ro
- {{ ess_compose_secrets_dir }}:/secrets/ess-generated:ro
{% endfor %}
# ===========================================================================
# Matrix Authentication Service (4 listeners)
# ===========================================================================
mas:
image: {{ ess_images.mas }}
container_name: mas
restart: unless-stopped
depends_on:
postgres: { condition: service_healthy }
networks:
- {{ ess_compose_internal_network }}
- {{ ess_compose_traefik_network }}
environment:
MAS_CONFIG: /conf/mas-config.yaml
command: ["server", "--no-migrate"]
volumes:
- {{ ess_compose_conf_dir }}/mas/config.yaml:/conf/mas-config.yaml:ro
- {{ ess_compose_secrets_dir }}:/secrets/ess-generated:ro
healthcheck:
test: ["CMD", "curl", "-fsS", "http://localhost:8081/health"]
interval: 10s
timeout: 5s
retries: 20
start_period: 30s
labels:
- "traefik.enable=true"
- "traefik.docker.network={{ ess_compose_traefik_network }}"
- "traefik.http.routers.ess-mas.rule=Host(`{{ ess_hostnames.mas }}`)"
- "traefik.http.routers.ess-mas.entrypoints={{ ess_compose_traefik_entrypoint }}"
- "traefik.http.routers.ess-mas.tls=true"
{% if ess_compose_traefik_certresolver | length > 0 %}
- "traefik.http.routers.ess-mas.tls.certresolver={{ ess_compose_traefik_certresolver }}"
{% endif %}
- "traefik.http.services.ess-mas.loadbalancer.server.port=8080"
# MAS root listener (port 8082) is mounted as a separate Traefik router so
# /.well-known/openid-configuration on the apex of the mas host is reachable.
# We attach a second router on the same service via a path rule.
# ===========================================================================
# HAProxy — fronts all Synapse + well-known traffic
# ===========================================================================
haproxy:
image: {{ ess_images.haproxy }}
container_name: haproxy
restart: unless-stopped
depends_on:
synapse-main: { condition: service_healthy }
networks:
- {{ ess_compose_internal_network }}
- {{ ess_compose_traefik_network }}
command: ["-f", "/usr/local/etc/haproxy/haproxy.cfg", "-dW"]
volumes:
- {{ ess_compose_conf_dir }}/haproxy/haproxy.cfg:/usr/local/etc/haproxy/haproxy.cfg:ro
- {{ ess_compose_conf_dir }}/haproxy/path_map_file:/synapse/path_map_file:ro
- {{ ess_compose_conf_dir }}/haproxy/path_map_file_get:/synapse/path_map_file_get:ro
- {{ ess_compose_conf_dir }}/haproxy/429.http:/synapse/429.http:ro
- {{ ess_compose_conf_dir }}/haproxy/admin-allow-ips.lst:/synapse/admin-allow-ips.lst:ro
- {{ ess_compose_conf_dir }}/haproxy/well-known/server:/well-known/server:ro
- {{ ess_compose_conf_dir }}/haproxy/well-known/client:/well-known/client:ro
- {{ ess_compose_conf_dir }}/haproxy/well-known/support:/well-known/support:ro
- {{ ess_compose_conf_dir }}/haproxy/well-known/element.json:/well-known/element.json:ro
healthcheck:
test: ["CMD", "wget", "-q", "-O-", "http://localhost:8406/synapse_ready"]
interval: 15s
timeout: 5s
retries: 20
start_period: 90s
labels:
# matrix.<server> -> HAProxy frontend synapse-http-in (port 8008)
- "traefik.enable=true"
- "traefik.docker.network={{ ess_compose_traefik_network }}"
- "traefik.http.routers.ess-synapse.rule=Host(`{{ ess_hostnames.synapse }}`)"
- "traefik.http.routers.ess-synapse.entrypoints={{ ess_compose_traefik_entrypoint }}"
- "traefik.http.routers.ess-synapse.tls=true"
{% if ess_compose_traefik_certresolver | length > 0 %}
- "traefik.http.routers.ess-synapse.tls.certresolver={{ ess_compose_traefik_certresolver }}"
{% endif %}
- "traefik.http.routers.ess-synapse.service=ess-synapse"
- "traefik.http.services.ess-synapse.loadbalancer.server.port=8008"
# <server>/.well-known/matrix -> HAProxy well-known-in (port 8010)
- "traefik.http.routers.ess-wellknown.rule=Host(`{{ ess_server_name }}`) && PathPrefix(`/.well-known/matrix`)"
- "traefik.http.routers.ess-wellknown.entrypoints={{ ess_compose_traefik_entrypoint }}"
- "traefik.http.routers.ess-wellknown.tls=true"
{% if ess_compose_traefik_certresolver | length > 0 %}
- "traefik.http.routers.ess-wellknown.tls.certresolver={{ ess_compose_traefik_certresolver }}"
{% endif %}
- "traefik.http.routers.ess-wellknown.service=ess-wellknown"
- "traefik.http.services.ess-wellknown.loadbalancer.server.port=8010"
# ===========================================================================
# Element Web (browser client)
# ===========================================================================
element-web:
image: {{ ess_images.element_web }}
container_name: element-web
restart: unless-stopped
networks: [ {{ ess_compose_traefik_network }} ]
volumes:
- {{ ess_compose_conf_dir }}/element-web/config.json:/app/config.json:ro
labels:
- "traefik.enable=true"
- "traefik.docker.network={{ ess_compose_traefik_network }}"
- "traefik.http.routers.ess-element-web.rule=Host(`{{ ess_hostnames.element_web }}`)"
- "traefik.http.routers.ess-element-web.entrypoints={{ ess_compose_traefik_entrypoint }}"
- "traefik.http.routers.ess-element-web.tls=true"
{% if ess_compose_traefik_certresolver | length > 0 %}
- "traefik.http.routers.ess-element-web.tls.certresolver={{ ess_compose_traefik_certresolver }}"
{% endif %}
- "traefik.http.services.ess-element-web.loadbalancer.server.port=8080"
# ===========================================================================
# Element Admin (admin panel)
# ===========================================================================
element-admin:
image: {{ ess_images.element_admin }}
container_name: element-admin
restart: unless-stopped
networks: [ {{ ess_compose_traefik_network }} ]
environment:
SERVER_NAME: "{{ ess_server_name }}"
labels:
- "traefik.enable=true"
- "traefik.docker.network={{ ess_compose_traefik_network }}"
- "traefik.http.routers.ess-element-admin.rule=Host(`{{ ess_hostnames.element_admin }}`)"
- "traefik.http.routers.ess-element-admin.entrypoints={{ ess_compose_traefik_entrypoint }}"
- "traefik.http.routers.ess-element-admin.tls=true"
{% if ess_compose_traefik_certresolver | length > 0 %}
- "traefik.http.routers.ess-element-admin.tls.certresolver={{ ess_compose_traefik_certresolver }}"
{% endif %}
- "traefik.http.services.ess-element-admin.loadbalancer.server.port=8080"
# ===========================================================================
# Matrix RTC / Element Call (LiveKit SFU + lk-jwt)
# ===========================================================================
matrix-rtc-sfu:
image: {{ ess_images.livekit }}
container_name: matrix-rtc-sfu
restart: unless-stopped
networks:
- {{ ess_compose_internal_network }}
- {{ ess_compose_traefik_network }}
command: ["--config", "/conf/sfu-config.yaml"]
volumes:
- {{ ess_compose_conf_dir }}/sfu/config.yaml:/conf/sfu-config.yaml:ro
# WebRTC media ports — DMZ firewall must NAT-forward these to this host.
ports:
- "{{ ess_rtc_tcp_port }}:{{ ess_rtc_tcp_port }}/tcp"
- "{{ ess_rtc_udp_port }}:{{ ess_rtc_udp_port }}/udp"
labels:
- "traefik.enable=true"
- "traefik.docker.network={{ ess_compose_traefik_network }}"
- "traefik.http.routers.ess-matrix-rtc.rule=Host(`{{ ess_hostnames.matrix_rtc }}`)"
- "traefik.http.routers.ess-matrix-rtc.entrypoints={{ ess_compose_traefik_entrypoint }}"
- "traefik.http.routers.ess-matrix-rtc.tls=true"
{% if ess_compose_traefik_certresolver | length > 0 %}
- "traefik.http.routers.ess-matrix-rtc.tls.certresolver={{ ess_compose_traefik_certresolver }}"
{% endif %}
- "traefik.http.routers.ess-matrix-rtc.service=ess-matrix-rtc"
- "traefik.http.services.ess-matrix-rtc.loadbalancer.server.port=7880"
matrix-rtc-authorisation:
image: {{ ess_images.lk_jwt }}
container_name: matrix-rtc-authorisation
restart: unless-stopped
depends_on:
matrix-rtc-sfu: { condition: service_started }
networks:
- {{ ess_compose_internal_network }}
- {{ ess_compose_traefik_network }}
environment:
LIVEKIT_URL: "wss://{{ ess_hostnames.matrix_rtc }}"
LIVEKIT_KEY: "{{ ess_livekit_key }}"
LIVEKIT_SECRET_FROM_FILE: /secrets/ess-generated/ELEMENT_CALL_LIVEKIT_SECRET
LIVEKIT_FULL_ACCESS_HOMESERVERS: "{{ ess_server_name }}"
volumes:
- {{ ess_compose_secrets_dir }}:/secrets/ess-generated:ro
labels:
# /sfu/get is the JWT token endpoint Element Call hits to join calls.
# It lives on the same host as the SFU but on a different backend.
- "traefik.enable=true"
- "traefik.docker.network={{ ess_compose_traefik_network }}"
- "traefik.http.routers.ess-matrix-rtc-auth.rule=Host(`{{ ess_hostnames.matrix_rtc }}`) && PathPrefix(`/sfu/get`)"
- "traefik.http.routers.ess-matrix-rtc-auth.entrypoints={{ ess_compose_traefik_entrypoint }}"
- "traefik.http.routers.ess-matrix-rtc-auth.tls=true"
- "traefik.http.routers.ess-matrix-rtc-auth.priority=200"
{% if ess_compose_traefik_certresolver | length > 0 %}
- "traefik.http.routers.ess-matrix-rtc-auth.tls.certresolver={{ ess_compose_traefik_certresolver }}"
{% endif %}
- "traefik.http.routers.ess-matrix-rtc-auth.service=ess-matrix-rtc-auth"
- "traefik.http.services.ess-matrix-rtc-auth.loadbalancer.server.port=8080"