Skip to content

Start9Labs/synapse-startos

Repository files navigation

Synapse Logo

Synapse on StartOS

Upstream docs: https://element-hq.github.io/synapse/latest

Everything not listed in this document should behave the same as upstream Synapse. If a feature, setting, or behavior is not mentioned here, the upstream documentation is accurate and fully applicable.

Synapse is the battle-tested, reference implementation of the Matrix protocol -- a next-generation, federated, full-featured, encrypted, independent messaging system.


Table of Contents


Image and Container Runtime

Property Value
Synapse image ghcr.io/element-hq/synapse (upstream pre-built image)
Nginx image nginx Alpine (upstream unmodified)
PostgreSQL image postgres Alpine (database sidecar)
Architectures x86_64, aarch64

Synapse runs behind an Nginx reverse proxy. Nginx handles client requests on port 80, proxies Matrix API traffic to Synapse on port 8008, and serves the Synapse Admin dashboard on port 8080.


Volume and Data Layout

Volume Mount Point Purpose
main /data Synapse config, media, keys, appservices, StartOS state
db /var/lib/postgresql PostgreSQL data

Key files on the main volume:

  • homeserver.yaml -- primary Synapse configuration (managed by StartOS)
  • homeserver.signing.key -- server signing key (auto-generated by Synapse)
  • homeserver.log.config -- logging configuration
  • media_store/ -- uploaded media files
  • store.json -- StartOS metadata (admin state, postgres password, SMTP selection)
  • appservices/*.yaml -- registered bridge/appservice configurations

Installation and First-Run Flow

Step Upstream StartOS
Generate config python -m synapse.app.homeserver --generate-config Automatic via setupOnInit
Set server name Edit homeserver.yaml "Set Server Address/URL" action (critical task)
Create admin user register_new_matrix_user CLI "Set Admin Password" action (critical task) -- generates a password; the actual user is registered when the service first starts

Key difference: On first install, StartOS generates the initial Synapse config automatically. You must run the "Set Server Address/URL" action (created as a critical task) to choose your permanent domain before starting. Completing it surfaces "Set Admin Password" as a second critical task; running that generates and shows the credentials. Starting the service then registers the admin user with the chosen password.

Warning: The server address/URL is permanent and cannot be changed after the first start.


Configuration Management

Setting Upstream Method StartOS Method
server_name homeserver.yaml "Set Server Address/URL" action (one-time)
enable_registration homeserver.yaml "Config" action
Federation homeserver.yaml listeners "Config" action (enable/disable + domain whitelist)
max_upload_size homeserver.yaml "Config" action (1-2000 MB)
SMTP/email homeserver.yaml "Configure SMTP" action (disabled/system/custom)
Admin password register_new_matrix_user "Set Admin Password" action
Appservices Manual YAML files Register/List/Delete Appservice actions

Configuration NOT exposed on StartOS:

  • log_config -- fixed logging configuration
  • database -- always PostgreSQL via sidecar container
  • trusted_key_servers -- defaults to matrix.org
  • report_stats -- always disabled
  • Listener bind addresses and ports

Network Access and Interfaces

Interface Port Protocol Type Purpose
Homeserver 80 (nginx) HTTP API Matrix client and federation API
Admin Dashboard 8080 (nginx) HTTP UI Synapse Admin web interface

Internally, Synapse listens on port 8008. Nginx proxies traffic from port 80, handles .well-known/matrix/server responses, and enforces max_upload_size on Matrix API routes.


Actions (StartOS UI)

Set Server Address/URL

Property Value
ID set-server-name
Visibility Hidden after first start
Availability Only when stopped
Purpose Choose permanent server domain (clearnet or Tor)

Presents available hostnames from the homeserver interface. Sets server_name and public_baseurl in homeserver.yaml. Cannot be changed after first start.

Set Admin Password

Property Value
ID set-admin-password
Visibility Enabled
Availability Any status
Purpose Set or reset the admin account password

Generates a random 22-character password, stores it in store.json as pendingAdminPassword, displays it, and calls sdk.restart. Applying the password needs both the Synapse image (for hash_password) and a running PostgreSQL, so the work happens at startup, not in the action: an apply-admin-password oneshot in the daemon chain consumes the pending password on the next start — if no users exist in PostgreSQL it runs register_new_matrix_user to create the admin; otherwise it hashes the password with hash_password and UPDATEs the first-registered user's password_hash. The pending field is cleared on success.

The restart is what makes the new password take effect immediately. pendingAdminPassword is read in main with .once(), not .const(), because the oneshot clears it — a watch on that field would restart main on its own write. On a stopped service sdk.restart is a no-op and the password is applied at the next start, which is why the action allows any status.

Config

Property Value
ID config
Visibility Enabled
Availability Any status
Purpose Configure registration, federation, upload limits

Settings:

Setting Default Description
Registration Disabled Allow public account creation
Federation Disabled Enable/disable with optional domain whitelist
Max Upload Size 50 MB File upload limit (1-2000 MB)

All three write homeserver.yaml, which main holds a .const() watch on, so the service restarts itself to pick them up.

Configure SMTP

Property Value
ID manage-smtp
Visibility Enabled
Availability Any status
Purpose Email notifications (disabled/system/custom)

The standard three-mode SMTP flow (see the packaging guide's Set Up SMTP / Email recipe). The action only writes the selection to store.json; main watches that field with .const() and renders the credentials into homeserver.yaml's email block, so changing SMTP restarts the service. When the selection is system, main also watches sdk.getSystemSmtp with .const(), so a later change to StartOS's own SMTP settings restarts Synapse with the new credentials.

Selecting Disabled writes email: null, which Synapse reads as an absent key (EmailConfig.read_config). main renders the email block before it const-reads homeserver.yaml, so the write is not a write-after-const.

System SMTP is snapshotted at action time. If StartOS's own SMTP settings change later, re-run this action to pick them up. (Several packages instead resolve getSystemSmtp in main with a .const() watch, which follows host changes live — that isn't available here without reintroducing the write-after-const problem above.)

Register Appservice

Property Value
ID register-appservice
Visibility Enabled
Availability Any status
Purpose Register a Matrix bridge with the homeserver

Accepts appservice credentials (ID, tokens, URL, namespace regex) and writes the registration YAML. Typically triggered automatically by bridge services via the exported ensureAppserviceRegistration API.

List Appservices

Property Value
ID list-appservices
Visibility Enabled
Availability Any status
Purpose View all registered bridges

Delete Appservice

Property Value
ID delete-appservice
Visibility Enabled
Availability Any status
Purpose Remove a registered bridge

Backups and Restore

Included in backup:

  • main volume -- Synapse config, media, keys, appservice registrations, StartOS state
  • db volume -- PostgreSQL database (backed up via pg_dump, restored via pg_restore)

Restore behavior:

  • All data, users, rooms, and settings are restored
  • Server name, keys, and admin credentials remain the same

Health Checks

Check Method Grace Period Display
Database pg_isready Default "Database"
Homeserver HTTP GET http://localhost:8008/health 15 seconds "Homeserver"
Nginx Port listening on 80 Default Hidden
Admin Dashboard HTTP GET http://localhost:8080 Default "Admin Dashboard"

Dependencies

None.


Limitations and Differences

  1. Server name is permanent -- once set and started, the server address/URL cannot be changed
  2. Admin username fixed -- always admin; only password can be changed
  3. No workers -- runs as a single monolith process (no worker-based scaling)
  4. Fixed logging -- log configuration is not user-configurable (INFO level, 100 MB rotation)
  5. No direct homeserver.yaml editing -- configuration is managed through StartOS actions
  6. Tor federation limitations -- .onion servers can only federate with other .onion servers

What Is Unchanged from Upstream

  • Full Matrix protocol compliance (client-server and server-server APIs)
  • End-to-end encryption support
  • Federation (when enabled)
  • Room creation, membership, and permissions
  • Media uploads and downloads
  • Push notifications
  • Account data and device management
  • Presence and typing indicators
  • All Matrix client compatibility (Element, FluffyChat, etc.)

Contributing

Build and development workflow follow the StartOS packaging guide: https://docs.start9.com/packaging. Keep README.md, instructions.md, and AGENTS.md in sync with any change to user-visible behavior or package structure.


Quick Reference for AI Consumers

package_id: synapse
images:
  synapse: dockerTag (ghcr.io/element-hq/synapse)
  nginx: nginx (Alpine)
  postgres: postgres (Alpine)
architectures: [x86_64, aarch64]
volumes:
  main: /data
  db: /var/lib/postgresql
ports:
  homeserver: 80 (nginx proxy to synapse:8008)
  admin: 8080 (synapse-admin dashboard)
dependencies: none
database: PostgreSQL (sidecar, localhost-only, password auth)
startos_managed_config:
  - server_name (one-time, permanent)
  - enable_registration
  - federation (listeners + domain whitelist)
  - max_upload_size
  - smtp
actions:
  - set-server-name (enabled/hidden, only-stopped)
  - set-admin-password (enabled, any)
  - config (enabled, any)
  - manage-smtp (enabled, any)
  - register-appservice (enabled, any)
  - list-appservices (enabled, any)
  - delete-appservice (enabled, any)
health_checks:
  - pg_isready (postgres)
  - http_get: /health (port 8008, 15s grace)
  - port_listening: 80
  - http_get: / (port 8080)
backup_method: pg_dump + main volume
public_api:
  - ensureAppserviceRegistration (for bridge services)

Releases

Used by

Contributors

Languages