# ============================================== # Matrix (Synapse + Element) - Sample Swarm Deployment # ============================================== # # Standalone Matrix homeserver deployment for use with Mosaic Stack. # This is SEPARATE infrastructure — not part of the Mosaic Stack itself. # Mosaic connects to it via MATRIX_HOMESERVER_URL environment variable. # # Also serves: personal communications, GoToSocial bridges, other projects. # # Usage (Docker Swarm via Portainer): # 1. Create a new stack in Portainer # 2. Paste this file or point to the repo # 3. Set environment variables in Portainer's env var section # 4. Deploy the stack # # Usage (Docker Swarm CLI): # 1. cp docker-compose.sample.matrix.env .env # 2. nano .env # Configure # 3. docker stack deploy -c docker-compose.sample.matrix.yml matrix # # Post-Deploy Setup: # 1. Generate Synapse config (first run only): # docker exec python -m synapse.app.homeserver \ # --server-name ${MATRIX_DOMAIN} --report-stats no \ # --generate-config --config-path /data/homeserver.yaml # # 2. Create admin account: # docker exec -it register_new_matrix_user \ # -u admin -a -c /data/homeserver.yaml http://localhost:8008 # # 3. Create Mosaic bot account: # docker exec -it register_new_matrix_user \ # -u mosaic-bot -c /data/homeserver.yaml http://localhost:8008 # # 4. Generate bot access token: # curl -X POST http://localhost:8008/_matrix/client/v3/login \ # -d '{"type":"m.login.password","user":"mosaic-bot","password":""}' # # 5. Set MATRIX_ACCESS_TOKEN in Mosaic Stack .env # # Required Environment Variables: # MATRIX_DOMAIN=matrix.example.com # Synapse server name (permanent!) # ELEMENT_DOMAIN=chat.example.com # Element Web domain # POSTGRES_PASSWORD= # Synapse database password # # Optional Environment Variables: # SYNAPSE_IMAGE_TAG=latest # Synapse version # ELEMENT_IMAGE_TAG=latest # Element Web version # POSTGRES_IMAGE_TAG=16-alpine # PostgreSQL version # TRAEFIK_ENTRYPOINT=websecure # Traefik entrypoint name # TRAEFIK_CERTRESOLVER=letsencrypt # Traefik cert resolver # TRAEFIK_DOCKER_NETWORK=traefik-public # Traefik network name # SYNAPSE_ENABLE_REGISTRATION=false # Public registration # SYNAPSE_REPORT_STATS=no # Anonymous stats reporting # SYNAPSE_MAX_UPLOAD_SIZE=50M # Max file upload size # # Connecting to Mosaic Stack: # Add to your Mosaic Stack .env: # MATRIX_HOMESERVER_URL=http://synapse:8008 (if same Docker network) # MATRIX_HOMESERVER_URL=https://matrix.example.com (if external) # MATRIX_ACCESS_TOKEN= # MATRIX_BOT_USER_ID=@mosaic-bot:matrix.example.com # # ============================================== services: # ====================== # Synapse (Matrix Homeserver) # ====================== synapse: image: matrixdotorg/synapse:${SYNAPSE_IMAGE_TAG:-latest} environment: SYNAPSE_SERVER_NAME: ${MATRIX_DOMAIN} SYNAPSE_REPORT_STATS: ${SYNAPSE_REPORT_STATS:-no} SYNAPSE_CONFIG_DIR: /data SYNAPSE_DATA_DIR: /data SYNAPSE_LOG_LEVEL: ${SYNAPSE_LOG_LEVEL:-WARNING} # Database connection (external PostgreSQL or bundled) POSTGRES_HOST: synapse-postgres POSTGRES_PORT: 5432 POSTGRES_DB: ${SYNAPSE_POSTGRES_DB:-synapse} POSTGRES_USER: ${SYNAPSE_POSTGRES_USER:-synapse} POSTGRES_PASSWORD: ${POSTGRES_PASSWORD} volumes: - synapse-data:/data - synapse-media:/data/media_store depends_on: synapse-postgres: condition: service_healthy healthcheck: test: ["CMD-SHELL", "curl -fSs http://localhost:8008/health || exit 1"] interval: 30s timeout: 10s retries: 3 start_period: 30s networks: - internal - traefik-public deploy: restart_policy: condition: on-failure delay: 10s labels: - "traefik.enable=true" - "traefik.http.routers.matrix.rule=Host(`${MATRIX_DOMAIN}`)" - "traefik.http.routers.matrix.entrypoints=${TRAEFIK_ENTRYPOINT:-websecure}" - "traefik.http.routers.matrix.tls=${TRAEFIK_TLS_ENABLED:-true}" - "traefik.http.routers.matrix.tls.certresolver=${TRAEFIK_CERTRESOLVER:-}" - "traefik.http.services.matrix.loadbalancer.server.port=8008" - "traefik.docker.network=${TRAEFIK_DOCKER_NETWORK:-traefik-public}" # Well-known delegation (optional — for .well-known/matrix/server) # - "traefik.http.routers.matrix-wellknown.rule=Host(`${MATRIX_DOMAIN}`) && PathPrefix(`/.well-known/matrix`)" # ====================== # Element Web (Matrix Client) # ====================== element-web: image: vectorim/element-web:${ELEMENT_IMAGE_TAG:-latest} volumes: - element-config:/app/config healthcheck: test: ["CMD-SHELL", "wget --no-verbose --tries=1 --spider http://localhost:80 || exit 1"] interval: 30s timeout: 5s retries: 3 networks: - internal - traefik-public deploy: restart_policy: condition: on-failure labels: - "traefik.enable=true" - "traefik.http.routers.element.rule=Host(`${ELEMENT_DOMAIN}`)" - "traefik.http.routers.element.entrypoints=${TRAEFIK_ENTRYPOINT:-websecure}" - "traefik.http.routers.element.tls=${TRAEFIK_TLS_ENABLED:-true}" - "traefik.http.routers.element.tls.certresolver=${TRAEFIK_CERTRESOLVER:-}" - "traefik.http.services.element.loadbalancer.server.port=80" - "traefik.docker.network=${TRAEFIK_DOCKER_NETWORK:-traefik-public}" # ====================== # PostgreSQL (Synapse Database) # ====================== # Separate from Mosaic's PostgreSQL — Synapse manages its own schema. # If you prefer a shared PostgreSQL instance, remove this service and # point POSTGRES_HOST to your existing PostgreSQL with a separate database. synapse-postgres: image: postgres:${POSTGRES_IMAGE_TAG:-16-alpine} environment: POSTGRES_DB: ${SYNAPSE_POSTGRES_DB:-synapse} POSTGRES_USER: ${SYNAPSE_POSTGRES_USER:-synapse} POSTGRES_PASSWORD: ${POSTGRES_PASSWORD} POSTGRES_INITDB_ARGS: "--encoding=UTF-8 --lc-collate=C --lc-ctype=C" volumes: - synapse-postgres-data:/var/lib/postgresql/data healthcheck: test: [ "CMD-SHELL", "pg_isready -U ${SYNAPSE_POSTGRES_USER:-synapse} -d ${SYNAPSE_POSTGRES_DB:-synapse}", ] interval: 10s timeout: 5s retries: 5 networks: - internal deploy: restart_policy: condition: on-failure # ====================== # coturn (TURN/STUN for VoIP) - Optional # ====================== # Uncomment if you need voice/video calls through NAT. # Requires additional DNS and port configuration. # # coturn: # image: coturn/coturn:latest # environment: # TURN_REALM: ${MATRIX_DOMAIN} # TURN_SECRET: ${COTURN_SECRET} # ports: # - "3478:3478/tcp" # - "3478:3478/udp" # - "5349:5349/tcp" # - "5349:5349/udp" # - "49152-49200:49152-49200/udp" # networks: # - internal # deploy: # restart_policy: # condition: on-failure volumes: synapse-data: synapse-media: synapse-postgres-data: element-config: networks: internal: driver: overlay traefik-public: external: true name: ${TRAEFIK_DOCKER_NETWORK:-traefik-public}