- Shell 96%
- Makefile 4%
| config | ||
| landing | ||
| scripts | ||
| .gitignore | ||
| Makefile | ||
| README.md | ||
| setup.sh | ||
Matrix Stack
Self-hosted Matrix homeserver stack based on Synapse, with Cinny web client, Ketesa admin UI, and a multilingual landing page.
The server name (which determines Matrix user IDs) can be set up in two ways:
- Root domain —
domain.tld→ users are@user:domain.tld(recommended) - Subdomain —
sub.domain.tld→ users are@user:sub.domain.tld
The setup script detects subdomain input and guides through the options interactively.
Components
| Service | Description | Default Port |
|---|---|---|
| Synapse | Matrix homeserver | 8008 |
| PostgreSQL | Database for Synapse | internal |
| Cinny | Web-based Matrix client | 8009 |
| Ketesa | Matrix admin UI | 8010 |
| Landing | Static landing page (nginx) | 8080 |
Prerequisites
System packages
| Package | Purpose |
|---|---|
make |
Run Makefile targets |
docker |
Container runtime |
docker compose |
Multi-container orchestration |
python3 |
Config file patching during setup |
curl |
Health checks |
Debian/Ubuntu:
sudo apt update && sudo apt install -y make curl python3
Alpine:
apk add make curl python3
Docker (all distros): Follow the official guide: https://docs.docker.com/engine/install/
DNS
Option A: Root domain setup (recommended)
Users will be @user:domain.tld. All A-Records point to the server IP:
| Record | Type | Value |
|---|---|---|
domain.tld |
A | <server IP> |
matrix.domain.tld |
A | <server IP> |
cinny.domain.tld |
A | <server IP> |
ketesa.domain.tld |
A | <server IP> |
Option B: Subdomain setup
Users will be @user:sub.domain.tld. The .well-known delegation must be
served from the webroot of sub.domain.tld.
| Record | Type | Value |
|---|---|---|
sub.domain.tld |
A | <server IP> |
matrix.sub.domain.tld |
A | <server IP> |
cinny.sub.domain.tld |
A | <server IP> |
ketesa.sub.domain.tld |
A | <server IP> |
Or with services under the root domain:
| Record | Type | Value |
|---|---|---|
sub.domain.tld |
A | <server IP> |
matrix.domain.tld |
A | <server IP> |
cinny.domain.tld |
A | <server IP> |
ketesa.domain.tld |
A | <server IP> |
Reverse Proxy (NPM)
Nginx Proxy Manager must be running and ports 80/443 must be open. Proxy hosts are configured after setup -- see NPM Setup below.
Quick Start
# 1. Check prerequisites and launch menu
./setup.sh
# 2. Full interactive setup (first time)
make generate
# 3. Start the stack
make up
# 4. Create admin user
make admin
File Structure
matrix/
├── setup.sh # Prerequisite check + make launcher
├── Makefile # All management targets
├── docker-compose.yml # Stack definition
├── .env # Generated secrets (do not commit)
├── scripts/
│ ├── generate.sh # Full interactive setup
│ ├── smtp.sh # SMTP config helper
│ └── admin.sh # Create admin user
├── config/
│ ├── setup.conf # All settings (generated, do not commit)
│ ├── smtpserver.conf # SMTP credentials (generated, do not commit)
│ ├── smtpserver.conf.sample # SMTP template (safe to commit)
│ ├── cinny/config.json # Cinny web client config
│ └── ketesa/config.json # Ketesa admin UI config
├── landing/
│ └── index.html # Multilingual landing page (DE/EN)
└── data/
├── postgres/ # PostgreSQL data volume
└── synapse/ # Synapse data + homeserver.yaml
Make Targets
make Show this help
make generate Full first-time interactive setup
make smtp Add or update SMTP configuration
make up Start all containers
make down Stop all containers
make restart Restart all containers
make update Pull latest images and restart
make logs Follow all container logs
make logs-synapse Follow Synapse logs only
make status Show container status
make admin Create a Matrix admin user
make shell-synapse Shell into Synapse container
make shell-postgres PostgreSQL shell
NPM Setup
After make up, configure the following proxy hosts in Nginx Proxy Manager:
| Domain | Forward to | Port | SSL |
|---|---|---|---|
yourdomain.tld |
localhost |
8080 | Let's Encrypt |
matrix.yourdomain.tld |
localhost |
8008 | Let's Encrypt |
cinny.yourdomain.tld |
localhost |
8009 | Let's Encrypt |
ketesa.yourdomain.tld |
localhost |
8010 | Let's Encrypt |
well-known delegation (on yourdomain.tld proxy host)
Add two Custom Locations:
/.well-known/matrix/server
default_type application/json;
return 200 '{"m.server":"matrix.yourdomain.tld:443"}';
add_header Access-Control-Allow-Origin *;
/.well-known/matrix/client
default_type application/json;
return 200 '{"m.homeserver":{"base_url":"https://matrix.yourdomain.tld"},"m.identity_server":{"base_url":"https://vector.im"}}';
add_header Access-Control-Allow-Origin *;
Security Notes
config/setup.confandconfig/smtpserver.confcontain plaintext passwords- Both files are chmod 600 and must not be committed to Git
.envmust not be committed to Git- See
.gitignorefor the full exclusion list
Federation
Federation is active by default. Verify with:
curl https://yourdomain.tld/.well-known/matrix/server
# Expected: {"m.server":"matrix.yourdomain.tld:443"}
Online tester: https://federationtester.matrix.org/
Password Reset
Password reset via email requires SMTP configuration.
Run make smtp to configure, or set it up during make generate.
Users can reset their password via the Cinny web client login page, or an admin can reset it via Ketesa.
Updates
make update
Pulls latest images for all services and restarts the stack.