🚀 Update 🚀

Synapse v1.0.0 is now released! I have upgraded my server without any problems and therefore updated the guide with the latest version.

So our good friends at matrix.org released a v0.99.1.1 of Synapse as a precursor for a much anticipated 1.0 release. A big change in the upcoming release is that federation between servers will now require a proper TLS certificate and the current self signed cert that Synapse provides won't work. Homeserver owners are advised to make that change as soon as possible, as v1 is supposed to be released in March.

I've been running my Synapse behind Traefik for quite some time now but never bothered to replace the certificate for federation, although I have proper TLS on the client side. Once I figured out the different options it was pretty straight forward to find one that worked with Traefik.

In this guide we are going to put both the client side and federation of Synapse on a non-subdomain - ie example.com - and use port 443 for both, so no 8448 for federation. If you have another setup - for example matrix.example.com - it should be possible with some adjustments.

We'll achive this by using both a SRV record and a .well-known/matrix/server file to delegate federation to example.com. Synapse itself will be available on synapse.example.com.

I'm also using postgres as the database for Synapse.


If you already have a server with Traefik running then continue. Otherwise I recommend you read my previous post and you'll be back here soon.

Also if you want to set up your own Matrix server at Digital Ocean feel free to use my referral link to get $100 for your server or my Hetzner link to get €20 😊 The referrals keep this blog going!


Let's make a directory for your data to live in, as sudo if needed.

mkdir /opt/matrix
# If you had to use sudo, set permissons
sudo chown -R $USER:$GROUP /opt/matrix

Then we need an internal docker network for the services to communicate on

docker network create matrix


We'll put our services in a docker-compose file to be able to easily manage things. We'll start with Synapse. I'm using the avhost/docker-matrix image but it should be possible to use the official one too I'm sure.

nano /opt/matrix/docker-compose.yml
version: "2"
    image: postgres:9.6.4
    restart: unless-stopped
      - default
     - /opt/matrix/pgdata:/var/lib/postgresql/data
     - POSTGRES_USER=synapse
      - "traefik.enable=false"

    image: avhost/docker-matrix:v1.0.0
    restart: unless-stopped
      - postgres
      - web
      - default
     # Coturn
     - "3478:3478"
     - "5349:5349"
     - /opt/matrix/synapse:/data
      - "traefik.backend=synapse"
      - "traefik.enable=true"
      - "traefik.frontend=true"
      - "traefik.port=8008"
      - "traefik.frontend.rule=Host:synapse.example.com"
      - "traefik.docker.network=web"

      name: matrix
  # Traefiks network
    external: true

☝️ Remember to replace example.com in traefik.frontend.rule. That is where we do the reverse proxy for servers. Also change the POSTGRES_PASSWORD env.


To be able to serve the .well-known path on our domain we need Nginx. We're also going to use nginx to proxy clients to Synapse. With this setup you have the options to also serve static files on your domain, like example.com/some-site.

Let's make a directory for nginx with a subdirectory for www.

mkdir -p /opt/matrix/nginx/www

Then we need a .well-known directory to put files in, for both our server and for clients.

mkdir -p /opt/matrix/nginx/www/.well-known/matrix

We start with the server file. This will take care of proxying federation to your subdomain so you'll be able to have usernames in Matrix like @me:example.com, while actually hosting Synapse at synapse.example.com.

nano /opt/matrix/nginx/www/.well-known/matrix/server

Here you place a JSON formatted file.

  "m.server": "synapse.example.com:443"

☝️ Replace example.com above and make sure to NOT include https or anything like that.

Next up is a client file, which makes it possible to sign in to your Riot with custom domain configured by only using your username and not full MXID (@username:example.com).

nano /opt/matrix/nginx/www/.well-known/matrix/client

