Matrix homeserver (Synapse v1.0.0) with Traefik
Tags: matrix, element, traefik, docker
Published: 2019-02-22 18:33:53

---
title: Matrix homeserver (Synapse v1.0.0) with Traefik
published: true
tags: matrix,element,traefik,docker
canonical_url: https://jonnev.se/matrix-homeserver-synapse-v0-99-1-1-with-traefik/
---

 ![Matrix homeserver (Synapse v0.99.1.1) with Traefik](https://jonnev.se/content/images/2019/02/create-matrix-account-2.jpg)

### 🚀 Update 🚀

Synapse `v1.0.0` is now [released](https://matrix.org/blog/2019/06/11/synapse-1-0-0-released)! I have upgraded my server without any problems and therefore updated the guide with the latest version.

<hr>

So our good friends at matrix.org [released](https://matrix.org/blog/2019/02/14/synapse-0-99-1-1-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](https://github.com/matrix-org/synapse/blob/master/docs/MSC1711_certificates_FAQ.md#configuring-certificates-for-compatibility-with-synapse-100) 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.

## Prerequisites

 If you **already have** a server with Traefik running then continue. Otherwise I recommend you read my [previous post](https://jonnev.se/traefik-with-docker-and-lets-encrypt/) and you'll be back here soon.

## Setup

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

```bash
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

```bash
docker network create matrix
```

### docker-compose.yml

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](https://hub.docker.com/r/avhost/docker-matrix) image but it should be possible to use the [official](https://hub.docker.com/r/matrixdotorg/synapse/) one too I'm sure.

```bash
nano /opt/matrix/docker-compose.yml
```

```yml
version: "2"
services:
  postgres:
    image: postgres:9.6.4
    restart: unless-stopped
    networks:
      - default
    volumes:
     - /opt/matrix/pgdata:/var/lib/postgresql/data
    environment:
     - POSTGRES_PASSWORD=SECRET
     - POSTGRES_USER=synapse
    labels:
      - "traefik.enable=false"

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

networks:
  default:
    external:
      name: matrix
  # Traefiks network
  web:
    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.


### Nginx

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.
```bash
mkdir -p /opt/matrix/nginx/www
```

Then we need a [.well-known](https://ma.ttias.be/well-known-directory-webservers-aka-rfc-5785/) directory to put files in, for both our server and for clients. 

```bash
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`.

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

Here you place a JSON formatted file.

```json
{
  "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](https://jonnev.se/riot-web-for-matrix-with-docker-and-traefik/) configured by only using your `username` and not full MXID (`@username:example.com`).

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

Another JSON formatted file.

```json
{
  "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`!

```bash
nano /opt/matrix/nginx/matrix.conf
```

```nginx
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`.

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

networks:
    # ...
```

### 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.
```bash
docker run -v /opt/matrix/synapse:/data --rm \
  -e SERVER_NAME=example.com \
  -e REPORT_STATS=yes \
  avhost/docker-matrix:v0.99.1.1 \
  generate
```

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

We then need to edit some stuff.

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

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

```yaml
## 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`).

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

    resources:
      - names: [client, federation]
        compress: false
```

We change from `sqlite` to `postgres`.

```yaml
# Database configuration
database:
  name: psycopg2
  args:
    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.

```yaml
# Enable registration for new users.
enable_registration: True
```

That should be it. I do recommend reading through the config and perhaps another [guide](https://matrix.org/docs/guides/) to checkout all the options but for now let's try and start up our services...

```bash
cd /opt/matrix
docker-compose up -d
docker-compose ps
```
... and you should get something like this.
```bash
      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      0.0.0.0:3478->3478/tcp, 0.0.0.0:5349->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](https://riot.im/app) to create an account and login. You need to select "Advanced" and then change Homeserver URL to your own.

![create-matrix-account-1](https://jonnev.se/content/images/2019/02/create-matrix-account-1.jpg)

## Final notes

### SRV Record
You should also add a [SRV record](https://github.com/matrix-org/synapse/blob/master/docs/MSC1711_certificates_FAQ.md#if-you-do-have-an-srv-record-currently) to support older homeservers. 

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

### Troubleshooting

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](https://matrix.org/federationtester/) is a good first place to check. If you want to see the actual data from the tester, use [this link](https://matrix.org/federationtester/api/report?server_name=matrix.org) instead but replace the hostname.

### Hosting Riot

If you want your own Riot you can use this [guide](https://jonnev.se/riot-web-for-matrix-with-docker-and-traefik/) 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/`).

### Thanks!

If you have any questions or feedback please comment below or hit me up on [Twitter](https://twitter.com/jonnever) 🙌