Guide to deploy Matrix Synapse with Coturn using Docker Compose and expose it via a VPS using Pangolin

A step-by-step guide to deploy Matrix Synapse with Coturn using Docker Compose, and expose it via a VPS using Pangolin. I’ll provide all necessary configuration files and detailed instructions to ensure a perfect deployment with proper federation and voice/video capabilities.



A Big thank you to the community member @cashford this was possible because of the member help only

Prerequisites

  • A VPS with Docker and Docker Compose installed.
  • A local machine with Docker and Docker Compose installed.
  • Access to the Pangolin dashboard for tunneling setup.
  • A domain name or subdomain provided by Pangolin (e.g., matrix.example.com).

Step 1: Set Up the Matrix Stack Locally

We’ll create a Docker Compose setup for Matrix Synapse and Coturn on your local machine.

1.1 Create Directory Structure

Create a directory for your Matrix setup and navigate into it:

mkdir matrix-deployment
cd matrix-deployment

1.2 Create Docker Compose File

Create a file named docker-compose.yml with the following content:

services:
  synapse:
    image: matrixdotorg/synapse:latest
    container_name: synapse
    volumes:
      - ./synapse_data:/data
    ports:
      - "8008:8008"  # Client API
    environment:
      - SYNAPSE_SERVER_NAME=matrix.example.com
      - SYNAPSE_REPORT_STATS=no
    restart: unless-stopped

  coturn:
    image: coturn/coturn:latest
    container_name: coturn
    volumes:
      - ./coturn.conf:/etc/coturn/turnserver.conf
    ports:
      - "3478:3478/tcp"   # STUN/TURN TCP
      - "3478:3478/udp"   # STUN/TURN UDP
      - "5349:5349/tcp"   # STUN/TURN SSL TCP
      - "5349:5349/udp"   # STUN/TURN SSL UDP
    command: ["-n", "--log-file=stdout", "--external-ip=YOUR_LOCAL_MACHINE_IP"]
    restart: unless-stopped

Notes:

  • Replace matrix.example.com with the domain provided by Pangolin (e.g., matrix.your-pangolin-domain.com).
  • Replace YOUR_LOCAL_MACHINE_IP with your local machine’s public IP or leave it unset for now; we’ll adjust it based on Pangolin’s tunneling.

1.3 Generate Synapse Configuration

Run the following command to generate the initial Synapse configuration:

docker compose run --rm synapse generate

This creates configuration files in the synapse_data directory, including homeserver.yaml.

1.4 Configure Coturn

Create a coturn.conf file in the matrix-deployment directory:

listening-port=3478
tls-listening-port=5349
realm=turn.matrix.crowdsec.hhf.technology
server-name=turn.matrix.crowdsec.hhf.technology
external-ip=10.0.1.62
min-port=49152
max-port=65535
verbose
fingerprint
lt-cred-mech
user=admin:password
static-auth-secret=d41d8cd98f00b204e9800998ecf8427e

Notes:

  • Replace turn.matrix.example.com with a subdomain (e.g., turn.your-pangolin-domain.com).
  • Replace YOUR_LOCAL_MACHINE_IP with your local machine’s public IP or the IP assigned by Pangolin later.
  • For simplicity, set a static username and password (e.g., username:password). In production, generate a secure password.
    docker exec -it <synapse-container-name> register_new_matrix_user -c /data/homeserver.yaml

Step 2: Set Up Pangolin

Pangolin uses WireGuard and Traefik to tunnel traffic from your VPS to your local machine. Follow these steps to configure it.

