docker.recipes

Element + Matrix Homeserver

advanced

Decentralized communication with Matrix Synapse and Element web client.

Overview

Matrix Synapse is the reference implementation of the Matrix protocol, an open standard for secure, decentralized real-time communication. Developed by the Matrix.org Foundation, Synapse serves as a homeserver that enables federation with other Matrix servers worldwide, creating a distributed network where users maintain control over their data while communicating across server boundaries. This architecture eliminates single points of failure and vendor lock-in common in centralized messaging platforms. This stack combines Synapse with Element Web (formerly Riot), the flagship Matrix client that provides a modern web interface for messaging, voice, and video calls. PostgreSQL serves as the primary database for storing room state, user data, and message history with ACID compliance ensuring data integrity during concurrent operations. Redis handles session caching and worker coordination for horizontal scaling, while Coturn provides TURN/STUN services essential for voice and video calls behind NAT firewalls. Nginx acts as a reverse proxy, handling SSL termination and routing federation traffic. Organizations seeking to deploy private, self-hosted communication infrastructure will find this stack particularly valuable. Unlike centralized services, Matrix federation allows internal users to communicate with external Matrix servers while maintaining control over sensitive data. The combination of Synapse's robust server implementation with Element's polished interface creates a production-ready alternative to platforms like Slack or Microsoft Teams, with the added benefit of end-to-end encryption by default and compliance with data sovereignty requirements.

Key Features

  • Matrix protocol federation enabling communication across different homeservers
  • End-to-end encryption for messages, voice calls, and video calls using Olm/Megolm cryptographic ratchets
  • PostgreSQL backend with optimized schema for Matrix's directed acyclic graph room structure
  • Redis-powered worker mode supporting horizontal scaling across multiple Synapse processes
  • Coturn TURN/STUN server for WebRTC voice and video calls through NAT firewalls
  • Element Web client with real-time synchronization, message threading, and rich media support
  • Server-side room directory and user discovery with configurable federation policies
  • Application service integration allowing bridges to IRC, Discord, Telegram, and other platforms

Common Use Cases

  • 1Enterprise internal communication with end-to-end encryption compliance requirements
  • 2Educational institutions requiring FERPA-compliant messaging with external collaboration
  • 3Healthcare organizations needing HIPAA-compliant communication infrastructure
  • 4Government agencies requiring sovereign control over communication data
  • 5Open source projects wanting community chat with IRC/Discord bridge integration
  • 6Privacy-focused organizations migrating from centralized platforms like Slack or Teams
  • 7International companies needing federated communication across regional data boundaries

Prerequisites

  • Minimum 2GB RAM (PostgreSQL requires 1GB+, Synapse 512MB+ for small deployments)
  • Valid domain name with DNS control for Matrix federation on port 8448
  • SSL certificate authority access or Let's Encrypt compatibility for HTTPS
  • Understanding of Matrix protocol concepts including homeservers, rooms, and federation
  • Network firewall configuration allowing ports 80, 443, 8448, and 3478-5349 range
  • Basic knowledge of reverse proxy configuration for Element Web and Synapse integration

For development & testing. Review security settings, change default credentials, and test thoroughly before production use. See Terms

docker-compose.yml

docker-compose.yml
1services:
2 postgres:
3 image: postgres:15-alpine
4 environment:
5 - POSTGRES_USER=synapse
6 - POSTGRES_PASSWORD=${POSTGRES_PASSWORD}
7 - POSTGRES_DB=synapse
8 - POSTGRES_INITDB_ARGS=--encoding=UTF-8 --lc-collate=C --lc-ctype=C
9 volumes:
10 - postgres_data:/var/lib/postgresql/data
11 networks:
12 - matrix_net
13
14 redis:
15 image: redis:7-alpine
16 volumes:
17 - redis_data:/data
18 networks:
19 - matrix_net
20
21 synapse:
22 image: matrixdotorg/synapse:latest
23 environment:
24 - SYNAPSE_SERVER_NAME=localhost
25 - SYNAPSE_REPORT_STATS=no
26 volumes:
27 - synapse_data:/data
28 depends_on:
29 - postgres
30 - redis
31 ports:
32 - "8008:8008"
33 - "8448:8448"
34 networks:
35 - matrix_net
36
37 element-web:
38 image: vectorim/element-web:latest
39 ports:
40 - "8080:80"
41 volumes:
42 - ./element-config.json:/app/config.json:ro
43 depends_on:
44 - synapse
45 networks:
46 - matrix_net
47
48 coturn:
49 image: coturn/coturn:latest
50 ports:
51 - "3478:3478"
52 - "3478:3478/udp"
53 - "5349:5349"
54 - "5349:5349/udp"
55 environment:
56 - TURN_REALM=localhost
57 - TURN_SECRET=${TURN_SECRET}
58 networks:
59 - matrix_net
60
61 nginx:
62 image: nginx:alpine
63 ports:
64 - "80:80"
65 - "443:443"
66 volumes:
67 - ./nginx.conf:/etc/nginx/nginx.conf:ro
68 depends_on:
69 - synapse
70 - element-web
71 networks:
72 - matrix_net
73
74volumes:
75 postgres_data:
76 redis_data:
77 synapse_data:
78
79networks:
80 matrix_net:

.env Template

.env
1# Matrix/Element
2POSTGRES_PASSWORD=secure_postgres_password
3TURN_SECRET=secure_turn_secret
4
5# Element at http://localhost:8080
6# Synapse API at http://localhost:8008

