Synapse Server Setup (Docker + Caddy)

This guide walks you through bringing up a Matrix Synapse homeserver behind Caddy with automatic HTTPS. It uses:

  • Domain: chat.fahadalisarwar.com (replace with your own where noted)
  • Docker volume for persistent data: matrix_synapse_data
  • Caddy as the reverse proxy/terminating TLS

⚠️ Security note: Enabling open registration is convenient for first-time setup, but you should disable it after creating your admin/user accounts.


0) Prerequisites

  • A server with Docker and Docker Compose installed
  • A DNS A/AAAA record for chat.yourdomain.com pointing to your server
  • Caddy running on the same host (or reachable reverse proxy) with ports 80 and 443 open

1) Create the Synapse data volume & generate the config

This creates a named Docker volume and generates the initial Synapse config into it.

# Create and populate /data inside the volume
docker run -it --rm \
  --mount type=volume,src=matrix_synapse_data,dst=/data \
  -e SYNAPSE_SERVER_NAME=chat.yourdomain.com \
  -e SYNAPSE_REPORT_STATS=yes \
  matrixdotorg/synapse:latest generate

This will create homeserver.yaml and other files inside the volume, typically visible on the host at:

/var/lib/docker/volumes/matrix_synapse_data/_data

If your system shows homeserver.yml (one l) keep that naming consistent—Synapse accepts either; just use the generated filename everywhere below.


2) Enable (temporary) user registration

Edit the generated config file:

/var/lib/docker/volumes/matrix_synapse_data/_data/homeserver.yaml

Add (or set) the following:

enable_registration: true
enable_registration_without_verification: true

Later, once you’ve created your users (and admin), revert these to false.


3) Minimal docker-compose.yml

Create a docker-compose.yml next to your deployment files:

services:
  synapse:
    image: matrixdotorg/synapse:latest
    container_name: synapse
    restart: unless-stopped
    volumes:
      - matrix_synapse_data:/data
    expose:
      - "8008"  # HTTP (Caddy will reverse_proxy to this)
    # If you prefer to bind 8008 to host (not required when using Caddy on same Docker network):
    # ports:
    #   - "127.0.0.1:8008:8008"
    # networks:
    #   - web  # put Caddy and Synapse on the same user-defined network

volumes:
  matrix_synapse_data:
    external: true
    name: matrix_synapse_data

# networks:
#   web:
#     external: true

Bring it up:

docker compose up -d

4) Caddy site config

Point your domain to the Synapse container through Caddy. Example site block:

chat.yourdomain.com {
  encode zstd gzip

  # CORS for mobile apps fetching .well-known
  header /.well-known/matrix/* Access-Control-Allow-Origin "*"
  header /.well-known/matrix/* Content-Type "application/json"

  # Federation discovery (if you terminate TLS on 443 at the proxy)
  @server path /.well-known/matrix/server
  respond @server `{"m.server": "chat.yourdomain.com:443"}` 200

  # Client discovery: where apps should talk to your homeserver
  @client path /.well-known/matrix/client
  respond @client `{
    "m.homeserver": { "base_url": "https://chat.yourdomain.com" }
  }` 200

  # Proxy actual Matrix endpoints to Synapse
  reverse_proxy /_matrix/* synapse:8008
  reverse_proxy /_synapse/client/* synapse:8008
}

Make sure Caddy can resolve synapse (the container name). If Caddy runs in Docker, put both Caddy and Synapse on the same Docker network (e.g., web) and use synapse:8008 as above. If Caddy runs on the host, either publish port 8008 on the synapse container or reverse proxy to localhost:8008 accordingly.

Reload Caddy to apply the config.


5) Quick validation

  • Check well-known endpoints (adjust domain if changed):

    curl -s https://chat.yourdomain.com/.well-known/matrix/server | jq
    curl -s https://chat.yourdomain.com/.well-known/matrix/client | jq
    
  • Check Synapse is serving via the proxy:

    curl -I https://chat.yourdomain.com/_matrix/client/versions
    

    You should get HTTP/2 200.


6) Create users (and an admin)

While registration is enabled, you can sign up from a Matrix client (e.g., Element). Alternatively, create an admin from the container:

# Create a user interactively (set password when prompted)
docker exec -it synapse register_new_matrix_user -c /data/homeserver.yaml http://localhost:8008
# To make the user an admin, add: --admin

When done, disable open registration by reverting:

enable_registration: false
enable_registration_without_verification: false

Restart Synapse if you changed the config:

docker compose restart synapse

7) Upgrades & maintenance

  • Update Synapse:

    docker compose pull synapse && docker compose up -d
    
  • Backups: snapshot/backup the matrix_synapse_data volume regularly.

  • Logs:

    docker logs -f synapse
    

8) Common pitfalls

  • Caddy can’t reach synapse: ensure both are on a shared network, or publish port 8008 to the host and update the upstream.
  • Wrong domain in SYNAPSE_SERVER_NAME: regenerate config or edit homeserver.yaml to match your public domain exactly.
  • Federation issues: confirm /.well-known/matrix/server returns your domain with :443, and that 443 is reachable from the internet.

9) Appendix: Change domain

If you use a different domain, replace every instance of chat.yourdomain.com in this README and commands. Re-run the config generation step (or carefully edit homeserver.yaml) to match the new domain.