2.1 Create a Site in Pangolin Dashboard

  1. Log in to your Pangolin dashboard (provided by your Pangolin setup).
  2. Create a new site and note the following:
    • Newt ID (YOUR_NEWT_ID)
    • Newt Secret (YOUR_NEWT_SECRET)
    • Endpoint URL (e.g., https://your-pangolin-domain.com)

2.2 Run Newt on Your Local Machine

Download Newt (Pangolin’s tunneling client) for your platform, then run it with the credentials from the dashboard:

./newt \
  --id YOUR_NEWT_ID \
  --secret YOUR_NEWT_SECRET \
  --endpoint https://your-pangolin-domain.com
  • Keep Newt running in a terminal or as a background service.
  • This establishes the WireGuard tunnel between your local machine and the VPS.

Step 3: Configure Ports in Pangolin

Modify Pangolin’s configuration on the VPS to expose the necessary ports.

3.1 Edit Pangolin’s docker-compose.yml

Locate or create Pangolin’s docker-compose.yml on your VPS (typically in the Pangolin installation directory). Add the required ports to the gerbil service:

services:
  gerbil:
    image: pangolin/gerbil:latest  # Adjust based on your Pangolin setup
    ports:
      - "51820:51820/udp"  # WireGuard
      - "80:80"            # HTTP
      - "443:443"          # HTTPS
      - "3478:3478/tcp"    # STUN/TURN TCP
      - "3478:3478/udp"    # STUN/TURN UDP
      - "5349:5349/tcp"    # STUN/TURN SSL TCP
      - "5349:5349/udp"    # STUN/TURN SSL UDP
    # Other existing configuration...

3.2 Add EntryPoints in Traefik Configuration

Edit or create config/traefik/traefik_config.yml in your Pangolin directory on the VPS:

entryPoints:
  web:
    address: ":80"
  websecure:
    address: ":443"
    http:
      tls: {}
  tcp-3478:
    address: ":3478/tcp"
  udp-3478:
    address: ":3478/udp"
  tcp-5349:
    address: ":5349/tcp"
  udp-5349:
    address: ":5349/udp"

Notes:

  • websecure (port 443) handles client API traffic with TLS.
  • Coturn ports are defined as raw TCP/UDP entry points since they don’t use HTTP.

3.3 Restart Pangolin Services

Apply the changes by restarting Pangolin:

sudo docker compose down
sudo docker compose up -d

Step 4: Create Resources in Pangolin Dashboard

Define resources to route traffic from the VPS to your local services.

  1. Matrix Client API (HTTP on Port 8008):

    • Type: HTTP
    • Domain: matrix.example.com
    • Entry Point: websecure (port 443)
    • Target: <synapse-container-ip>:8008 (e.g., 172.18.0.2:8008, find this using docker inspect synapse on your local machine)
    • Path: /_matrix/*
    • Path: _synapse/client/*
    • Path: .well-known/matrix/server
  2. Coturn STUN/TURN (TCP/UDP on Port 3478):

    • Type: Raw TCP/UDP
    • Entry Point: turn-tcp (TCP) and turn-udp (UDP)
    • Target: <coturn-container-ip>:3478 (e.g., 172.18.0.3:3478, find this using docker inspect coturn)
  3. Coturn STUN/TURN SSL (TCP/UDP on Port 5349):

    • Type: Raw TCP/UDP
    • Entry Point: turn-ssl-tcp (TCP) and turn-ssl-udp (UDP)
    • Target: <coturn-container-ip>:5349

Finding Container IPs:

  • Run docker network ls to find the network (e.g., matrix-deployment_default).
  • Run docker network inspect <network-name> to get the IPs of synapse and coturn.

Step 5: Modify Matrix Configuration

Update Synapse’s configuration to use Pangolin’s domain and integrate with Coturn.

5.1 Edit homeserver.yaml

Open synapse_data/homeserver.yaml and modify the following sections:

# Configuration file for Synapse.
#
# This is a YAML file: see [1] for a quick introduction. Note in particular
# that *indentation is important*: all the elements of a list or dictionary
# should have the same indentation.
#
# [1] https://docs.ansible.com/ansible/latest/reference_appendices/YAMLSyntax.html
#
# For more information on how to configure Synapse, including a complete accounting of
# each option, go to docs/usage/configuration/config_documentation.md or
# https://element-hq.github.io/synapse/latest/usage/configuration/config_documentation.html
server_name: "matrix.crowdsec.hhf.technology"
pid_file: /data/homeserver.pid
listeners:
  - port: 8008
    tls: false
    type: http
    x_forwarded: true
    bind_addresses: ['0.0.0.0']
    resources:
      - names: [client, federation]
        compress: false
database:
  name: sqlite3
  args:
    database: /data/homeserver.db
log_config: "/data/matrix.crowdsec.hhf.technology.log.config"
media_store_path: /data/media_store
serve_server_wellknown: true
registration_shared_secret: "qir:OXhJicNi7e~NX#U=GJw&eACZTL~g==7Bi@.qR+YGmXjsLD"
report_stats: false
macaroon_secret_key: "DQEcko3Ubrfl28+V*N8nc4st#IJ0i8c,D.dxei;IhZPMKj2HI7"
form_secret: ";^zICi3XRkD:y^whG@J~J9wytt;v7vxjbqZ,_Z&-r.CaS5a.i_"
signing_key_path: "/data/matrix.crowdsec.hhf.technology.signing.key"
trusted_key_servers:
  - server_name: "matrix.org"
turn_uris:
  - "turn:turn.matrix.crowdsec.hhf.technology:3478?transport=udp"
  - "turn:turn.matrix.crowdsec.hhf.technology:3478?transport=tcp"
  - "turns:turn.matrix.crowdsec.hhf.technology:5349?transport=udp"
  - "turns:turn.matrix.crowdsec.hhf.technology:5349?transport=tcp"
turn_shared_secret: "d41d8cd98f00b204e9800998ecf8427e"
turn_user_lifetime: 86400000
turn_allow_guests: true


# vim:ft=yaml

Notes:

  • Replace matrix.example.com with your Pangolin-provided domain.
  • Replace turn.matrix.example.com with your Coturn domain (same or a subdomain).
  • Set turn_shared_secret to a secure value and match it in coturn.conf if using long-term credentials instead of static username:password.

5.2 Update Coturn Configuration (Optional)

If using a shared secret instead of static credentials, update coturn.conf:

listening-port=3478
tls-listening-port=5349
realm=turn.matrix.crowdsec.hhf.technology
server-name=turn.matrix.crowdsec.hhf.technology
external-ip=10.0.1.62
min-port=49152
max-port=65535
verbose
fingerprint
lt-cred-mech
user=admin:password
static-auth-secret=d41d8cd98f00b204e9800998ecf8427e

Restart Coturn after changes:

docker compose restart coturn

Step 6: Deploy Your Matrix Stack

Start your local Matrix services:

docker compose up -d

Verify the services are running:

docker ps

Step 7: Test Your Deployment

  1. Client Access:

    • Open a browser or Matrix client (e.g., Element) and connect to https://matrix.example.com.
    • Register a user to confirm the client API works.
      https://federationtester.matrix.org/#matrix.crowdsec.hhf.technology
  2. Federation:

  3. Voice/Video:

    • Start a voice or video call in a Matrix room to test Coturn functionality.

Traffic Flow

  • Users: https://matrix.example.com → Pangolin VPS (port 443) → WireGuard → Newt → Synapse (port 8008)
  • STUN/TURN: turn.matrix.example.com:3478/5349 → Pangolin VPS (ports 3478/5349) → WireGuard → Newt → Coturn

Final Notes

  • Security: Use strong passwords/secrets and consider adding TLS certificates to Coturn for production use.
  • Troubleshooting: Check logs with docker logs synapse and docker logs coturn if issues arise.

This setup provides a fully functional Matrix server with federation and voice/video support, exposed securely via Pangolin. Let me know if you need further clarification!

5 Likes

external-ip=10.0.1.62

Is the Lan ip address or homeserver public ip address?

1 Like

Does this bypass the crowdsec/middleware config? If I understand it right it would?

1 Like

Yes it will. You understood correct.

1 Like

Are you able to update the guide with adding in element call? I may be able to help with some, working on trying to spin up one myself.

1 Like

currently working on a different project. crowdsec-manager once done i will focus my attention here.

2 Likes

Hi your guide let me set up coturn in under 15min. I already had pangolin and matrix/synapse installed. Thanks alot for your guide!

2 Likes

thank you. more self-hosted deployments coming soon

Does Pangolin not support port ranges? Trying to add matrix-rtc it says to open port 50100-50200 but I’m not seeing a way to accomplish this. Has anyone been able to make this work?

pangolin has nothing to with it. traefik doesn’t support ranges.

Ah… well that is unfortunate. So it seems this isn’t currently possible

Services that require huge port ranges shouldn’t be exposed directly through an ingress proxy anyway anything above 5-8 ports is a bad service technically in the modern days, We have coturn for that. Which is built on turn but it’s designed to do dumb stuff like have over 100 ports open and forwarded to a server. Matrix-rtc connects to coturn has the ability to talk over 100 or even more ports multiple services can use the same coturn.

It looks like matrix-rtc also has its own turn built in, although legacy calls do not work. It seems it also shows a large port range in the documentation, but I guess there is nothing saying you can’t squeeze that down, although I wonder what it does to the reliability of it.

Matrix is a hit and go when it comes to call/vid call. not my fav.

Agreed, it for sure could be better, but there doesn’t seem to be a better option out there currently.