matrix.org chat is split into two parts, the server and the client. The server we are going to use is called Synapse and the client is Riot.im. The Synapse will also need Postgres database and Redis for caching.
Make sure your folder structure looks like this.
example/
data/
postgres/
data/
(empty)
traefik/
(empty)
matrix/
nginx/
(empty)
synapse/
(empty)
riot/
(empty)
docker-compose.yml
You can generate them via:
mkdir -p data/postgres/data
mkdir -p data/traefik
mkdir -p data/matrix/{nginx,synapse,riot}
touch docker-compose.yml
Create your basic Traefik configuration. The following should work out of the box, if you change your email in the ACME section.
# data/traefik/traefik.yml
entryPoints:
web:
address: ":80"
web-secure:
address: ":443"
api:
dashboard: true
insecure: true
providers:
file:
directory: "/config"
watch: true
docker:
endpoint: "unix:///var/run/docker.sock"
network: "example_default"
watch: true
exposedByDefault: false
certificatesResolvers:
letsencrypt:
acme:
email: "your_name@example.com"
storage: "/acme.json"
httpChallenge:
entryPoint: "web"The following additional Traefik config is useful for http to https automatic redirection.
# data/traefik/config/routers.yml
http:
routers:
redirecttohttps:
entryPoints:
- "web"
middlewares:
- "httpsredirect"
rule: "HostRegexp(`{host:.+}`)"
service: "noop@internal"# data/traefik/config/middlewares.yml
http:
middlewares:
httpsredirect:
redirectScheme:
scheme: https
permanent: trueMake sure you have a postgres database with a login for the Synapse server.
The following is a sample docker-compose config for Postgres.
# docker-compose.yml
services:
traefik:
...
postgres:
image: "postgres:9.6"
restart: "unless-stopped"
environment:
POSTGRES_PASSWORD: "admin"
volumes:
- "./data/postgres/data:/var/lib/postgresql/data"The following code will create a database synapse with user synapse and password password. Make sure that your database has the correct encoding!
CREATE ROLE synapse;
ALTER ROLE synapse WITH PASSWORD 'password';
ALTER ROLE synapse WITH LOGIN;
CREATE DATABASE synapse ENCODING 'UTF8' LC_COLLATE='C' LC_CTYPE='C' template=template0 OWNER synapse;
GRANT ALL PRIVILEGES ON DATABASE synapse TO synapse;Make sure you have redis running. Example docker-compose configuration:
# docker-compose.yml
services:
traefik:
...
postgres:
...
redis:
image: "redis:latest"
restart: "unless-stopped"Note: You will have two sub-domains for Synapse via Traefik. One matrix.example.com which will be handled by Nginx and a second one synapse.example.com which will point directly into your Synapse server. It will be explained in a later section below.
First create a sample config. This will also create your signing key and bunch of other secrets.
docker run -it --rm \
-v $(pwd)/data/matrix/synapse:/data \
-e SYNAPSE_SERVER_NAME=matrix.example.com \
-e SYNAPSE_REPORT_STATS=yes \
-e UID=1000 \
-e GID=1000 \
matrixdotorg/synapse:latest generate
This will create the following files:
example/
data/
...
matrix/
nginx/
(empty)
synapse/
homeserver.yaml
matrix.example.com.log.config
matrix.example.com.signing.key
riot/
(empty)
Edit the homeserver.yaml so you end up with something like the config below. (notice the enable_registration!)
# data/matrix/synapse/homeserver.yaml
server_name: "matrix.example.com"
pid_file: /data/homeserver.pid
web_client_location: https://riot.example.com/
public_baseurl: https://synapse.example.com/
report_stats: true
enable_registration: true
listeners:
- port: 8008
tls: false
type: http
x_forwarded: true
resources:
- names: [client, federation]
compress: false
retention:
enabled: true
federation_ip_range_blacklist:
- '127.0.0.0/8'
- '10.0.0.0/8'
- '172.16.0.0/12'
- '192.168.0.0/16'
- '100.64.0.0/10'
- '169.254.0.0/16'
- '::1/128'
- 'fe80::/64'
- 'fc00::/7'
database:
name: psycopg2
args:
user: synapse
password: password
database: synapse
host: postgres
cp_min: 5
cp_max: 10
log_config: "/data/matrix.example.com.log.config"
media_store_path: "/data/media_store"
registration_shared_secret: "abc"
macaroon_secret_key: "abc"
form_secret: "abc"
signing_key_path: "/data/matrix.example.com.signing.key"
trusted_key_servers:
- server_name: "matrix.org"
redis:
enabled: true
host: redis
port: 6379
Now add your docker-compose config for Synapse:
# docker-compose.yml
services:
traefik:
...
postgres:
...
redis:
...
synapse:
image: "matrixdotorg/synapse:latest"
restart: "unless-stopped"
environment:
SYNAPSE_CONFIG_DIR: "/data"
SYNAPSE_CONFIG_PATH: "/data/homeserver.yaml"
UID: "1000"
GID: "1000"
TZ: "Europe/London"
volumes:
- "./data/matrix/synapse:/data"
labels:
- "traefik.enable=true"
- "traefik.http.services.synapse.loadbalancer.server.port=8008"
- "traefik.http.routers.synapse.rule=Host(`synapse.example.com`)"
- "traefik.http.routers.synapse.entrypoints=web-secure"
- "traefik.http.routers.synapse.tls.certresolver=letsencrypt"Now when you go to https://synapse.example.com/ you should be redirected to https://synapse.example.com/_matrix/static/ and you should see It works! Synapse is running.
So the Synapse server is running, but this is not everything you need for federation. You may have noticed that we have used two domains: synapse.example.com and matrix.example.com.
The federation documentation is bit configusing if you don't know enough about how Matrix works internally. Hopefully this example below will explain it.
Create the following files:
The Nginx configuration file at data/matrix/nginx/matrix.conf relative to your docker-compose.yml with the following contents:
server {
listen 80 default_server;
server_name matrix.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 *;
}
}
The data/matrix/nginx/www/.well-known/matrix/client with the following contents:
{
"m.homeserver": {
"base_url": "https://matrix.example.com"
}
}
And the data/matrix/nginx/www/.well-known/matrix/server with the following contents:
{
"m.server": "synapse.example.com:443"
}
And the docker-compose config for Nginx:
# docker-compose.yml
services:
traefik:
...
postgres:
...
redis:
...
synapse:
...
nginx:
image: "nginx:latest"
restart: "unless-stopped"
volumes:
- "./data/matrix/nginx/matrix.conf:/etc/nginx/conf.d/matrix.conf"
- ./data/matrix/nginx/www:/var/www/
labels:
- "traefik.enable=true"
- "traefik.http.services.matrix.loadbalancer.server.port=80"
- "traefik.http.routers.matrix.rule=Host(`matrix.example.com`)"
- "traefik.http.routers.matrix.entrypoints=web-secure"
- "traefik.http.routers.matrix.tls.certresolver=letsencrypt"After you start the container, you should be able to access https://matrix.example.com/.well-known/matrix/client which gives you "base_url": "https://matrix.example.com".
And also you should be able to access https://matrix.example.com/.well-known/matrix/server which gives you "m.server": "synapse.example.com:443".
And also accessing https://matrix.example.com/_matrix/static/ should show you the exact same page as shown at https://synapse.example.com/_matrix/static/.
The Nginx simply acts as a proxy in this case, with two additional files.
Now, when you go to https://federationtester.matrix.org/ and type in your domain name, in this case matrix.example.com (without https) you should get all green checks.
The last thing is to set up the Riot Web UI which will be used to send the messages.
Create a new file at data/matrix/riot/config.json with the following contents:
{
"default_server_config": {
"m.homeserver": {
"base_url": "https://matrix.example.com",
"server_name": "matrix.example.com"
},
"m.identity_server": {
"base_url": "https://vector.im"
}
},
"disable_custom_urls": false,
"disable_guests": false,
"disable_login_language_selector": false,
"disable_3pid_login": false,
"brand": "Element",
"integrations_ui_url": "https://scalar.vector.im/",
"integrations_rest_url": "https://scalar.vector.im/api",
"integrations_widgets_urls": [
"https://scalar.vector.im/_matrix/integrations/v1",
"https://scalar.vector.im/api",
"https://scalar-staging.vector.im/_matrix/integrations/v1",
"https://scalar-staging.vector.im/api",
"https://scalar-staging.riot.im/scalar/api"
],
"bug_report_endpoint_url": "https://riot.im/bugreports/submit",
"defaultCountryCode": "GB",
"showLabsSettings": false,
"features": {
"feature_new_spinner": "labs",
"feature_pinning": "labs",
"feature_custom_status": "labs",
"feature_custom_tags": "labs",
"feature_state_counters": "labs"
},
"default_federate": true,
"default_theme": "light",
"roomDirectory": {
"servers": [
"matrix.org"
]
},
"welcomeUserId": "@riot-bot:matrix.org",
"piwik": {
"url": "https://piwik.riot.im/",
"whitelistedHSUrls": [
"https://matrix.org"
],
"whitelistedISUrls": [
"https://vector.im",
"https://matrix.org"
],
"siteId": 1
},
"enable_presence_by_hs_url": {
"https://matrix.org": false,
"https://matrix-client.matrix.org": false
},
"settingDefaults": {
"breadcrumbs": true
},
"jitsi": {
"preferredDomain": "jitsi.riot.im"
}
}And add Riot to your docker-compose file:
# docker-compose.yml
services:
traefik:
...
postgres:
...
redis:
...
synapse:
...
nginx:
...
riot:
image: "bubuntux/riot-web:latest"
volumes:
- "./data/matrix/riot/config.json:/etc/riot-web/config.json:ro"
labels:
- "traefik.enable=true"
- "traefik.http.services.riot.loadbalancer.server.port=80"
- "traefik.http.routers.riot.rule=Host(`riot.example.com`)"
- "traefik.http.routers.riot.entrypoints=web-secure"
- "traefik.http.routers.riot.tls.certresolver=letsencrypt"Go to https://riot.example.com and click register and select "Advanced" and enter the Homeserver URL as https://matrix.example.com (NOT https://synapse.example.com!).
After creating an account, simply log in. That's it.
Navigate to https://riot.example.com/#/room/#hello-matrix:matrix.org This will join you to #hello-matrix:matrix.org room on <matrix.org>.
If you get 401 unauthorized error in your logs (Synapse logs), check the SVR DNS record below.
Note: This may not be needed!
Add the following SVR record to your DNS server:
_matrix._tcp.matrix.synapse.example.com
With value of:
1 10 443 synapse.example.com
After it is done, you should be able to test if it is correct via dig or similar CLI tool:
dig -t SRV _matrix._tcp.matrix.example.com @8.8.8.8
And should print the following:
;; ANSWER SECTION:
_matrix._tcp.matrix.example.com. 299 IN SRV 1 10 443 synapse.example.com.
It is possible to integrate Synapse with OpenLDAP. In this case you don't need to create any user from the Riot Web UI. All authentication will be handled by OpenLDAP server.
Add this into your homeserver.yml file:
password_providers:
- module: "ldap_auth_provider.LdapAuthProvider"
config:
enabled: true
uri: "ldap://openldap:389"
start_tls: false
base: "ou=users,dc=example,dc=com"
attributes:
uid: "uid"
mail: "email"
name: "cn"
bind_dn: cn=admin,dc=example,dc=com
bind_password: password
filter: "(memberOf=cn=matrix,ou=groups,dc=example,dc=com)"Assuming that:
- Your OpenLDAP server can be accessed via Docker network at
openldap:389. - You have domain of
dc=example,dc=com. - You have admin (or any user for bind)
cn=admin,dc=example,dc=com. - You have an organization unit of users at
ou=users,dc=example,dc=com. - You have a
groupOfUniqueNamesascn=matrix,ou=example,dc=example,dc=com. - Your users are added into the group
cn=matrixso they have a propertymemberOf.
I recommend using OpenLDAP as the following:
services:
openldap:
image: "osixia/openldap:latest"
restart: "unless-stopped"
environment:
LDAP_ORGANISATION: "Homelab"
LDAP_DOMAIN: "dc=example,dc=com"
LDAP_ADMIN_PASSWORD: "password"
LDAP_REMOVE_CONFIG_AFTER_SETUP: "false"
volumes:
- "./data/ldap/data:/var/lib/ldap"
- "./data/ldap/config:/etc/ldap/slapd.d"In the end, your files should look like this:
example/
data/
postgres/
data/
... stuff ...
traefik/
config/
middlewares.yml
routers.yml
traefik.yml
acme.json
matrix/
nginx/
www/
.well-known/
matrix/
server
client
matrix.conf
synapse/
media_store/
... stuff ...
homeserver.yml
matrix.example.com.log.config
matrix.example.com.signing.key
riot/
config.json
docker-compose.yml
@TekExplorer
What happens when you do not use a subdomain? Also, do you mean telling users to use a subdomain for Riot Web?