“This is your last chance.”
After this, there’s no turning back.
You take the green pill — you stay in comfort,
messaging through platforms that decide what’s best for you. You trust their terms, their updates, their eyes.

You take the red pill — and you stay in control.
No gatekeepers. No tracking. No compromises.
You host your own Matrix. You own your conversations.
All I’m offering is the truth. Nothing more.
Your server. Your rules. Your reality.
Take Back Trust. Take Back Control.
When you use WhatsApp, Signal, or Telegram, you’re not just using a tool — you’re depending on someone else’s infrastructure, decisions, and promises.
Even if the app is end-to-end encrypted, you still have to trust:
- That the encryption works as advertised
- That metadata isn’t logged
- That your access won’t be limited, blocked, or monetized
You’re not really free — you’re just tolerated, until you’re not.
Self-hosting changes that.
With Matrix, you can run your own open, federated, encrypted messaging server.
That means:
- You own your data
- You decide who can use it
- You don’t rely on Big Tech’s infrastructure or policies
- And most importantly: you don’t have to trust — you can verify
This guide walks you through setting up Matrix Synapse on a clean RHEL 9 system (or Rocky, AlmaLinux), using a Python virtual environment for clean separation and PostgreSQL for production-grade data handling.
No Docker. No containers. No cloud lock-in.
Just your machine, your rules.
1. System Requirements & Dependencies
Before you can run your own Matrix server, you need to prepare a clean, Linux-based system with the necessary tools and libraries.
This guide assumes RHEL 9, Rocky Linux 9, or AlmaLinux 9, but it will work on most modern RPM-based distributions.
1.1 Basic Assumptions
- You have root access to the system (either directly or via
sudo
) - You have a public domain name, e.g.
matrix.example.com
- You are comfortable working in a terminal
- The server has open ports for:
- 443/TCP (for HTTPS traffic)
- 8448/TCP (for Matrix federation)
1.2 Install Required System Packages
Run the following commands to install development tools, Python libraries, and PostgreSQL:
sudo dnf install -y git gcc python3 python3-devel python3-virtualenv libpq-devel libffi-devel gcc openssl-devel postgresql postgresql-server postgresql-contrib
These packages are required to:
- Build and run Python packages in isolation
- Use PostgreSQL as a reliable backend database for Synapse
- Connect Python (Synapse) to the PostgreSQL server via
libpq
1.3 Initialize PostgreSQL (first-time setup)
If you’ve just installed PostgreSQL on this system, initialize the database cluster like this:
sudo postgresql-setup --initdb
sudo systemctl enable --now postgresql
This sets up a basic PostgreSQL environment and ensures it starts automatically on boot.
2. Application Setup in a Virtual Environment
To keep the system clean and avoid version conflicts, we install Synapse in a Python virtual environment (venv
). This isolates all Python dependencies from the rest of the system and makes the installation easier to maintain, update, and remove later.
2.1 Why use a virtual environment?
Using a virtual environment ensures:
- Synapse doesn’t interfere with other Python packages on your system
- You can run different versions of Synapse (or other Python apps) side by side
- Upgrades are isolated and easily reversible
- You avoid permission issues or dependency conflicts with system Python
It’s the recommended way to run Python applications on bare metal or VMs.
2.2 Create Directory and Virtual Environment
Create a dedicated directory for your Matrix installation:
mkdir -p /opt/synapse
cd /opt/synapse
Now create the virtual environment:
python3 -m venv venv
Activate the environment:
source venv/bin/activate
You’ll now see the shell prompt prefixed with (venv)
— this means the environment is active.
2.3 Upgrade Pip and Install Synapse
First, upgrade pip
inside the virtual environment:
pip install --upgrade pip
Then install Synapse with PostgreSQL support:
pip install matrix-synapse[postgres]
If you want to test Synapse without PostgreSQL, it’s also possible to use the built-in SQLite backend — but this is not recommended for production use, especially with more than a few users or if federation is enabled.
3. Generate and Configure the Synapse Instance
With Synapse installed in the virtual environment, the next step is to generate the base configuration and adjust it to your setup — especially to use PostgreSQL as the backend database.
3.1 Generate the Initial Configuration
Still inside your virtual environment (source venv/bin/activate
), run:
python -m synapse.app.homeserver \
--server-name matrix.example.com \
--config-path homeserver.yaml \
--generate-config \
--report-stats=no
Replace matrix.example.com
with the actual domain name your Matrix server will use.
This will create:
homeserver.yaml
— the main configuration file- TLS key material (can be ignored if you’re using a reverse proxy later)
- Signing key files (keep these safe — they identify your server in the federation)
Tip: Always keep a backup of the
homeserver.yaml
and*.signing.key
files. They are essential for server identity and federation.
3.2 Prepare for PostgreSQL Usage
By default, Synapse configures itself to use SQLite. For production use, you should switch to PostgreSQL.
Open homeserver.yaml
in your preferred editor and locate the database:
section.
Replace it with the following block:
database:
name: psycopg2
args:
user: synapse
password: YourStrongPassword
database: synapse
host: 127.0.0.1
port: 5432
Don’t forget to replace
YourStrongPassword
with the actual password you’ll assign to the PostgreSQL user in the next step.
3.3 Optional: Disable Features You Don’t Need Yet
For a clean and simple first setup, you can comment out or disable the following sections in homeserver.yaml
:
- Email registration and password reset (unless you configure SMTP)
- Auto-join rooms
- Presence tracking or push notifications (if you’re not using mobile clients)
4. Set Up and Secure the PostgreSQL Database
Synapse supports both SQLite and PostgreSQL — but for anything beyond testing or single-user setups, PostgreSQL is the recommended choice. It offers better performance, reliability, and scalability for federated environments.
This chapter covers creating a dedicated PostgreSQL database and user for Synapse, using locale C
for optimal performance and compatibility.
4.1 Connect to PostgreSQL as the Administrative User
Switch to the postgres
system user:
sudo -u postgres psql
You’ll now see the PostgreSQL prompt:
postgres=#
4.2 Create the Database with Locale “C”
Run the following SQL statements exactly as shown:
CREATE DATABASE synapse
ENCODING 'UTF8'
LC_COLLATE='C'
LC_CTYPE='C'
template=template0
OWNER postgres;
This creates a new database named synapse
with clean, fast collation settings.
PostgreSQL needs to restart to pick up locale settings correctly — but this is already handled during creation with template0
.
4.3 Create a Dedicated Database User
CREATE USER synapse WITH PASSWORD 'YourStrongPassword';
ALTER ROLE synapse WITH LOGIN;
Use a strong password and remember to insert it into your
homeserver.yaml
later.
4.4 Assign Permissions
Make the new user the owner of the database:
ALTER DATABASE synapse OWNER TO synapse;
GRANT ALL PRIVILEGES ON DATABASE synapse TO synapse;
Then exit the PostgreSQL shell:
\q
You now have:
- A production-grade database called
synapse
- A restricted, named user with full access to that database
- Synapse configured to connect using those credentials
5. Initialize the Synapse Database and Start the Server
At this point, all components are in place:
- Synapse is installed inside a virtual environment
- Configuration is prepared and points to PostgreSQL
- The database and user are ready
Now it’s time to initialize the database schema and launch Synapse for the first time.
5.1 Run the Initialization Step
Still inside your virtual environment (source venv/bin/activate
), run:
/opt/synapse/venv/bin/python -m synapse.app.homeserver \
--config-path /opt/synapse/homeserver.yaml
This will:
- Connect to the PostgreSQL database
- Create all necessary tables and internal structures
- Start Synapse in the foreground
You should see logging output as the homeserver boots up.
If there are any errors here, check file permissions, database credentials, or missing Python packages inside the virtual environment.
5.2 (Optional) Run Synapse in the Background
For now, you can stop the server with Ctrl + C
. Later, you’ll want to set up a proper systemd
service so Synapse runs automatically on boot and restarts on failure.
We’ll cover that in a later chapter.
6. Create Your First Admin and Regular User
You’re now ready to create user accounts for your homeserver. Matrix Synapse comes with a built-in CLI tool to register users directly — no web UI needed.
Make sure your virtual environment is active and you’re in the Synapse base directory (/opt/synapse
in our example).
6.1 Create an Admin User (Morpheus)
cd /opt/synapse
venv/bin/register_new_matrix_user \
-c homeserver.yaml \
-u morpheus \
-p RedPillOnly \
--admin \
http://localhost:8008
This creates a user @morpheus:example.com
with admin privileges.
6.2 Create a Regular User (Neo)
venv/bin/register_new_matrix_user \
-c homeserver.yaml \
-u neo \
-p WakeUpNeo \
http://localhost:8008
This registers a standard Matrix user @neo:example.com
— ready to log in using a Matrix client like Element.
You can create as many users as you need this way, or enable public registration later (not recommended for open federation servers without moderation in place).
6.3 User Management (Command-Line Basics)
Matrix Synapse doesn’t include a built-in UI for managing users. However, with the CLI tool that comes with Synapse, you can fully manage accounts.
Alle Befehle werden im Synapse-Verzeichnis ausgeführt, die virtuelle Umgebung muss vorher aktiviert werden:
cd /opt/synapse
source venv/bin/activate
Change a User’s Password
To generate a new password hash:
venv/bin/hash_password
Das Tool fragt nach einem neuen Passwort und gibt einen bcrypt-Hash zurück, z. B.:
$2b$12$KFuYjQn7wFxqTn1zKwhMR./qD5sYgR5w9/...
Dieser Hash kann manuell in die Datenbank geschrieben werden. Alternativ können Benutzer ihr Passwort über den Client selbst zurücksetzen – das ist der empfohlene Weg.
Deactivate a User
Ein deaktivierter Account kann sich nicht mehr einloggen, bleibt aber im System erhalten (z. B. für Rauminhalte):
venv/bin/deactivate_matrix_user \
-c homeserver.yaml \
-u neo
Damit wird @neo:example.com
deaktiviert.
Promote or Demote Admin Rights (optional)
Es gibt keinen offiziellen CLI-Befehl zum Anpassen von Admin-Rechten. Über die Datenbank geht es manuell:
UPDATE users SET admin = TRUE WHERE name = '@neo:example.com';
Zum Entfernen von Admin-Rechten:
UPDATE users SET admin = FALSE WHERE name = '@neo:example.com';
Wichtig: Datenbank vorher sichern. Fehlerhafte Änderungen können den Server beschädigen.
Delete a User Completely
Eine vollständige Löschung eines Benutzers (inkl. Nachrichten, Medien, Events) ist mit Bordmitteln nicht vorgesehen.
Für DSGVO-konforme Löschungen gibt es eigene Tools und APIs, die individuell eingebunden werden müssen.
7. Reverse Proxy and HTTPS Setup (Nginx)
Synapse itself listens only on localhost:8008
by default.
To make your server accessible to the outside world via HTTPS (https://matrix.example.com
), a reverse proxy is required — typically Nginx.
This setup also handles TLS termination (SSL certificates) and forwards traffic securely to Synapse.
7.1 Basic Nginx Configuration
Create a new config file under /etc/nginx/conf.d/matrix.conf
:
server {
listen 80;
server_name matrix.example.com;
location / {
return 301 https://$host$request_uri;
}
}
server {
listen 443 ssl;
server_name matrix.example.com;
# Placeholder – configure TLS properly
ssl_certificate /etc/letsencrypt/live/matrix.example.com/fullchain.pem;
ssl_certificate_key /etc/letsencrypt/live/matrix.example.com/privkey.pem;
location / {
proxy_pass http://127.0.0.1:8008;
proxy_set_header Host $host;
proxy_set_header X-Forwarded-For $remote_addr;
proxy_set_header X-Forwarded-Proto https;
}
}
Replace
matrix.example.com
with your actual domain.
7.2 TLS Certificates
This guide assumes that a valid TLS certificate is already available.
- Recommended: Use Let’s Encrypt with a tool like Certbot to generate and renew certificates automatically.
- You can also use any other SSL certificate provider.
Note: TLS setup is not part of this guide. Only the placeholder paths are shown above.
7.3 Reload Nginx
Once configured:
nginx -t
systemctl reload nginx
Your Synapse server should now be reachable at:
https://matrix.example.com
8. Domain Setup and Optional Federation
Matrix works locally out of the box. But to make it usable for clients (and optionally other servers), a few domain-level configurations are recommended.
This step is optional if you want to use Matrix purely as a private messenger.
If you do want to interact with other servers (federation), the following setup is required.
8.1 Recommended Domain Structure
To keep things clean and flexible, we separate user-visible and server-visible domains:
Purpose | Example Domain | Notes |
---|---|---|
Matrix user IDs | @user:example.com | What users see and share |
Homeserver base URL | matrix.example.com | The actual backend |
This way, your server can live at matrix.example.com
, while your users appear to be on example.com
.
It also allows you to migrate or rename infrastructure later, without changing user IDs.
8.2 Client Discovery via .well-known
This lets Matrix clients automatically find your homeserver when a user types @neo:example.com
.
Create this file:https://example.com/.well-known/matrix/client
Content:
{
"m.homeserver": {
"base_url": "https://matrix.example.com"
}
}
8.3 Server-to-Server Discovery (Federation)
Only required if you want to federate with the wider Matrix network.
Create this file:https://example.com/.well-known/matrix/server
Content:
{
"m.server": "matrix.example.com:443"
}
This tells other Matrix servers where to reach your homeserver.
8.4 DNS and Firewall Checklist
- A DNS A or AAAA record for
matrix.example.com
must point to your server. - Ports 443/tcp must be open to the public internet if you want federation.
- Port 8008 should not be exposed to the internet — it’s internal only.
8.5 Test Federation (optional)
You can test if your server is reachable from the outside using:
10. Running Synapse as a systemd Service
To ensure your Matrix Synapse server starts automatically on boot and is easy to manage, create a systemd
service unit.
10.1 Create the Service File
Create a file at /etc/systemd/system/synapse.service
with the following content:
[Unit]
Description=Matrix Synapse homeserver
After=network.target postgresql.service
Requires=postgresql.service
[Service]
Type=simple
User=root
WorkingDirectory=/opt/synapse
ExecStart=/opt/synapse/venv/bin/python -m synapse.app.homeserver --config-path /opt/synapse/homeserver.yaml
Restart=on-failure
RestartSec=10
[Install]
WantedBy=multi-user.target
10.2 Explanation
User=root
: runs Synapse as root by default (adjust if you want a dedicated user)WorkingDirectory
: points to your Synapse install folderExecStart
: runs Synapse using Python from the virtual environmentRestart=on-failure
: auto-restarts if the process crashes
10.3 Enable and Start the Service
Reload systemd to recognize the new service:
sudo systemctl daemon-reload
Enable Synapse to start on boot:
sudo systemctl enable synapse
Start the service now:
sudo systemctl start synapse
Check status and logs:
sudo systemctl status synapse
sudo journalctl -u synapse -f
10.4 Optional: Run as Dedicated User
For better security, create a dedicated user (e.g., matrix
) and adjust the service:
- Change
User=root
toUser=matrix
- Ensure file permissions for
/opt/synapse
are accessible tomatrix
Example:
sudo useradd -r -d /opt/synapse -s /sbin/nologin matrix
sudo chown -R matrix:matrix /opt/synapse
11. Config Best Practices with Relevant Settings
- Disable public user registration
enable_registration: false
- Use a strong shared secret for scripted user creation
registration_shared_secret: "a-very-long-random-secret"
- Bind only to localhost
listeners: - port: 8008 bind_addresses: ['127.0.0.1'] tls: false type: http resources: - names: [client, federation]
- Use PostgreSQL backend
database: name: psycopg2 args: user: synapse password: YOUR_PASSWORD database: synapse host: 127.0.0.1 port: 5432
- Configure admin contact for federation
admin_contact: 'mailto:admin@example.com'
- Limit logging verbosity (set to info or warning)
log_level: INFO
12. Backup and Restore
Backup essentials
- PostgreSQL database
Regularly back up usingpg_dump
, for example:pg_dump -U synapse -F c synapse > /backup/synapse-$(date +%F).dump
- Configuration files
Backuphomeserver.yaml
and key files (TLS certificates, signing keys). - Media store
Backup the media directory (e.g.,/opt/synapse/media_store
).
Restore basics
- Restore database dump:
pg_restore -U synapse -d synapse /backup/synapse-YYYY-MM-DD.dump
- Restore config files and keys.
- Verify file permissions.
- Restart Synapse service.
Regularly test your backups to ensure reliable recovery.
13. Upgrading Synapse
Keeping your Synapse server up-to-date is important for security, bug fixes, and new features. Here’s a straightforward approach:
13.1 Preparation
- Backup database, config files, media, and keys before upgrading.
- Read the official Synapse release notes for any breaking changes or special upgrade instructions.
13.2 Upgrade Steps
- Activate your virtual environment:
source /opt/synapse/venv/bin/activate
- Upgrade Synapse via pip:
pip install --upgrade matrix-synapse
- Run any required database migrations:
/opt/synapse/venv/bin/python -m synapse.app.homeserver \ --config-path /opt/synapse/homeserver.yaml \ --upgrade
- Restart the Synapse service:
sudo systemctl restart synapse
13.3 Verify
- Check service status:
sudo systemctl status synapse
- Monitor logs for errors:
sudo journalctl -u synapse -f
Keep upgrade frequency reasonable; don’t skip major versions without reading upgrade notes.
14. Optional: SRV Record for Federation
In case your Matrix homeserver (matrix.example.com
) differs from your user domain (example.com
), an SRV record allows other servers to discover your actual endpoint – especially if you’re not using a .well-known
configuration.
Example SRV Record for example.com
For DNS providers without dedicated SRV fields (e.g., SchlundTech), use a value format like this:
Type | Name / Host | Value | TTL |
---|---|---|---|
SRV | _matrix._tcp | 10 5 443 matrix.example.com. | 3600 |
✅ Note: Include the trailing dot after the hostname (
matrix.example.com.
)
Test Your SRV Record
- Matrix Federation Tester:
https://federationtester.matrix.org - Google Dig Tool:
https://toolbox.googleapps.com/apps/dig/#SRV/_matrix._tcp/example.com - Command Line:
dig +short SRV _matrix._tcp.example.com
ℹ️ If you already use a
.well-known/matrix/server
file, the SRV record is not required but can act as a fallback or future-proofing.
Choose the right pill and take control
You’re not just stepping out of the Matrix — you’re stepping into it, on your terms.
Forget the big messengers who trap you in their system and demand your trust. With your own Synapse server, you hold the keys, set the rules, and decide who you connect with.
So pick the red pill — the one that gives you freedom, control, and sovereignty over your communication.
Welcome to the real Matrix. It’s yours now.