synapse post

This commit is contained in:
2026-04-30 14:19:58 +02:00
parent b7d5a28570
commit f1886a5c73
@@ -0,0 +1,315 @@
---
layout: post
author: Sam Hadow
tags: messaging podman sysadmin
---
In this blog post I'll describe how to self host a [matrix.org](https://matrix.org/) instance using podman. The homeserver used will be synapse.
# What are matrix.org and synapse?
First a short introduction:
matrix.org is an open source, secure and decentralized communication protocol. Secure because it defines how end to end encryption should be implemented in the clients.
With matrix.org you have [homeservers](https://matrix.org/ecosystem/servers/) and [clients](https://matrix.org/ecosystem/clients/). Homeservers are what the clients connect to and can federate with each other *(meaning someone with an account on homeserver A can talk to someone with an account on homeserver B)*.
[Bridges](https://matrix.org/ecosystem/bridges/) to other messaging services can also be hosted alongside a homeserver. Hosting your own homeserver allows you to host the bridges you want.
In this blog post I explain how to self host a specific homeserver, which is maintained by the matrix.org foundation: synapse.
# self hosting synapse
## initial setup
### creating secret and directories
Create the database secret not to expose it in your configuration files:
```bash
echo -n "<postgres-pass>" > /tmp/secret
podman secret create synapse_postgres_pass /tmp/secret
```
Adapt the path to your own path for the directories:
```bash
mkdir -p /home/data/podman/synapse/{db,config,media,logs}
```
### generating the configuration file
*(adapt your-domain to your own, for example: example.org)*
```bash
podman pod create --name synapse -p 8008:8008 -m=2048m
podman run -it --rm \
-v /home/data/podman/synapse/config:/data:Z \
-e SYNAPSE_SERVER_NAME=<your-domain> \
-e SYNAPSE_REPORT_STATS=no \
docker.io/matrixdotorg/synapse:latest generate
```
### modifying the configuration file
The previous command will generate a homeserver.yaml file. You'll have to modify this file before using synapse. You'll have to modify at least the following parts:
+ modify the database part *(replace the password)*
```yaml
database:
name: psycopg2
txn_limit: 10000
args:
user: synapse
password: <POSTGRES_PASSWORD>
dbname: synapse
host: 127.0.0.1
port: 5432
cp_min: 5
cp_max: 10
```
+ set `enable_registration` to false unless you want users to register freely on your instance.
+ set a shared secret file with `registration_shared_secret_path` to have access to an [API](https://element-hq.github.io/synapse/latest/admin_api/register_api.html) to create users. Be sure to use a secure secret as anyone having this secret can register on your instance as an admin. Also keep in mind the path is relative to the container, not the host.
### reverse proxy
In this guide the port used is 8008, we'll use nginx to server the synapse homeserver on the port 443.
*(adapt your-domain to your own)*
This part is important for users to have the name user@your-domain while hosting synapse on a subdomain. And it's also important for clients and other homeserver to recognize your server.
in sites-available:
```nginx
# synapse.conf
server {
listen 443 ssl;
listen [::]:443 ssl;
server_name synapse.<your-domain>;
ssl_certificate /etc/letsencrypt/live/<your-domain>/fullchain.pem;
ssl_certificate_key /etc/letsencrypt/live/<your-domain>/privkey.pem;
location /.well-known/matrix/server {
return 200 '{"m.server": "synapse.<your-domain>:443"}';
add_header Content-Type application/json;
}
location /.well-known/matrix/client {
return 200 '{"m.homeserver": {"base_url": "https://synapse.<your-domain>"},"m.identity_server": {"base_url": "https://vector.im"}}';
add_header Content-Type application/json;
add_header "Access-Control-Allow-Origin" *;
}
location / {
proxy_pass http://127.0.0.1:8008;
proxy_set_header X-Forwarded-For $remote_addr;
proxy_set_header X-Forwarded-Proto $scheme;
proxy_set_header Host $host;
# Nginx by default only allows file uploads up to 1M in size
# Increase client_max_body_size to match max_upload_size defined in homeserver.yaml
client_max_body_size 256M;
# Synapse responses may be chunked, which is an HTTP/1.1 feature.
proxy_http_version 1.1;
}
add_header Strict-Transport-Security "max-age=31536000; includeSubDomains" always;
}
```
In your http.conf add this snippet in the https server block:
```nginx
location /.well-known/matrix/server {
return 200 '{"m.server": "synapse.<your-domain>:443"}';
add_header Content-Type application/json;
add_header "Access-Control-Allow-Origin" *;
}
location /.well-known/matrix/client {
return 200 '{"m.homeserver": {"base_url": "https://synapse.<your-domain>"},"m.identity_server": {"base_url": "https://vector.im"}}';
add_header Content-Type application/json;
add_header "Access-Control-Allow-Origin" *;
}
```
## running the pod
### with podman command line
Again adapt the path:
*The database version can be different but it needs to be pinned to a specific version to avoid issues with updates. You'll have to manually update this container.*
```bash
podman run -d --pod=synapse \
--secret synapse_postgres_pass,type=env,target=POSTGRES_PASSWORD \
-e POSTGRES_DB="synapse" \
-e POSTGRES_USER="synapse" \
-e POSTGRES_INITDB_ARGS="--encoding=UTF-8 --lc-collate=C --lc-ctype=C" \
-v /home/data/podman/synapse/db:/var/lib/postgresql/data:Z \
--name=synapse-db \
docker.io/library/postgres:16
podman run -d --pod=synapse \
-e SYNAPSE_CONFIG_PATH=/data/homeserver.yaml \
-v /home/data/podman/synapse/config:/data:Z \
-v /home/data/podman/synapse/media:/data/media:z \
-v /home/data/podman/synapse/logs:/data/logs:Z \
--name=synapse-app \
--label io.containers.autoupdate=registry docker.io/matrixdotorg/synapse:latest
```
Then you can generate the systemd services:
```bash
cd ~/.config/systemd/user/
podman generate systemd --restart-policy=on-failure --files --new --name synapse
systemctl --user daemon-reload
systemctl --user enable --now pod-synapse.service
```
### with systemd services directly
Create these files in `~/.config/systemd/user/`.
```ini
# pod-synapse.service
[Unit]
Description=Podman pod-synapse.service
Documentation=man:podman-generate-systemd(1)
Wants=network-online.target
After=network-online.target
RequiresMountsFor=/run/user/1000/containers
Wants=container-synapse-app.service container-synapse-db.service
Before=container-synapse-app.service container-synapse-db.service
[Service]
Environment=PODMAN_SYSTEMD_UNIT=%n
Restart=on-failure
TimeoutStopSec=70
ExecStartPre=/usr/bin/podman pod create \
--infra-conmon-pidfile %t/pod-synapse.pid \
--pod-id-file %t/pod-synapse.pod-id \
--exit-policy=stop \
--name synapse \
-p 8008:8008 \
-m=2048m \
--replace
ExecStart=/usr/bin/podman pod start \
--pod-id-file %t/pod-synapse.pod-id
ExecStop=/usr/bin/podman pod stop \
--ignore \
--pod-id-file %t/pod-synapse.pod-id \
-t 10
ExecStopPost=/usr/bin/podman pod rm \
--ignore \
-f \
--pod-id-file %t/pod-synapse.pod-id
PIDFile=%t/pod-synapse.pid
Type=forking
[Install]
WantedBy=default.target
```
```ini
# container-synapse-db.service
[Unit]
Description=Podman container-synapse-db.service
Documentation=man:podman-generate-systemd(1)
Wants=network-online.target
After=network-online.target
RequiresMountsFor=%t/containers
BindsTo=pod-synapse.service
After=pod-synapse.service
[Service]
Environment=PODMAN_SYSTEMD_UNIT=%n
Restart=on-failure
TimeoutStopSec=70
ExecStart=/usr/bin/podman run \
--cidfile=%t/%n.ctr-id \
--cgroups=no-conmon \
--rm \
--pod-id-file %t/pod-synapse.pod-id \
--sdnotify=conmon \
--replace \
-d \
--secret synapse_postgres_pass,type=env,target=POSTGRES_PASSWORD \
-e POSTGRES_DB=synapse \
-e POSTGRES_USER=synapse \
-e "POSTGRES_INITDB_ARGS=--encoding=UTF-8 --lc-collate=C --lc-ctype=C" \
-v /home/data/podman/synapse/db:/var/lib/postgresql/data:Z \
--name=synapse-db \
docker.io/library/postgres:16
ExecStop=/usr/bin/podman stop \
--ignore -t 10 \
--cidfile=%t/%n.ctr-id
ExecStopPost=/usr/bin/podman rm \
-f \
--ignore -t 10 \
--cidfile=%t/%n.ctr-id
Type=notify
NotifyAccess=all
[Install]
WantedBy=default.target
```
```ini
# container-synapse-app.service
[Unit]
Description=Podman container-synapse-app.service
Documentation=man:podman-generate-systemd(1)
Wants=network-online.target
After=network-online.target
RequiresMountsFor=%t/containers
BindsTo=pod-synapse.service
After=pod-synapse.service
[Service]
Environment=PODMAN_SYSTEMD_UNIT=%n
Restart=on-failure
TimeoutStopSec=70
ExecStart=/usr/bin/podman run \
--cidfile=%t/%n.ctr-id \
--cgroups=no-conmon \
--rm \
--pod-id-file %t/pod-synapse.pod-id \
--sdnotify=conmon \
--replace \
-d \
-e SYNAPSE_CONFIG_PATH=/data/homeserver.yaml \
-v /home/data/podman/synapse/config:/data:Z \
-v /home/data/podman/synapse/media:/data/media:z \
-v /home/data/podman/synapse/logs:/data/logs:Z \
--name=synapse-app \
--label io.containers.autoupdate=registry docker.io/matrixdotorg/synapse:latest
ExecStop=/usr/bin/podman stop \
--ignore -t 10 \
--cidfile=%t/%n.ctr-id
ExecStopPost=/usr/bin/podman rm \
-f \
--ignore -t 10 \
--cidfile=%t/%n.ctr-id
Type=notify
NotifyAccess=all
[Install]
WantedBy=default.target
```
## managing the homeserver
### registering users
With access to the server, creating the user interactively:
```bash
podman exec -it synapse-app /bin/sh
register_new_matrix_user --user <username> --config /data/homeserver.yaml
```
Or with the registration API, you can use [this script](https://git.hadow.fr/sam.hadow/Useful-scripts/src/branch/main/create_matrix_account.sh).