Deploying a Matrix Synapse Server with Docker and Traefik
Table of Contents ๐
- Changelog
- 1. Prerequisites
- 2. Directory Structure
- 3. Download the Ansible Playbook
- 4. Initial Configuration
- 5. Install Ansible and Dependencies
- 6. Configure Ansible Hosts
- 7. Adjust Traefik for Federation
- 8. Run the Matrix Installation
- 9. Start the Matrix Services
- 10. Verify the Installation
- 11. Create Your First User
- 12. Log In with Element
- 13. Accessing the Synapse Admin UI
- 14. Maintenance
- 15. Troubleshooting
- Conclusion
This guide will walk you through deploying a powerful, federated Matrix Synapse homeserver. We will use the popular matrix-docker-ansible-deploy
playbook, which simplifies the setup of Synapse and its various components, including bridges for other chat platforms.
This setup is designed to integrate seamlessly with an existing Traefik v3 reverse proxy and a root-domain Nginx web server, making it a perfect addition to a modern, container-based infrastructure.
Changelog
Date | Change |
---|---|
2025-09-17 | Initial Version: Guide created, focusing on Traefik v3 integration, Ansible deployment, and enabling various bridges and features like Sliding Sync. |
1. Prerequisites
- Traefik v3 and CrowdSec with Docker Compose: A Modern Security Stack: This is the foundation for our reverse proxy and security.
- Deploying a Secure Nginx Website with Traefik and Docker Compose: This step is crucial as it establishes a web server on your root domain and correctly configures the
.well-known/matrix
delegation required for federation.
โ ๏ธ HARD REQUIREMENT |
The following steps will not work correctly without the Traefik and Nginx stacks running as described in the prerequisite guides. |
โน๏ธ ROOT DOMAIN REMAINS FREE FOR YOUR WEBSITE |
Synapse runs on a dedicated subdomain (e.g., |
Architecture (simplified)
Users / Browsers / Homeservers
โ
โผ
your-domain.com โโ> [Nginx](../nginx_webserver/) on root domain
โ โ
โ โโ Serves your website (/, /assets, ...)
โ โโ Serves .well-known/matrix/{server,client}
โ โ
โ โผ
โโโโโโโโโโโโโโโโโโโโโโโโโโโ delegates to โโ> matrix.your-domain.com
(Synapse via Traefik)
โ
โผ
Traefik (websecure, synapse)
โ
โผ
Synapse
You will also need:
git
,jq
, andpwgen
for version control, JSON parsing, and password generation.- Python 3 and
pip
installed on your server. sudo
or root access.
Install the required tools:
sudo apt update && sudo apt -y install git jq pwgen python3-pip
2. Directory Structure
First, weโll create a dedicated directory for the Matrix playbook configuration and files.
sudo mkdir -p /opt/containers/matrix
3. Download the Ansible Playbook
Next, clone the official matrix-docker-ansible-deploy
repository from GitHub into the directory you just created.
sudo git clone https://github.com/spantaleev/matrix-docker-ansible-deploy.git /opt/containers/matrix
4. Initial Configuration
The playbook is configured using Ansible variables. Weโll start by creating a configuration directory based on your Matrix serverโs hostname and copying the example vars.yml
file.
โน๏ธ HOSTNAME CONVENTION |
The playbook expects the server to be available at |
cd /opt/containers/matrix
# Replace 'matrix.your-domain.com' with your actual server name
sudo mkdir -p inventory/host_vars/matrix.your-domain.com
sudo cp examples/vars.yml inventory/host_vars/matrix.your-domain.com/
4.1. Generate Secret Keys
The configuration requires two strong secret keys. You can generate these using pwgen
or openssl
.
# Generate two strong keys and save them for the next step
pwgen -s 64 1
pwgen -s 64 1
4.2. Customize vars.yml
Now, open the vars.yml
file and customize it for your environment. This is the most critical step.
sudo nano inventory/host_vars/matrix.your-domain.com/vars.yml
Below is a complete configuration based on a feature-rich setup. Adjust the values to match your domain, secrets, and desired features.
# The bare domain name which represents your Matrix identity.
matrix_domain: your-domain.com
matrix_homeserver_implementation: synapse
matrix_homeserver_generic_secret_key: 'PASTE-YOUR-GENERIC-SECRET-KEY-HERE'
# Use our own Traefik (externally managed)
matrix_playbook_reverse_proxy_type: other-traefik-container
traefik_certs_dumper_enabled: false
traefik_config_certificatesResolvers_acme_email: 'your-email@example.com'
postgres_connection_password: 'PASTE-YOUR-POSTGRES-PASSWORD-HERE'
devture_systemd_docker_base_docker_service_name: "docker"
matrix_playbook_docker_installation_enabled: false
# We delegate .well-known from the root domain via Nginx
matrix_static_files_enabled: false
matrix_static_files_container_labels_traefik_enabled: false
matrix_coturn_enabled: false
# Traefik integration
matrix_playbook_reverse_proxy_container_network: proxy
matrix_playbook_reverse_proxy_hostname: traefik
matrix_federation_traefik_entrypoint_name: synapse
# Disable Synapse's internal client-API router (or redirect to a valid entrypoint)
matrix_synapse_reverse_proxy_companion_container_labels_internal_client_api_enabled: false
matrix_synapse_reverse_proxy_companion_container_labels_internal_client_api_traefik_entrypoints: websecure
# --- Core services ---
matrix_synapse_container_labels_traefik_docker_network: proxy
matrix_synapse_container_labels_traefik_entrypoints: websecure
matrix_synapse_container_labels_traefik_tls_certResolver: tls_resolver
matrix_synapse_container_labels_traefik_middlewares: 'security-headers@file,crowdsec-bouncer@docker'
matrix_client_element_container_labels_traefik_docker_network: proxy
matrix_client_element_container_labels_traefik_entrypoints: websecure
matrix_client_element_container_labels_traefik_tls_certResolver: tls_resolver
matrix_client_element_container_labels_traefik_middlewares: 'security-headers@file,crowdsec-bouncer@docker'
matrix_admin: '@your-username:your-domain.com'
matrix_sliding_sync_enabled: true
matrix_sliding_sync_container_labels_traefik_docker_network: proxy
matrix_sliding_sync_container_labels_traefik_entrypoints: websecure
matrix_sliding_sync_container_labels_traefik_tls_certResolver: tls_resolver
matrix_sliding_sync_container_labels_traefik_middlewares: 'security-headers@file,crowdsec-bouncer@docker'
matrix_synapse_admin_enabled: true
matrix_synapse_admin_container_http_host_bind_port: ""
matrix_synapse_admin_container_labels_traefik_docker_network: proxy
matrix_synapse_admin_container_labels_traefik_entrypoints: websecure
matrix_synapse_admin_container_labels_traefik_tls_certResolver: tls_resolver
matrix_synapse_admin_container_labels_traefik_middlewares: 'security-headers@file,crowdsec-bouncer@docker'
# E-Mail relay (optional)
exim_relay_sender_address: "matrix@your-domain.com"
exim_relay_relay_use: true
exim_relay_relay_host_name: "mail.your-domain.com"
exim_relay_relay_host_port: 587
exim_relay_relay_auth: true
exim_relay_relay_auth_username: "user@your-domain.com"
exim_relay_relay_auth_password: "YOUR-EMAIL-PASSWORD"
matrix_appservice_double_puppet_enabled: true
# --- Bridges ---
# The playbook can install and configure various bridges to connect your Matrix homeserver
# to other chat networks like Telegram, Discord, Signal, etc. When enabling a bridge,
# it is crucial to provide the `homeserver` configuration block to ensure it communicates
# correctly with Synapse via Traefik. For more details on available bridges and their
# specific configurations, refer to the official documentation.
# Telegram โ Correct EntryPoints + specify Homeserver
matrix_mautrix_telegram_enabled: true
matrix_mautrix_telegram_api_id: 12345678
matrix_mautrix_telegram_api_hash: 'your_telegram_api_hash'
matrix_mautrix_telegram_container_labels_traefik_entrypoints: websecure
matrix_mautrix_telegram_container_labels_public_endpoint_traefik_entrypoints: websecure
matrix_mautrix_telegram_container_labels_metrics_traefik_entrypoints: websecure
matrix_mautrix_telegram_configuration_extension_yaml: |
homeserver:
address: "https://matrix.your-domain.com"
domain: "your-domain.com"
bridge:
permissions:
'{{ matrix_admin }}': admin
# Meta Messenger
matrix_mautrix_meta_messenger_enabled: true
matrix_mautrix_meta_messenger_configuration_extension_yaml: |
homeserver:
address: "https://matrix.your-domain.com"
domain: "your-domain.com"
bridge:
permissions:
'*': relay
'your-domain.com': admin
'{{ matrix_admin }}': admin
# Discord โ Correct Avatar-Proxy & general EntryPoints
matrix_mautrix_discord_enabled: true
matrix_mautrix_discord_container_labels_traefik_entrypoints: websecure
matrix_mautrix_discord_container_labels_avatar_proxy_traefik_entrypoints: websecure
matrix_mautrix_discord_configuration_extension_yaml: |
homeserver:
address: "https://matrix.your-domain.com"
domain: "your-domain.com"
# --- Element Call / Livekit ---
matrix_element_call_enabled: true
matrix_element_call_container_labels_traefik_entrypoints: websecure
matrix_element_call_container_labels_traefik_tls_certResolver: tls_resolver
matrix_element_call_container_labels_traefik_docker_network: proxy
matrix_livekit_jwt_service_container_labels_traefik_entrypoints: websecure
matrix_livekit_jwt_service_container_labels_traefik_tls_certResolver: tls_resolver
matrix_livekit_jwt_service_container_labels_traefik_docker_network: proxy
livekit_server_container_labels_traefik_entrypoints: websecure
livekit_server_container_labels_public_metrics_traefik_tls_certResolver: tls_resolver
livekit_server_container_labels_traefik_docker_network: proxy
5. Install Ansible and Dependencies
This playbook uses Ansible to automate the setup. Choose the installation method that fits your distribution policy.
For Debian 12 (system pip with โbreak-system-packages)
# Ensure pip is available
sudo apt install -y python3-pip
# Install Ansible and required Python packages system-wide
pip3 install --break-system-packages ansible docker passlib
For Other Linux Distributions (recommended: Python virtual environment)
# Create and activate a dedicated virtual environment
python3 -m venv ~/.venvs/matrix-ansible
source ~/.venvs/matrix-ansible/bin/activate
# Upgrade pip and install required packages in the venv
pip install --upgrade pip
pip install ansible docker passlib
โน๏ธ VIRTUALENV REMINDER |
If you use the virtual environment, remember to reactivate it in new shell sessions with: |
Next, install the Ansible roles required by the playbook:
cd /opt/containers/matrix
make roles
6. Configure Ansible Hosts
Now, we need to tell Ansible which server to configure. Since we are running it locally, the setup is simple.
# Copy the example hosts file
sudo cp examples/hosts inventory/hosts
# Edit the file
sudo nano inventory/hosts
Modify the file to look like this. The ansible_connection=local
tells Ansible to run all commands on the machine itโs currently on, instead of connecting to a remote server via SSH. Because of this, ansible_host=127.0.0.1
(localhost) is the correct value. Parameters like ansible_ssh_user=root
are ignored in this mode, as the playbook runs with the permissions of the user executing it (in our case, root
via sudo
).
[matrix_servers]
matrix.your-domain.com ansible_host=127.0.0.1 ansible_connection=local
7. Adjust Traefik for Federation
For Matrix federation to work, other servers need to connect to yours on port 8448
. We must expose this port in our main Traefik stack and create a dedicated entrypoint for it.
Go to your Traefik stackโs directory (e.g., /opt/containers/traefik-stack
).
7.1. Expose Federation Port
Edit your main docker-compose.yml
:
sudo nano docker-compose.yml
Add port 8448
to the ports
section of the traefik
service:
services:
traefik:
# ... other settings
ports:
- "80:80"
- "443:443"
- "8448:8448" # Add this line for Matrix federation
# ... rest of the settings
7.2. Create Federation and Internal Entrypoints
Edit your static Traefik configuration (traefik/config/traefik.yml
):
sudo nano traefik/config/traefik.yml
Add a new synapse
entrypoint for federation and a matrix-internal-matrix-client-api
entrypoint for internal communication.
For recent playbook versions, it is recommended to also define the internal matrix-internal-matrix-client-api
entrypoint on port 8008. This port is not published to the host and serves as an internal C2S route for bridges and add-ons, preventing errors in the Traefik dashboard.
entryPoints:
web:
address: ":80"
# ...
websecure:
address: ":443"
synapse: # For federation
address: ":8448"
matrix-internal-matrix-client-api: # For internal C2S communication
address: ":8008"
7.3. Restart Traefik
Apply the changes by restarting your Traefik stack:
cd /opt/containers/traefik-stack
docker compose up -d
8. Run the Matrix Installation
With all the configuration in place, we can now run the Ansible playbook to set up and install everything.
cd /opt/containers/matrix
sudo ansible-playbook -i inventory/hosts setup.yml --tags=setup-all
This command will download all necessary Docker images, generate configuration files, and prepare the services. It may take several minutes.
9. Start the Matrix Services
Once the setup is complete, start all the Matrix containers:
sudo ansible-playbook -i inventory/hosts setup.yml --tags=start
After a few moments, all the services you enabled should be running. You can verify this with docker ps
.
10. Verify the Installation
After starting the services, itโs crucial to verify that all components are running and communicating correctly.
10.1. Check Running Containers
First, ensure all enabled services are running. The exact names will vary based on your matrix_domain
.
docker ps --format '{{.Names}}' | grep -E 'synapse|element|sliding|admin'
10.2. Verify .well-known
Delegation (CLI)
Check if your Nginx server is correctly serving the delegation files for your root domain.
# Replace 'your-domain.com' with your actual domain
curl -s https://your-domain.com/.well-known/matrix/server | jq
curl -s https://your-domain.com/.well-known/matrix/client | jq
Both commands should return a clean JSON output pointing to matrix.your-domain.com
.
10.3. Verify Federation Endpoint (CLI)
Test if the Synapse federation port (8448
) is correctly exposed through Traefik.
# Replace 'matrix.your-domain.com' with your actual matrix subdomain
curl -sS https://matrix.your-domain.com:8448/_matrix/federation/v1/version | jq
This should return a JSON object with server information, confirming the federation endpoint is reachable.
10.4. Verify CrowdSec Bouncer Registration
Check if the CrowdSec bouncer for Traefik has successfully registered with the CrowdSec agent.
cd /opt/containers/traefik-stack
docker compose exec crowdsec cscli bouncers list
You should see the Traefik bouncer in the list with a valid status.
10.5. Web-based Federation Test
Finally, use the official Federation Tester for a comprehensive check.
- Go to: https://federationtester.matrix.org/
- Enter your server name (e.g.,
your-domain.com
) and run the test.
If everything is configured correctly, you should see a success message.
11. Create Your First User
Use the playbook to register your administrator user.
โ ๏ธ PASSWORD SECURITY |
To avoid leaving your password in your shellโs history, omit the
|
cd /opt/containers/matrix
sudo ansible-playbook -i inventory/hosts setup.yml --tags=register-user -e 'username=your-username' -e 'admin=yes'
You can re-run this command with admin=no
to create non-admin users.
12. Log In with Element
Your homeserver is now ready! You can access the Element web client by navigating to https://matrix.your-domain.com
.
- Click โSign Inโ.
- Your homeserver should be pre-filled. If not, edit it to show your serverโs address (
matrix.your-domain.com
). - Log in with the username and password you just created.
13. Accessing the Synapse Admin UI
Because we set matrix_synapse_admin_enabled: true
in our vars.yml
configuration, a powerful web-based administration interface for Synapse is automatically deployed. This UI is essential for server maintenance and user management.
You can access it at: https://matrix.your-domain.com/synapse-admin
Log in with your Matrix administrator account. From here, you can:
- Manage users (deactivate, make admin, etc.).
- View server statistics and metrics.
- Explore rooms and manage their settings.
This interface is the primary tool for the day-to-day administration of your homeserver.
14. Maintenance
Updating Matrix
To update your Matrix server and its components to the latest version:
cd /opt/containers/matrix
# Pull the latest changes from the git repository
sudo git pull
# It's a good practice to review the CHANGELOG.md for breaking changes
# sudo nano CHANGELOG.md
# Re-run the setup and start tags to apply updates
sudo ansible-playbook -i inventory/hosts setup.yml --tags=setup-all,start
Stopping and Uninstalling
To stop all Matrix services:
cd /opt/containers/matrix
sudo ansible-playbook -i inventory/hosts setup.yml --tags=stop
To completely uninstall and delete all data:
cd /opt/containers/matrix
sudo ansible-playbook -i inventory/hosts setup.yml --tags=uninstall
โ ๏ธ DATA LOSS |
The |
15. Troubleshooting
Below are common issues and quick checks to resolve them.
โ ๏ธ TIP: VERIFY ONE LAYER AT A TIME |
When in doubt, verify DNS โ Traefik โ Nginx |
15.1 Federation test fails
- Ensure port 8448 is exposed by Traefik and the
synapse
entrypoint exists.
cd /opt/containers/traefik-stack
docker compose ps traefik
docker inspect traefik | jq '.[0].NetworkSettings.Ports["8448/tcp"]'
- Test the federation version endpoint directly:
curl -sS https://matrix.your-domain.com:8448/_matrix/federation/v1/version | jq
15.2 .well-known is invalid or missing
- Verify the
.well-known
delegation from your root domain (served by Nginx server):
curl -s https://your-domain.com/.well-known/matrix/server | jq
curl -s https://your-domain.com/.well-known/matrix/client | jq
- Re-check that the placeholders in
conf/nginx.conf
were replaced viased
and that CORS headers middleware uses camelCase keys in Traefik labels (e.g.,accessControlAllowOriginList
).
15.3 Traefik/CrowdSec middleware not applied
- Ensure you reference providers correctly:
- Security headers:
security-headers@file
- CrowdSec bouncer:
crowdsec-bouncer@docker
- Security headers:
15.4 ACME/TLS issues
- Inspect Traefik logs for ACME errors and DNS problems:
docker compose logs -n 200 traefik | tail -n 200
- Confirm A/AAAA records for
traefik.your-domain.com
andmatrix.your-domain.com
point to your server and that port 443 is reachable. - Disable CDN Proxy for Initial Certificate: If you are using a CDN like Cloudflare, ensure the proxy is disabled (set to โDNS Onlyโ or โgrey cloudโ) for your domains during the first certificate request. The HTTP-01 challenge requires Letโs Encrypt to reach your server directly. You can re-enable the proxy after the certificate is issued.
15.5 Docker network not found
- Make sure the external
proxy
network exists and is used by all services behind Traefik:
docker network ls | grep proxy || docker network create proxy
15.6 Permission problems in inventory/host_vars
- If editing files is cumbersome, you can align ownership (optional):
sudo chown -R "$USER":"$USER" /opt/containers/matrix/inventory
15.7 Ansible/venv not active
- If you installed Ansible in a virtualenv, reactivate it before running playbooks:
source ~/.venvs/matrix-ansible/bin/activate
15.8 CrowdSec bouncer not registered
- Check the bouncer status inside the CrowdSec container (run in your Traefik stack directory):
docker compose exec crowdsec cscli bouncers list
Conclusion
You now have a fully functional, federated Matrix Synapse homeserver integrated with your Traefik proxy and Nginx web server. This powerful setup not only gives you control over your own secure communications but is also extensible with numerous bridges and features, allowing you to create a central hub for all your chat services.