Another JSON formatted file.

  "m.homeserver": {
    "base_url": "https://example.com"

☝️ Here you include the full URL. You can also add m.identity_server if you have your own.

Now we need a config file for nginx!

nano /opt/matrix/nginx/matrix.conf
server {
  listen         80 default_server;
  server_name    example.com;

 # Traefik -> nginx -> synapse
 location /_matrix {
    proxy_pass http://synapse:8008;
    proxy_set_header X-Forwarded-For $remote_addr;
    client_max_body_size 128m;

  location /.well-known/matrix/ {
    root /var/www/;
    default_type application/json;
    add_header Access-Control-Allow-Origin  *;

Now we can add nginx to our docker-compose.yml.

version: "2"
    # ...
    # ...
  # Add this
    image: nginx:1.12-alpine
    restart: unless-stopped
      - web
      - default
      - "traefik.enable=true"
      - "traefik.frontend.rule=Host:example.com"
      - "traefik.frontend.passHostHeader=true"
      - "traefik.port=80"
      - "traefik.docker.network=web"
      - ./nginx/matrix.conf:/etc/nginx/conf.d/matrix.conf
      - ./nginx/www:/var/www/

    # ...

Synapse config

Now we are ready to configure our Synapse so we can get it up and running.

This will create a homeserver.yaml in our synapse directory.

docker run -v /opt/matrix/synapse:/data --rm \
  -e SERVER_NAME=example.com \
  -e REPORT_STATS=yes \
  avhost/docker-matrix:v1.0.0 \

☝️ Replace example.com with your "short" domain, no subdomains.

We then need to edit some stuff.

sudo nano /opt/matrix/synapse/homeserver.yaml

This is not a complete configuration file, just the parts that needs editing.

## Server ##

server_name: "example.com"

☝️ Since we are using a proxy for federation we only use the "short" domain here, without the subdomain (synapse.example.com).

  - port: 8008
    tls: false
    # Since it's running in a container we need to listen to
    # The port is only exposed on the host and put behind reverse proxy
    bind_addresses: ['']
    # Previous
    # bind_addresses: ['::1', '']
    type: http
    x_forwarded: true

      - names: [client, federation]
        compress: false

We change from sqlite to postgres.

# Database configuration
  name: psycopg2
    user: synapse
    # From your docker-compose.yml
    password: YOUR_PASSWORD_HERE
    database: synapse

    # This hostname is accessible through the docker network and is set 
    # by docker-compose. If you change the name of the service it will be different
    host: postgres

We'll enable registration to be able to test. You can change this later on.

# Enable registration for new users.
enable_registration: True

That should be it. I do recommend reading through the config and perhaps another guide to checkout all the options but for now let's try and start up our services...

cd /opt/matrix
docker-compose up -d
docker-compose ps

... and you should get something like this.

      Name                     Command              State                            Ports
matrix_nginx_1      nginx -g daemon off;            Up      80/tcp
matrix_postgres_1   docker-entrypoint.sh postgres   Up      5432/tcp
matrix_synapse_1    /start.sh autostart             Up>3478/tcp,>5349/tcp, 8448/tcp

If the services are not up have a look at the logs with docker-compose logs (start with docker-compose logs synapse if the wall of text is too much).

Register account and login

If everything looks good, you can now use Riot to create an account and login. You need to select "Advanced" and then change Homeserver URL to your own.


Final notes

SRV Record

You should also add a SRV record to support older homeservers.

_matrix._tcp.example.com. IN SRV 10 5 443 synapse.example.com.


If it seems like people on other homeservers can't see your messages, or the other way around, your federation might be broken. Matrid Federation Tester is a good first place to check. If you want to see the actual data from the tester, use this link instead but replace the hostname.

Hosting Riot

If you want your own Riot you can use this guide I've written. It should also possible be to add Riot to the setup above with some modifications to your docker-compose.yml (add riot.example.com to traefik.frontend.rule) and your nginx.conf (add a new location and directory to nginx/www/).


If you have any questions or feedback please comment below or hit me up on Twitter 🙌