From f1886a5c7353ff5ad50895e153887ff798cecb97 Mon Sep 17 00:00:00 2001 From: Sam Hadow Date: Thu, 30 Apr 2026 14:19:58 +0200 Subject: [PATCH] synapse post --- ...f-hosting-a-matrix-instance-with-podman.md | 315 ++++++++++++++++++ 1 file changed, 315 insertions(+) create mode 100644 _posts/2026-04-30-self-hosting-a-matrix-instance-with-podman.md diff --git a/_posts/2026-04-30-self-hosting-a-matrix-instance-with-podman.md b/_posts/2026-04-30-self-hosting-a-matrix-instance-with-podman.md new file mode 100644 index 0000000..dd9a690 --- /dev/null +++ b/_posts/2026-04-30-self-hosting-a-matrix-instance-with-podman.md @@ -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 "" > /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= \ + -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: + 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.; + + ssl_certificate /etc/letsencrypt/live//fullchain.pem; + ssl_certificate_key /etc/letsencrypt/live//privkey.pem; + + location /.well-known/matrix/server { + return 200 '{"m.server": "synapse.:443"}'; + add_header Content-Type application/json; + } + location /.well-known/matrix/client { + return 200 '{"m.homeserver": {"base_url": "https://synapse."},"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.: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."},"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 --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).