2026-06-04 14:30:29 -04:00
2022-02-08 01:37:10 +01:00
2026-04-29 15:01:50 -04:00
2025-01-28 17:42:07 +01:00
2024-04-12 12:30:05 -03:00
2024-04-12 12:30:05 -03:00
2024-09-19 14:57:49 +02:00
2026-03-05 14:49:11 +01:00
2026-06-04 14:30:29 -04:00

Matrix (Synapse)

  • Category: Apps
  • Status: 0, work-in-progress
  • Image: matrixdotorg/synapse, 4, upstream
  • Healthcheck: Yes
  • Backups: No
  • Email: Yes
  • Tests: No
  • SSO: Yes

Basic usage

  1. Set up Docker Swarm and abra
  2. Deploy coop-cloud/traefik
  3. abra app new matrix-synapse --secrets (optionally with --pass if you'd like to save secrets in pass)
  4. abra app config YOURAPPDOMAIN - be sure to change $DOMAIN to something that resolves to your Docker swarm box
  5. abra app deploy YOURAPPDOMAIN
  6. Create an initial user: abra app run YOURAPPDOMAIN app register_new_matrix_user -c /data/homeserver.yaml http://localhost:8008

Tips & Tricks

Create User

register_new_matrix_user -u <username> -k $(cat /var/run/secrets/registration) -p <password>

Set Admin User

abra app cmd YOURAPPDOMAIN db set_admin <adminuser>

Disabling federation

  • Use DISABLE_FEDERATION=1 to turn off federation listeners
  • Don't use compose.matrix.yml in your traefik config to keep the federation ports closed

Enabling federation

Federation is on by default (DISABLE_FEDERATION=0). Remote homeservers need a way to discover the host:port that serves your SERVER_NAME. There are three supported approaches.

Option 1: built-in well-known (SERVER_NAME = DOMAIN)

Set SERVE_SERVER_WELLKNOWN=true and leave SERVER_NAME unset (defaults to DOMAIN). The recipe's nginx serves /.well-known/matrix/server and /.well-known/matrix/client on DOMAIN.

Suitable when users are e.g. @alice:matrix.example.com.

Option 2: external well-known on SERVER_NAME

Use when you want users to be e.g. @alice:example.com while Synapse runs at matrix.example.com. Set:

SERVER_NAME=example.com
DOMAIN=matrix.example.com
SERVE_SERVER_WELLKNOWN=false

The two paths that must be served on SERVER_NAME are:

  • https://example.com/.well-known/matrix/server{"m.server": "matrix.example.com:443"}
  • https://example.com/.well-known/matrix/client{"m.homeserver": {"base_url": "https://matrix.example.com"}}

Recommended — let this recipe serve them via Traefik by enabling compose.wellknown.yml:

COMPOSE_FILE="$COMPOSE_FILE:compose.wellknown.yml"

This publishes a Traefik router Host(${SERVER_NAME}) && PathPrefix(/.well-known/matrix) pointing at the matrix nginx, which already serves both files. The path-scoped, high-priority rule coexists with any apex website that also serves Host(${SERVER_NAME}) — that site keeps serving everything except /.well-known/matrix. SERVER_NAME must resolve to this Traefik so ACME can issue its certificate.

Alternative — serve the two files yourself from whatever already hosts example.com.

Option 3: Traefik matrix-federation entrypoint (port 8448)

Use when SERVER_NAMEDOMAIN but you have no separate web service at SERVER_NAME. Remote homeservers fall back to SERVER_NAME:8448 when there's no delegation.

Requirements:

  • traefik >= 5.1.2+v3.6.15 with MATRIX_FEDERATION_ENABLED=1 and compose.matrix.yml enabled.
  • SERVER_NAME set in your matrix-synapse env (used by the federation router's Host rule).

With these in place, the recipe publishes a Traefik router on Host(${SERVER_NAME}) via the matrix-federation entrypoint, reusing the existing matrix nginx → synapse path.

Option 4: DNS SRV records (usually not viable here)

Federation can also be delegated with a DNS SRV record on SERVER_NAME instead of well-known:

_matrix-fed._tcp.example.com.  3600 IN SRV 10 0 8448 matrix.example.com.   # modern
_matrix._tcp.example.com.      3600 IN SRV 10 0 8448 matrix.example.com.   # deprecated, for older peers

The catch is TLS: on the SRV path a remote validates the certificate against SERVER_NAME, not the SRV target. This recipe's Traefik only issues a cert for DOMAIN, so:

  • SRV → DOMAIN:443 fails — the presented cert is for DOMAIN, but the peer requires one for SERVER_NAME.
  • SRV → SERVER_NAME:443 collides — Traefik routes TLS by SNI, and Host(SERVER_NAME) on :443 is already owned by whatever apex site serves SERVER_NAME.
  • SRV → SERVER_NAME:8448 works — the Option 3 matrix-federation router holds a cert for SERVER_NAME — but that's just Option 3 made explicit (the :8448 fallback already works with no SRV record).

So I think SRV does little here. Probably prefer Option 2 (well-known).

Verifying

The canonical test:

Or check the underlying paths directly. They should all return JSON:

# Options 1 & 2 — delegation
curl https://SERVER_NAME/.well-known/matrix/server

# Option 3 — federation endpoint via 8448
curl https://SERVER_NAME:8448/_matrix/key/v2/server

# Confirms Synapse itself is healthy (independent of the path remote servers use)
curl https://DOMAIN/_matrix/key/v2/server

Getting client discovery on a custom domain

Enable compose.wellknown.yml (see Option 2 above) — it serves /.well-known/matrix/client on SERVER_NAME too, so clients signing in as @alice:example.com auto-discover the homeserver.

Bridges

For all Bridges:

  • Setting it up is a bit of a chicken/egg & chasing cats moment.
  • Make sure to uncomment APP_SERVICES_ENABLED, HOMESERVER_URL, HOMESERVER_DOMAIN, compose.shared_secret_auth.yml, SHARED_SECRET_AUTH_ENABLED and SECRET_SHARED_SECRET_AUTH_VERSION
  • include the registration in synapse, e.g. APP_SERVICE_CONFIGS="[\"/telegram-data/registration.yaml\"]"
  • and set yourself as admin, e.g.: TELEGRAM_BRIDGE_PERMISSIONS="{ \"*\": \"relaybot\", \"@akadmin:example.com\": \"admin\"}"

Important

The shared secret authenticator may break when matrix-synapse uses a newer python version with an error stating something like "module not found". You have to fix the path in the compose.shared_secret_auth.yml like here

Telegram bridging

You need to get your bot setup on the telegram side first by creating a telegram app and a telegram bot and have these values:

api_id: ...
api_hash: ...
telegram_bot_token: ...

Experimental script for a automated token replacement:

DOMAIN=<domain>
abra app secret insert $DOMAIN telegram_api_hash v1 <secret>
abra app secret insert $DOMAIN telegram_bot_token v1 <secret>
abra app secret generate -a $DOMAIN

abra app deploy $DOMAIN
abra app cmd -l $DOMAIN set_bridge_tokens telegram

Alternatively a manual guide for the necessary steps:

DOMAIN=<domain>
abra app secret insert $DOMAIN telegram_api_hash v1 <secret>
abra app secret insert $DOMAIN telegram_bot_token v1 <secret>
abra app secret generate -a $DOMAIN

abra app deploy $DOMAIN
abra app run $DOMAIN telegrambridge cat /data/registration.yaml
abra app undeploy $DOMAIN

abra app secret rm $DOMAIN telegram_as_token
abra app secret insert $DOMAIN telegram_as_token v1 <secret>

abra app secret rm $DOMAIN telegram_hs_token
abra app secret insert $DOMAIN telegram_hs_token v1 <secret>

abra app deploy $DOMAIN

Some helpful documentation:

Discord bridging

WIP docs

Just as messy as the Telegram bridging above! Rough guide:

  • get a local copy of config.yaml
  • fill it out with the values you need, all the discord token stuff, etc.
  • run mkdir -p data && cp config.yaml data/ then docker run --rm -v data:/data halfshot/matrix-appservice-discord:v1.0.0 sh -c "cd /data && node /build/src/discordas.js -r -u "http://discordbridge:9005" -c config.yaml"
  • this generates the app service registration configuration you need to feed to the homeserver
  • run secret generation for the discord_db_password, insert your discord_bot_token
  • run abra app cp <domain> discord-registration.yaml app:/discord-data (it has to be called discord-registration.yaml)
  • deploy the bridge & happy hacking

Some helpful documentation:

Signal bridging

Experimental script for a more automated token replacement:

DOMAIN=<domain>
abra app secret generate -a $DOMAIN
abra app deploy $DOMAIN
abra app cmd -l $DOMAIN set_bridge_tokens signal

Alternatively a manual guide for the necessary steps:

DOMAIN=<domain>
abra app secret insert $DOMAIN signal_hs_token v1 foo
abra app secret insert $DOMAIN signal_as_token v1 foo
abra app secret generate $DOMAIN -a
abra app deploy $DOMAIN
abra app run $DOMAIN signalbridge cat /data/registration.yaml

abra app secret rm $DOMAIN signal_as_token
abra app secret insert $DOMAIN signal_as_token v1 <secret>
abra app secret rm $DOMAIN signal_hs_token
abra app secret insert $DOMAIN signal_hs_token v1 <secret>

abra app deploy $DOMAIN
  • message @signalbot:example.com to test
  • See the docs for authentication
Description
No description provided
Readme 507 KiB
Languages
Shell 73.3%
Python 22.4%
Roff 4.3%