181 lines
7.9 KiB
YAML
181 lines
7.9 KiB
YAML
# ============================================================
|
|
# Matrix Self-Hosted Stack
|
|
# Services: Continuwuity, Coturn, LiveKit, lk-jwt-service
|
|
#
|
|
# Domains (replace throughout):
|
|
# matrix.example.com — homeserver
|
|
# livekit.example.com — LiveKit + JWT service
|
|
#
|
|
# Before starting:
|
|
# 1. Fill in all YOUR_* placeholders
|
|
# 2. Set your email for Let's Encrypt
|
|
# 3. Run: docker network create proxy
|
|
# 4. Then: docker compose up -d
|
|
# 5. Grab admin password: docker compose logs homeserver | grep "Created user"
|
|
# 6. Remove the --execute flag from homeserver command after first boot
|
|
# ============================================================
|
|
|
|
services:
|
|
|
|
# ----------------------------------------------------------
|
|
# Traefik — Reverse Proxy & TLS termination
|
|
# Handles HTTPS for matrix.example.com and livekit.example.com
|
|
# Does NOT handle coturn (raw UDP/TCP, not HTTP)
|
|
# ----------------------------------------------------------
|
|
traefik:
|
|
image: traefik:latest
|
|
container_name: traefik
|
|
restart: unless-stopped
|
|
ports:
|
|
- "80:80"
|
|
- "443:443"
|
|
volumes:
|
|
- /var/run/docker.sock:/var/run/docker.sock:ro
|
|
- acme:/etc/traefik/acme
|
|
environment:
|
|
TRAEFIK_LOG_LEVEL: INFO
|
|
TRAEFIK_ENTRYPOINTS_WEB_ADDRESS: ":80"
|
|
TRAEFIK_ENTRYPOINTS_WEB_HTTP_REDIRECTIONS_ENTRYPOINT_TO: websecure
|
|
TRAEFIK_ENTRYPOINTS_WEBSECURE_ADDRESS: ":443"
|
|
TRAEFIK_ENTRYPOINTS_WEBSECURE_HTTP_TLS_CERTRESOLVER: letsencrypt
|
|
# Allow encoded characters needed by Matrix
|
|
TRAEFIK_ENTRYPOINTS_WEBSECURE_HTTP_ENCODEDCHARACTERS_ALLOWENCODEDSLASH: "true"
|
|
TRAEFIK_ENTRYPOINTS_WEBSECURE_HTTP_ENCODEDCHARACTERS_ALLOWENCODEDHASH: "true"
|
|
TRAEFIK_CERTIFICATESRESOLVERS_LETSENCRYPT: "true"
|
|
TRAEFIK_CERTIFICATESRESOLVERS_LETSENCRYPT_ACME_EMAIL: "you@example.com" # EDIT THIS
|
|
TRAEFIK_CERTIFICATESRESOLVERS_LETSENCRYPT_ACME_KEYTYPE: EC384
|
|
TRAEFIK_CERTIFICATESRESOLVERS_LETSENCRYPT_ACME_HTTPCHALLENGE: "true"
|
|
TRAEFIK_CERTIFICATESRESOLVERS_LETSENCRYPT_ACME_HTTPCHALLENGE_ENTRYPOINT: web
|
|
TRAEFIK_CERTIFICATESRESOLVERS_LETSENCRYPT_ACME_STORAGE: /etc/traefik/acme/acme.json
|
|
TRAEFIK_PROVIDERS_DOCKER: "true"
|
|
TRAEFIK_PROVIDERS_DOCKER_EXPOSEDBYDEFAULT: "false"
|
|
networks:
|
|
- proxy
|
|
|
|
# ----------------------------------------------------------
|
|
# Continuwuity — Matrix Homeserver
|
|
# ----------------------------------------------------------
|
|
homeserver:
|
|
image: forgejo.ellis.link/continuwuation/continuwuity:latest
|
|
container_name: continuwuity
|
|
restart: unless-stopped
|
|
# Remove the --execute flag after first boot once you have your admin password
|
|
command: /sbin/conduwuit --execute "users create-user admin"
|
|
depends_on:
|
|
- traefik
|
|
volumes:
|
|
- db:/var/lib/continuwuity
|
|
- ./continuwuity.toml:/etc/continuwuity.toml:ro
|
|
# Use host DNS to avoid Docker's resolver causing federation issues
|
|
- /etc/resolv.conf:/etc/resolv.conf:ro
|
|
networks:
|
|
- proxy
|
|
labels:
|
|
- "traefik.enable=true"
|
|
# Serve matrix.example.com AND well-known paths on example.com for delegation
|
|
- "traefik.http.routers.continuwuity.rule=(Host(`matrix.example.com`) || (Host(`example.com`) && PathPrefix(`/.well-known/matrix`)))"
|
|
- "traefik.http.routers.continuwuity.entrypoints=websecure"
|
|
- "traefik.http.routers.continuwuity.tls.certresolver=letsencrypt"
|
|
- "traefik.http.services.continuwuity.loadbalancer.server.port=6167"
|
|
environment:
|
|
CONTINUWUITY_SERVER_NAME: "example.com" # Your Matrix ID domain (@user:example.com)
|
|
CONTINUWUITY_DATABASE_PATH: /var/lib/continuwuity
|
|
CONTINUWUITY_ADDRESS: 0.0.0.0
|
|
CONTINUWUITY_PORT: 6167
|
|
CONTINUWUITY_MAX_REQUEST_SIZE: "20000000" # ~20 MB
|
|
CONTINUWUITY_ALLOW_REGISTRATION: "false" # Enable with a token for invite-only
|
|
CONTINUWUITY_REGISTRATION_TOKEN: "YOUR_INVITE_TOKEN" # EDIT THIS
|
|
CONTINUWUITY_ALLOW_FEDERATION: "true"
|
|
CONTINUWUITY_ALLOW_CHECK_FOR_UPDATES: "true"
|
|
CONTINUWUITY_TRUSTED_SERVERS: '["matrix.org"]'
|
|
CONTINUWUITY_CONFIG: /etc/continuwuity.toml
|
|
# Well-known delegation so clients and servers find your homeserver
|
|
CONTINUWUITY_WELL_KNOWN: |
|
|
{
|
|
client=https://matrix.example.com,
|
|
server=matrix.example.com:443
|
|
}
|
|
ulimits:
|
|
nofile:
|
|
soft: 1048567
|
|
hard: 1048567
|
|
|
|
# ----------------------------------------------------------
|
|
# Coturn — TURN/STUN server for legacy 1:1 calls
|
|
# Uses host networking — Traefik does NOT proxy this.
|
|
# Reachable directly on the host IP via UDP/TCP ports 3478/5349
|
|
# and the relay port range 50201-65535.
|
|
# ----------------------------------------------------------
|
|
coturn:
|
|
image: coturn/coturn:latest
|
|
container_name: coturn
|
|
restart: unless-stopped
|
|
network_mode: host
|
|
volumes:
|
|
- ./coturn.conf:/etc/coturn/turnserver.conf:ro
|
|
|
|
# ----------------------------------------------------------
|
|
# LiveKit — Media server for MatrixRTC / Element Call
|
|
# Also uses host networking for RTC performance.
|
|
# Traefik proxies HTTP/WS on port 7880 via livekit.example.com.
|
|
# ----------------------------------------------------------
|
|
livekit:
|
|
image: livekit/livekit-server:latest
|
|
container_name: livekit
|
|
restart: unless-stopped
|
|
command: --config /etc/livekit.yaml
|
|
network_mode: host
|
|
volumes:
|
|
- ./livekit.yaml:/etc/livekit.yaml:ro
|
|
# Note: with network_mode: host, Traefik cannot directly label this container.
|
|
# The livekit.example.com routing is handled via the lk-jwt-service container
|
|
# labels, with LiveKit itself accessed at 127.0.0.1:7880 from that container's
|
|
# perspective. See lk-jwt-service labels below.
|
|
|
|
# ----------------------------------------------------------
|
|
# lk-jwt-service — Issues JWT tokens for LiveKit
|
|
# Matrix users authenticate here before joining calls.
|
|
# Traefik routes livekit.example.com here, with path-based
|
|
# splitting: /sfu/get, /healthz, /get_token go to this service;
|
|
# everything else is reverse-proxied onward to LiveKit on :7880.
|
|
# ----------------------------------------------------------
|
|
lk-jwt-service:
|
|
image: ghcr.io/element-hq/lk-jwt-service:latest
|
|
container_name: lk-jwt-service
|
|
restart: unless-stopped
|
|
environment:
|
|
- LIVEKIT_JWT_BIND=:8081
|
|
- LIVEKIT_URL=wss://livekit.example.com # EDIT: your LiveKit domain
|
|
- LIVEKIT_KEY=YOUR_LK_KEY # EDIT: from generate-keys
|
|
- LIVEKIT_SECRET=YOUR_LK_SECRET # EDIT: from generate-keys
|
|
- LIVEKIT_FULL_ACCESS_HOMESERVERS=example.com # EDIT: your Matrix domain
|
|
ports:
|
|
- "127.0.0.1:8081:8081" # Only bind to localhost; Traefik reaches it here
|
|
networks:
|
|
- proxy
|
|
labels:
|
|
- "traefik.enable=true"
|
|
# High-priority rule: JWT paths go to lk-jwt-service
|
|
- "traefik.http.routers.livekit-jwt.rule=Host(`livekit.example.com`) && (PathPrefix(`/sfu/get`) || PathPrefix(`/healthz`) || PathPrefix(`/get_token`))"
|
|
- "traefik.http.routers.livekit-jwt.entrypoints=websecure"
|
|
- "traefik.http.routers.livekit-jwt.tls.certresolver=letsencrypt"
|
|
- "traefik.http.routers.livekit-jwt.priority=10"
|
|
- "traefik.http.services.livekit-jwt.loadbalancer.server.port=8081"
|
|
# Low-priority rule: everything else on livekit.example.com goes to LiveKit itself
|
|
- "traefik.http.routers.livekit.rule=Host(`livekit.example.com`)"
|
|
- "traefik.http.routers.livekit.entrypoints=websecure"
|
|
- "traefik.http.routers.livekit.tls.certresolver=letsencrypt"
|
|
- "traefik.http.routers.livekit.priority=1"
|
|
- "traefik.http.services.livekit.loadbalancer.server.url=http://127.0.0.1:7880"
|
|
|
|
# ----------------------------------------------------------
|
|
# Volumes & Networks
|
|
# ----------------------------------------------------------
|
|
volumes:
|
|
db:
|
|
acme:
|
|
|
|
networks:
|
|
proxy:
|
|
external: true
|