270 lines
7.2 KiB
Markdown
270 lines
7.2 KiB
Markdown
# Matrix / Element Self-Hosted Server
|
|
|
|
A Docker Compose setup for running a self-hosted [Matrix](https://matrix.org/) homeserver (Synapse) with the [Element Web](https://element.io/) client and a Synapse admin panel.
|
|
|
|
## What's included
|
|
|
|
| Service | Image | Default Port | Purpose |
|
|
|---|---|---|---|
|
|
| **Synapse** | `matrixdotorg/synapse:latest` | `8008` | Matrix homeserver |
|
|
| **Element Web** | `vectorim/element-web:latest` | `8080` | Web chat client |
|
|
| **Synapse Admin** | `awesometechnologies/synapse-admin:latest` | `8081` | Admin UI |
|
|
| **PostgreSQL** | `postgres:14` | `5432` | Database for Synapse |
|
|
|
|
---
|
|
|
|
## Prerequisites
|
|
|
|
- [Docker](https://docs.docker.com/get-docker/) and [Docker Compose](https://docs.docker.com/compose/install/)
|
|
- A domain name pointed at your server (e.g. `chat.example.com`)
|
|
- A reverse proxy (nginx, Caddy, Traefik, etc.) to handle TLS termination — the containers themselves do not manage HTTPS
|
|
|
|
---
|
|
|
|
## Setup
|
|
|
|
### 1. Clone the repository
|
|
|
|
```bash
|
|
git clone <your-repo-url>
|
|
cd matrix
|
|
```
|
|
|
|
### 2. Configure environment variables
|
|
|
|
Copy the example env file and edit it:
|
|
|
|
```bash
|
|
cp .env.example .env
|
|
```
|
|
|
|
Open `.env` and set each value:
|
|
|
|
```dotenv
|
|
# PostgreSQL
|
|
POSTGRES_DB=synapse
|
|
POSTGRES_USER=synapse
|
|
POSTGRES_PASSWORD=<strong-random-password>
|
|
|
|
# Synapse — must match your public domain
|
|
SYNAPSE_SERVER_NAME=chat.example.com
|
|
|
|
# Synapse Admin UI
|
|
REACT_APP_SERVER=https://chat.example.com
|
|
REACT_APP_REGISTRATION_ENABLED=false # set to true only if you want open registration
|
|
```
|
|
|
|
> **Security note:** Use a strong, unique password for `POSTGRES_PASSWORD`. Never commit `.env` to version control — it is already in `.gitignore`.
|
|
|
|
### 3. Generate the Synapse configuration
|
|
|
|
Synapse needs a `homeserver.yaml` generated before it can start. Run this once:
|
|
|
|
```bash
|
|
docker run --rm \
|
|
-e SYNAPSE_SERVER_NAME=chat.example.com \
|
|
-e SYNAPSE_REPORT_STATS=yes \
|
|
-v "$(pwd)/synapse-data:/data" \
|
|
matrixdotorg/synapse:latest generate
|
|
```
|
|
|
|
Replace `chat.example.com` with your actual domain. This creates `synapse-data/homeserver.yaml`.
|
|
|
|
### 4. Point Synapse at PostgreSQL
|
|
|
|
Open `synapse-data/homeserver.yaml` and replace the default SQLite database block with:
|
|
|
|
```yaml
|
|
database:
|
|
name: psycopg2
|
|
args:
|
|
user: synapse
|
|
password: <your-POSTGRES_PASSWORD>
|
|
database: synapse
|
|
host: postgres
|
|
cp_min: 5
|
|
cp_max: 10
|
|
```
|
|
|
|
Use the same credentials you set in `.env`.
|
|
|
|
### 5. Configure the Element Web client
|
|
|
|
Copy the example config and edit it:
|
|
|
|
```bash
|
|
cp element-config.json.example element-config.json
|
|
```
|
|
|
|
Update every occurrence of `chat.example.com` to your domain:
|
|
|
|
```json
|
|
{
|
|
"default_server_config": {
|
|
"m.homeserver": {
|
|
"base_url": "https://chat.example.com",
|
|
"server_name": "chat.example.com"
|
|
}
|
|
},
|
|
"disable_custom_urls": true,
|
|
"brand": "My Matrix Server",
|
|
"showLabsSettings": true,
|
|
"voip": {
|
|
"stun_servers": [
|
|
{ "urls": ["stun:turn.example.com:3478"] }
|
|
],
|
|
"turn_servers": [
|
|
{
|
|
"urls": [
|
|
"turn:turn.example.com:3478?transport=udp",
|
|
"turn:turn.example.com:3478?transport=tcp",
|
|
"turns:turn.example.com:5349?transport=tcp"
|
|
],
|
|
"secret": "<your-turn-server-secret>",
|
|
"expiry": 86400000
|
|
}
|
|
]
|
|
}
|
|
}
|
|
```
|
|
|
|
If you do not have a TURN server, remove the `voip` and `webrtc` blocks entirely. Voice/video calls on the same local network will still work without them.
|
|
|
|
### 6. Start the stack
|
|
|
|
```bash
|
|
docker compose up -d
|
|
```
|
|
|
|
Verify all containers are running:
|
|
|
|
```bash
|
|
docker compose ps
|
|
```
|
|
|
|
Check Synapse logs for errors:
|
|
|
|
```bash
|
|
docker compose logs -f synapse
|
|
```
|
|
|
|
### 7. Create your first admin user
|
|
|
|
Once Synapse is running, register an admin account:
|
|
|
|
```bash
|
|
docker compose exec synapse register_new_matrix_user \
|
|
-c /data/homeserver.yaml \
|
|
-u <username> \
|
|
-p <password> \
|
|
-a \
|
|
http://localhost:8008
|
|
```
|
|
|
|
The `-a` flag grants admin privileges. You can omit it for regular users.
|
|
|
|
---
|
|
|
|
## Reverse proxy / TLS
|
|
|
|
The containers expose plain HTTP. You must front them with a reverse proxy that terminates TLS. A minimal **nginx** example:
|
|
|
|
```nginx
|
|
server {
|
|
listen 443 ssl;
|
|
server_name chat.example.com;
|
|
|
|
ssl_certificate /etc/letsencrypt/live/chat.example.com/fullchain.pem;
|
|
ssl_certificate_key /etc/letsencrypt/live/chat.example.com/privkey.pem;
|
|
|
|
# Element Web
|
|
location / {
|
|
proxy_pass http://127.0.0.1:8080;
|
|
proxy_set_header Host $host;
|
|
proxy_set_header X-Real-IP $remote_addr;
|
|
}
|
|
|
|
# Synapse Matrix API and federation
|
|
location /_matrix {
|
|
proxy_pass http://127.0.0.1:8008;
|
|
proxy_set_header Host $host;
|
|
proxy_set_header X-Real-IP $remote_addr;
|
|
client_max_body_size 50M;
|
|
}
|
|
|
|
location /_synapse {
|
|
proxy_pass http://127.0.0.1:8008;
|
|
proxy_set_header Host $host;
|
|
proxy_set_header X-Real-IP $remote_addr;
|
|
}
|
|
}
|
|
```
|
|
|
|
For automatic TLS with Let's Encrypt, use [Certbot](https://certbot.eff.org/) or [Caddy](https://caddyserver.com/).
|
|
|
|
### Matrix federation (optional)
|
|
|
|
If you want other Matrix homeservers to be able to communicate with yours, port `8448` must be reachable publicly. You can either:
|
|
|
|
- Add a second `server { listen 8448 ssl; ... }` block that proxies to `http://127.0.0.1:8008`, or
|
|
- Add a `.well-known/matrix/server` file served from your domain pointing federation to port 443
|
|
|
|
---
|
|
|
|
## Accessing the services
|
|
|
|
| URL | Service |
|
|
|---|---|
|
|
| `https://chat.example.com` | Element Web client |
|
|
| `https://chat.example.com:8081` | Synapse Admin panel |
|
|
| `https://chat.example.com/_matrix` | Matrix homeserver API |
|
|
|
|
Log into the admin panel at port `8081` using the admin credentials you created in step 7.
|
|
|
|
---
|
|
|
|
## Data persistence
|
|
|
|
All persistent data is stored in local directories that are bind-mounted into the containers:
|
|
|
|
| Directory | Contents |
|
|
|---|---|
|
|
| `postgres-data/` | PostgreSQL database files |
|
|
| `synapse-data/` | Synapse config, media uploads, signing keys |
|
|
|
|
Both directories are excluded from git via `.gitignore`. **Back them up regularly.**
|
|
|
|
---
|
|
|
|
## Upgrading
|
|
|
|
Pull the latest images and recreate the containers:
|
|
|
|
```bash
|
|
docker compose pull
|
|
docker compose up -d
|
|
```
|
|
|
|
Check the [Synapse changelog](https://github.com/element-hq/synapse/blob/master/CHANGES.md) before upgrading major versions — some releases require manual migration steps.
|
|
|
|
---
|
|
|
|
## Troubleshooting
|
|
|
|
**Synapse fails to start with a database error**
|
|
Make sure the credentials in `synapse-data/homeserver.yaml` match those in `.env`, and that the `postgres` service is fully healthy before Synapse starts. You can force the order with:
|
|
```bash
|
|
docker compose up -d postgres
|
|
# wait a few seconds, then:
|
|
docker compose up -d synapse element-web element-admin
|
|
```
|
|
|
|
**Element Web shows "Homeserver is not reachable"**
|
|
Verify that `base_url` in `element-config.json` uses `https://` and points to the domain your reverse proxy serves (not `localhost`).
|
|
|
|
**"M_FORBIDDEN" when registering users**
|
|
Open registration is disabled by default. Either use the `register_new_matrix_user` command (step 7) or set `enable_registration: true` in `homeserver.yaml` and restart Synapse.
|
|
|
|
**Port conflicts**
|
|
If any default port is already in use on your host, change the left side of the port mapping in `docker-compose.yml` (e.g. `"8082:80"` for Element Web) and update your reverse proxy accordingly.
|