Usage Notes

  1. 1Element at http://localhost:8080
  2. 2Synapse at http://localhost:8008
  3. 3Federation via port 8448
  4. 4TURN for voice/video
  5. 5Decentralized and private

Individual Services(6 services)

Copy individual services to mix and match with your existing compose files.

postgres
postgres:
  image: postgres:15-alpine
  environment:
    - POSTGRES_USER=synapse
    - POSTGRES_PASSWORD=${POSTGRES_PASSWORD}
    - POSTGRES_DB=synapse
    - POSTGRES_INITDB_ARGS=--encoding=UTF-8 --lc-collate=C --lc-ctype=C
  volumes:
    - postgres_data:/var/lib/postgresql/data
  networks:
    - matrix_net
redis
redis:
  image: redis:7-alpine
  volumes:
    - redis_data:/data
  networks:
    - matrix_net
synapse
synapse:
  image: matrixdotorg/synapse:latest
  environment:
    - SYNAPSE_SERVER_NAME=localhost
    - SYNAPSE_REPORT_STATS=no
  volumes:
    - synapse_data:/data
  depends_on:
    - postgres
    - redis
  ports:
    - "8008:8008"
    - "8448:8448"
  networks:
    - matrix_net
element-web
element-web:
  image: vectorim/element-web:latest
  ports:
    - "8080:80"
  volumes:
    - ./element-config.json:/app/config.json:ro
  depends_on:
    - synapse
  networks:
    - matrix_net
coturn
coturn:
  image: coturn/coturn:latest
  ports:
    - "3478:3478"
    - 3478:3478/udp
    - "5349:5349"
    - 5349:5349/udp
  environment:
    - TURN_REALM=localhost
    - TURN_SECRET=${TURN_SECRET}
  networks:
    - matrix_net
nginx
nginx:
  image: nginx:alpine
  ports:
    - "80:80"
    - "443:443"
  volumes:
    - ./nginx.conf:/etc/nginx/nginx.conf:ro
  depends_on:
    - synapse
    - element-web
  networks:
    - matrix_net

Quick Start

terminal
1# 1. Create the compose file
2cat > docker-compose.yml << 'EOF'
3services:
4 postgres:
5 image: postgres:15-alpine
6 environment:
7 - POSTGRES_USER=synapse
8 - POSTGRES_PASSWORD=${POSTGRES_PASSWORD}
9 - POSTGRES_DB=synapse
10 - POSTGRES_INITDB_ARGS=--encoding=UTF-8 --lc-collate=C --lc-ctype=C
11 volumes:
12 - postgres_data:/var/lib/postgresql/data
13 networks:
14 - matrix_net
15
16 redis:
17 image: redis:7-alpine
18 volumes:
19 - redis_data:/data
20 networks:
21 - matrix_net
22
23 synapse:
24 image: matrixdotorg/synapse:latest
25 environment:
26 - SYNAPSE_SERVER_NAME=localhost
27 - SYNAPSE_REPORT_STATS=no
28 volumes:
29 - synapse_data:/data
30 depends_on:
31 - postgres
32 - redis
33 ports:
34 - "8008:8008"
35 - "8448:8448"
36 networks:
37 - matrix_net
38
39 element-web:
40 image: vectorim/element-web:latest
41 ports:
42 - "8080:80"
43 volumes:
44 - ./element-config.json:/app/config.json:ro
45 depends_on:
46 - synapse
47 networks:
48 - matrix_net
49
50 coturn:
51 image: coturn/coturn:latest
52 ports:
53 - "3478:3478"
54 - "3478:3478/udp"
55 - "5349:5349"
56 - "5349:5349/udp"
57 environment:
58 - TURN_REALM=localhost
59 - TURN_SECRET=${TURN_SECRET}
60 networks:
61 - matrix_net
62
63 nginx:
64 image: nginx:alpine
65 ports:
66 - "80:80"
67 - "443:443"
68 volumes:
69 - ./nginx.conf:/etc/nginx/nginx.conf:ro
70 depends_on:
71 - synapse
72 - element-web
73 networks:
74 - matrix_net
75
76volumes:
77 postgres_data:
78 redis_data:
79 synapse_data:
80
81networks:
82 matrix_net:
83EOF
84
85# 2. Create the .env file
86cat > .env << 'EOF'
87# Matrix/Element
88POSTGRES_PASSWORD=secure_postgres_password
89TURN_SECRET=secure_turn_secret
90
91# Element at http://localhost:8080
92# Synapse API at http://localhost:8008
93EOF
94
95# 3. Start the services
96docker compose up -d
97
98# 4. View logs
99docker compose logs -f

One-Liner

Run this command to download and set up the recipe in one step:

terminal
1curl -fsSL https://docker.recipes/api/recipes/element-matrix-complete/run | bash

Troubleshooting

  • Federation not working: Verify port 8448 is accessible externally and SSL certificate covers your domain's Matrix subdomain
  • Voice/video calls failing: Check that Coturn ports 3478, 5349, and configured port range are open in firewall for UDP traffic
  • Database connection errors: Ensure PostgreSQL locale settings match Synapse requirements (UTF-8, C collation) and user has proper permissions
  • Element showing 'Failed to start' error: Verify config.json contains correct homeserver URL and base_url matches your domain
  • High memory usage in PostgreSQL: Tune shared_buffers and effective_cache_size based on available RAM, consider vacuuming large rooms
  • Synapse workers not distributing load: Check Redis connectivity and ensure worker configuration file properly defines worker types and ports

Community Notes

Loading...
Loading notes...

Download Recipe Kit

Get all files in a ready-to-deploy package

Includes docker-compose.yml, .env template, README, and license

Ad Space