Files changed: engine/.embcache.json engine/changelog.md engine/lint-report.md vault/.trash/2026-06-19.md vault/docs/hardware/environment.md vault/docs/hardware/ip-allocation.md vault/docs/matrix/archivist.md vault/docs/matrix/matrix_host.md vault/docs/matrix/mautrix_signal.md vault/docs/matrix/synapse.md vault/docs/matrix/synapse_retention_discovery.md vault/docs/navi/cc-rules.md vault/docs/navi/deployment.md vault/docs/navi/themes.md vault/docs/services/ots-setup.md vault/docs/services/services.md vault/docs/services/usenet.md vault/docs/software/authentik.md vault/docs/software/caddy.md vault/docs/software/central.md vault/docs/software/dns.md vault/docs/software/geo-tools.md vault/docs/software/navi.md vault/docs/software/recon.md vault/docs/software/searxng.md vault/glossary.md vault/notes/echo6-landing-page-data-export.md vault/notes/ia-download-queue.md vault/projects/advbbs-project.md vault/projects/argus.md vault/projects/deploy-livesync.md vault/projects/fleet-patch-audit.md vault/projects/fleet-platform-baseline.md vault/projects/matrix-synapse-deployment.md vault/projects/meshai-config-hot-apply.md vault/projects/meshai-region-routing-plan.md vault/projects/meshai.md vault/projects/meshcore-transport.md vault/projects/meshtastic-headscale-runbook.md vault/projects/mmud-project.md vault/projects/nominatim-v5-reimport.md vault/runbooks/add-peertube-channel.md vault/runbooks/authentik-access-groups.md vault/runbooks/authentik-create-invitation.md vault/runbooks/authentik-oidc-application.md vault/runbooks/authentik-upgrade.md vault/runbooks/central-deploy-cutover.md vault/runbooks/ct-runbook.md vault/runbooks/edge2-access-reference.md vault/runbooks/expose-service-contabo.md vault/runbooks/expose-service-edge2.md vault/runbooks/expose-service-home.md vault/runbooks/fleet-magicdns-resolved-migration.md vault/runbooks/headless-browser-page-verification.md vault/runbooks/headscale-oidc-boot-order.md vault/runbooks/headscale-onboard-node.md vault/runbooks/ia-cli-reference.md vault/runbooks/ia-download-mirror.md vault/runbooks/idahomesh-bridge-setup.md vault/runbooks/idahomesh-vpn-device-setup.md vault/runbooks/lxc-service-migration.md vault/runbooks/mailcow-create-mailbox.md vault/runbooks/meshai-prod-compose-override.md vault/runbooks/meshmonitor-password-reset.md vault/runbooks/meshtastic-sidecar-node.md vault/runbooks/meshtasticd-sim-nodes-runbook.md vault/runbooks/nordvpn-lxc.md vault/runbooks/peertube-remote-runner.md vault/runbooks/pg-backup.md vault/runbooks/pi-nas-omv-runbook.md vault/runbooks/pipeline-patterns.md vault/runbooks/proxmox-create-ubuntu-vm.md vault/runbooks/proxmox-onboard-node.md vault/runbooks/pymc-repeater-kiss-tnc-reenumeration.md vault/runbooks/recon-operations.md vault/runbooks/recon-service-integration.md vault/runbooks/syncthing-add-node.md vault/runbooks/toc-cortex-pve9.2-update.md vault/session-resume/SESSION-HANDOFF-meshai-test.md
12 KiB
| title | type | tags | aliases | related | updated | ||||||||||||||||
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
| Matrix Synapse Deployment | project |
|
|
2026-07-13 |
Matrix Synapse Deployment
Status: Deployed 2026-02-15, migrated to Contabo 2026-02-15. Migrated to edge2 CT 106 2026-06-19. Target (historical, at time of deployment): Contabo VPS (5.189.158.149 / 100.64.0.1) URLs: https://matrix.echo6.co (synapse), https://element.echo6.co (Element Web) Server Name: echo6.co (federated identity: @user:echo6.co)
Architecture
| Component | Detail |
|---|---|
| Host (historical, at deployment time) | Contabo VPS (5.189.158.149 / 100.64.0.1) |
| Host (current) | edge2 CT 106 (100.64.0.37) — migrated 2026-06-18 |
| Docker services | synapse (127.0.0.1:8008), Element Web (127.0.0.1:8088), PostgreSQL 16 |
| Reverse proxy (historical) | Contabo caddy (auto ACME certs) |
| Reverse proxy (current) | edge2 host caddy |
| SSO | authentik OIDC → communication-users group |
| Federation | Well-known delegation on echo6.co base domain (served by utility Caddy) |
| Compose path | /opt/matrix/docker-compose.yml |
| Backup | Daily at 3AM, 14-day retention, /opt/matrix/backups/ |
The server name is echo6.co (not matrix.echo6.co) so federated user IDs are @user:echo6.co. The Synapse instance lives at matrix.echo6.co and delegation is handled via .well-known endpoints on the base domain.
Step 1: Provision LXC Container
Run ct-runbook.md on the utility node with these parameters:
CTID=108
HOSTNAME=matrix
STORAGE=local-lvm
DISK_SIZE=16
MEMORY=2048
CORES=2
BRIDGE=vmbr0
After the runbook completes (user, SSH, Docker, Tailscale all verified), continue here.
Step 2: Create Project Structure
SSH into CT 108:
CT_IP=$(ssh root@192.168.1.241 "pct exec 108 -- hostname -I | awk '{print \$1}'")
sshpass -p '7redditGold' ssh zvx@$CT_IP
Create directories:
sudo mkdir -p /opt/matrix/{synapse,postgres,element,backups,scripts}
sudo chown -R zvx:zvx /opt/matrix
Step 3: Create Docker Compose
Create /opt/matrix/docker-compose.yml:
services:
postgres:
image: postgres:16-alpine
container_name: matrix-postgres
restart: unless-stopped
environment:
POSTGRES_DB: synapse
POSTGRES_USER: synapse
POSTGRES_PASSWORD: ${POSTGRES_PASSWORD}
POSTGRES_INITDB_ARGS: "--encoding=UTF8 --lc-collate=C --lc-ctype=C"
volumes:
- ./postgres:/var/lib/postgresql/data
networks:
- matrix-net
healthcheck:
test: ["CMD-SHELL", "pg_isready -U synapse -d synapse"]
interval: 10s
timeout: 5s
retries: 5
synapse:
image: matrixdotorg/synapse:latest
container_name: matrix-synapse
restart: unless-stopped
depends_on:
postgres:
condition: service_healthy
environment:
SYNAPSE_CONFIG_PATH: /data/homeserver.yaml
volumes:
- ./synapse:/data
ports:
- "8008:8008"
networks:
- matrix-net
element:
image: vectorim/element-web:latest
container_name: matrix-element
restart: unless-stopped
volumes:
- ./element/config.json:/app/config.json:ro
ports:
- "8080:80"
networks:
- matrix-net
networks:
matrix-net:
driver: bridge
Create /opt/matrix/.env:
POSTGRES_PASSWORD=$(openssl rand -base64 24 | tr -d '/+=' | head -c 32)
echo "POSTGRES_PASSWORD=$POSTGRES_PASSWORD" > /opt/matrix/.env
chmod 600 /opt/matrix/.env
echo "Save this password to /home/zvx/projects/.ref/credentials"
cat /opt/matrix/.env
Step 4: Generate Synapse Config
cd /opt/matrix
docker run -it --rm \
-v ./synapse:/data \
-e SYNAPSE_SERVER_NAME=echo6.co \
-e SYNAPSE_REPORT_STATS=no \
matrixdotorg/synapse:latest generate
Edit synapse/homeserver.yaml — replace the full database section and add OIDC config:
server_name: "echo6.co"
public_baseurl: "https://matrix.echo6.co/"
listeners:
- port: 8008
type: http
tls: false
x_forwarded: true
bind_addresses: ['0.0.0.0']
resources:
- names: [client, federation]
compress: false
database:
name: psycopg2
args:
user: synapse
password: <POSTGRES_PASSWORD from .env>
database: synapse
host: matrix-postgres
port: 5432
cp_min: 5
cp_max: 10
media_store_path: /data/media_store
enable_registration: false
url_preview_enabled: true
# Authentik OIDC — fill client_id and client_secret after running authentik-oidc-application.md
oidc_providers:
- idp_id: authentik
idp_name: "Echo6 SSO"
discover: true
issuer: "https://auth.echo6.co/application/o/matrix/"
client_id: "<from authentik-oidc-application.md>"
client_secret: "<from authentik-oidc-application.md>"
scopes: ["openid", "profile", "email"]
user_mapping_provider:
config:
localpart_template: "{{ user.preferred_username }}"
display_name_template: "{{ user.name }}"
Step 5: Configure Element Web
Create /opt/matrix/element/config.json:
{
"default_server_config": {
"m.homeserver": {
"base_url": "https://matrix.echo6.co",
"server_name": "echo6.co"
}
},
"brand": "Echo6 Chat",
"disable_guests": true,
"disable_3pid_login": false
}
Step 6: Start Services
cd /opt/matrix
docker compose up -d
docker compose ps
Wait for Synapse to initialize the database (watch logs):
docker compose logs -f synapse
# Wait for "Synapse now listening on TCP port 8008"
Step 7: Expose via Caddy and DNS
Run expose-service-home.md twice — once for matrix.echo6.co and once for element.echo6.co.
This service has OIDC, so use local IP per the runbook's decision table.
matrix.echo6.co
- Backend:
192.168.1.108:8008(local IP, has OIDC) - Issue cert, install cert, add Caddy site block, add GoDaddy dns
Caddy site block (note the path-based routing for Matrix):
matrix.echo6.co {
tls /etc/caddy/certs/matrix.echo6.co.fullchain.crt /etc/caddy/certs/matrix.echo6.co.key
reverse_proxy /_matrix/* 192.168.1.108:8008
reverse_proxy /_synapse/* 192.168.1.108:8008
}
element.echo6.co
- Backend:
192.168.1.108:8080(local IP) - Issue cert, install cert, add Caddy site block, add GoDaddy dns
element.echo6.co {
tls /etc/caddy/certs/element.echo6.co.fullchain.crt /etc/caddy/certs/element.echo6.co.key
reverse_proxy 192.168.1.108:8080
}
Well-known delegation (federation)
This must go on the echo6.co base domain. Check if there's already an echo6.co block in the Utility Caddy Caddyfile — if so, merge these handle directives into it. If not, add a new block:
echo6.co {
tls /etc/caddy/certs/echo6.co.fullchain.crt /etc/caddy/certs/echo6.co.key
handle /.well-known/matrix/server {
header Content-Type application/json
respond `{"m.server": "matrix.echo6.co:443"}`
}
handle /.well-known/matrix/client {
header Content-Type application/json
header Access-Control-Allow-Origin *
respond `{"m.homeserver": {"base_url": "https://matrix.echo6.co"}}`
}
# ... any existing handlers for echo6.co ...
}
If echo6.co doesn't have a cert yet, issue one via acme.sh following the same pattern in expose-service-home.md.
dnsmasq split DNS
Historical (Contabo, pre-2026-06-19 migration). Was added to /etc/dnsmasq.d/tailscale-dns.conf on Contabo:
address=/matrix.echo6.co/100.64.0.8
address=/element.echo6.co/100.64.0.8
Both point to the Utility Caddy Tailscale IP (100.64.0.8), which proxies to CT 108.
Restart dnsmasq (historical — ran on Contabo):
ssh root@100.64.0.1 "systemctl restart dnsmasq"
Current (post-migration): Matrix and Element now run on edge2 CT 106 (100.64.0.37), fronted by edge2's own host Caddy — see services and matrix_host. dnsmasq split-DNS on the old Contabo host is no longer part of this path.
Step 8: Configure Authentik SSO
Run authentik-oidc-application.md with these inputs:
SERVICE_NAME=Matrix
SERVICE_SLUG=matrix
SERVICE_URL=https://matrix.echo6.co
OIDC_CALLBACK_PATH=/_synapse/client/oidc/callback
NEEDS_OFFLINE_ACCESS=no
CLIENT_TYPE=confidential
After completing the runbook, take the Client ID and Client Secret and update synapse/homeserver.yaml (Step 4) with the real values. Then restart Synapse:
cd /opt/matrix && docker compose restart synapse
Step 9: Bind Access Group
Run authentik-access-groups.md Procedure B to bind the matrix application to the communication-users group.
APP_SLUG=matrix
GROUP_PK=31bce176-cd86-4aea-8db3-a57e03d5c2d1 # communication-users
This shares the same access group as Mailcow.
Step 10: Create Admin User
docker exec -it matrix-synapse register_new_matrix_user \
-u matt \
-p <secure-password> \
-a \
-c /data/homeserver.yaml \
http://localhost:8008
Step 11: Schedule PostgreSQL Backups
Run pg-backup.md with these inputs:
CONTAINER_NAME=matrix-postgres
DB_NAME=synapse
DB_USER=synapse
BACKUP_DIR=/opt/matrix/backups
RETENTION_DAYS=14
CRON_SCHEDULE="0 3 * * *"
Verification
Internal (from CT 108)
curl -s http://localhost:8008/_matrix/client/versions | jq .
curl -s http://localhost:8008/_matrix/federation/v1/version | jq .
curl -s http://localhost:8080 | head -5
External (from cortex or any tailnet device)
curl -s https://matrix.echo6.co/_matrix/client/versions | jq .
curl -s https://matrix.echo6.co/_matrix/federation/v1/version | jq .
curl -sI https://element.echo6.co | head -5
curl -s https://echo6.co/.well-known/matrix/server | jq .
curl -s https://echo6.co/.well-known/matrix/client | jq .
Federation
curl -s "https://federationtester.matrix.org/api/report?server_name=echo6.co" | jq '.FederationOK'
Must return true.
SSO
- Open https://element.echo6.co
- Click SSO login
- Should redirect to auth.echo6.co → authenticate → redirect back to Element
- Verify user identity matches authentik profile
Troubleshooting
Synapse won't start
docker compose logs synapse 2>&1 | tail -50
Common causes: bad YAML indentation in homeserver.yaml, wrong PostgreSQL password, database not ready.
Federation test fails
Check in order:
.well-known/matrix/serverreturns{"m.server": "matrix.echo6.co:443"}/_matrix/federation/v1/versionis accessible from the public internet- Caddy is routing
/_matrix/*paths correctly (not just root) - GoDaddy DNS for
echo6.copoints to 199.6.36.163
SSO login loop
See troubleshooting in authentik-oidc-application.md. Most common cause: missing signing key on the Authentik provider, or wrong callback path.
Element can't connect
Verify Element's config.json has base_url set to https://matrix.echo6.co (not http://, not localhost).
Runbook References
| Step | Runbook | Purpose |
|---|---|---|
| 1 | ct-runbook.md | LXC provisioning, Docker, user, SSH, Tailscale |
| 7 | expose-service-home.md | SSL cert, Caddy site block, GoDaddy DNS |
| 8 | authentik-oidc-application.md | Create OIDC provider + application |
| 9 | authentik-access-groups.md | Bind communication-users group |
| 11 | pg-backup.md | Scheduled PostgreSQL backup with retention |
Credentials Reference
Store in /home/zvx/projects/.ref/credentials:
# Matrix Synapse
MATRIX_POSTGRES_PASSWORD=<from .env>
MATRIX_OIDC_CLIENT_ID=<from authentik-oidc-application.md>
MATRIX_OIDC_CLIENT_SECRET=<from authentik-oidc-application.md>
MATRIX_OIDC_ISSUER=https://auth.echo6.co/application/o/matrix/
MATRIX_ADMIN_USER=matt
Post-Deploy Updates
After deployment, update these docs:
docs/services/services.md— add Matrix entrydocs/software/caddy.md— add matrix.echo6.co and element.echo6.co site blocksdocs/software/dns.md— note well-known delegation on echo6.codocs/hardware/environment.md— add CT 108 to LXC table and Headscale node listrunbooks/authentik-access-groups.md— add Matrix to application bindings table
Created: 2026-02-15