Skip to content

TutorialsCommunication

How to install Matrix Synapse and Element Web on Ubuntu or Debian

Run your own Matrix homeserver with Synapse from the official packages, PostgreSQL, Caddy for HTTPS and federation delegation, plus Element Web in the browser.

  • Advanced
  • 50 min read
  • Updated

Tested on: Ubuntu 24.04 LTS, Ubuntu 26.04 LTS, Debian 12, Debian 13

This guide is not available in your language yet, so it is shown in English.

On this page
  1. Prerequisites
  2. Step 1 — Install PostgreSQL and create the database
  3. Step 2 — Add the Matrix.org repository and install Synapse
  4. Step 3 — Point Synapse at PostgreSQL and lock down registration
  5. Step 4 — Configure Caddy for Synapse and delegation
  6. Step 5 — Open the firewall and test federation
  7. Step 6 — Create the first administrator
  8. Step 7 — Install Element Web
  9. Back up and restore
  10. Update Synapse
  11. Troubleshooting
  12. register_new_matrix_user says no registration_shared_secret is defined
  13. Synapse does not start: Database has incorrect collation
  14. The federation tester reports a .well-known or certificate error
  15. Element cannot reach the homeserver
  16. Invited users from other servers cannot join your rooms
  17. Next steps

Matrix is an open, federated protocol for chat and calls: users on different servers can talk to each other, much like email. Synapse is the homeserver maintained by Element, and Element Web is the browser client. This guide installs Synapse from the official packages.matrix.org repository on Ubuntu or Debian, stores its data in PostgreSQL, puts it behind Caddy for HTTPS, and uses .well-known delegation so that user IDs look like @alice:example.com while the server itself runs at matrix.example.com. You then add Element Web on its own subdomain, create the first administrator with open registration switched off, and set up backups and updates.

The setup uses three names. Choose them now:

NameExamplePurpose
Server nameexample.comAppears in every user ID and room alias. It cannot be changed later.
Synapse hostmatrix.example.comWhere clients and other servers reach Synapse, on port 443.
Element Webelement.example.comThe browser client. Element recommends a different domain from the homeserver to limit the impact of cross-site scripting bugs.

Prerequisites

  • A server running Ubuntu 24.04 LTS, Ubuntu 26.04 LTS, Debian 12 or Debian 13 on x86_64 (amd64). The Matrix.org packages are built for amd64; packages.matrix.org publishes them for all four releases.
  • A non-root user with sudo rights: see Secure a new Linux server and Set up SSH keys.
  • DNS A records (and AAAA records if you use IPv6) for matrix.example.com and element.example.com pointing to this server.
  • The main domain example.com must serve two small JSON files over HTTPS. If it points to this server, Caddy serves them in Step 4; if your website runs elsewhere, you publish the same files there.
  • Caddy installed as described in Caddy reverse proxy.

The Synapse project does not publish minimum requirements. The figures below are a conservative starting point for a small private server, not official numbers:

ResourceMinimum (official)Suggested starting point
CPUNot published2 vCPU
RAMNot published2 GB, or 4 GB if users join large public rooms on other servers
DiskNot published20 GB plus uploaded media
DatabasePostgreSQL 14 or later for current releasesThe PostgreSQL package of your distribution

Step 1 — Install PostgreSQL and create the database

Install PostgreSQL and the client library Synapse uses, generate a password, and create a database user and a database with the encoding and locale Synapse requires:

Bash
sudo apt update
sudo apt install postgresql libpq5
openssl rand -hex 24
sudo -u postgres createuser --pwprompt synapse_user
sudo -u postgres createdb --encoding=UTF8 --locale=C --template=template0 --owner=synapse_user synapse

Enter the generated password twice when createuser asks for it, and keep it for Step 3. Check the new database:

Bash
sudo -u postgres psql -l | grep synapse

The line should show UTF8 encoding and C for both collation and character type. Synapse refuses to start on a database with another collation.

Step 2 — Add the Matrix.org repository and install Synapse

Synapse's documentation recommends the Matrix.org packages. Debian's own matrix-synapse package is only available for Debian 14 (forky) and unstable, and the documentation does not recommend the package in Ubuntu's archive.

Bash
sudo apt install -y lsb-release wget apt-transport-https
sudo wget -O /usr/share/keyrings/matrix-org-archive-keyring.gpg https://packages.matrix.org/debian/matrix-org-archive-keyring.gpg
echo "deb [signed-by=/usr/share/keyrings/matrix-org-archive-keyring.gpg] https://packages.matrix.org/debian/ $(lsb_release -cs) main" | sudo tee /etc/apt/sources.list.d/matrix-org.list
sudo apt update
sudo apt install matrix-synapse-py3

During installation the package asks two questions:

  • Name of the server: enter your server name, example.com, not matrix.example.com. The answer is written to /etc/matrix-synapse/conf.d/server_name.yaml.
  • Report homeserver usage statistics: answer as you prefer. The default is no.

Check the result:

Bash
cat /etc/matrix-synapse/conf.d/server_name.yaml
sudo systemctl status matrix-synapse --no-pager

The package starts Synapse straight away with a temporary SQLite database. No accounts exist yet, so switching to PostgreSQL in the next step loses nothing.

Step 3 — Point Synapse at PostgreSQL and lock down registration

The main configuration lives in /etc/matrix-synapse/homeserver.yaml. Put your own settings into separate files in /etc/matrix-synapse/conf.d/ instead: Synapse loads them after the main file, and package upgrades do not ask you to merge them. Stop Synapse first:

Bash
sudo systemctl stop matrix-synapse

Create three files. In the first one, replace change-me with the database password from Step 1 before you run the command; the second one generates a random registration secret:

Bash
sudo tee /etc/matrix-synapse/conf.d/database.yaml > /dev/null <<'EOF'
database:
  name: psycopg2
  args:
    user: synapse_user
    password: "change-me"
    dbname: synapse
    host: localhost
    cp_min: 5
    cp_max: 10
EOF
sudo tee /etc/matrix-synapse/conf.d/registration.yaml > /dev/null <<EOF
enable_registration: false
registration_shared_secret: "$(openssl rand -hex 32)"
EOF
echo 'public_baseurl: "https://matrix.example.com/"' | sudo tee /etc/matrix-synapse/conf.d/public_baseurl.yaml
  • database switches Synapse to PostgreSQL.
  • enable_registration: false keeps open sign-up off (it is also the default).
  • registration_shared_secret lets register_new_matrix_user create accounts. Anyone who knows it can register users, including administrators, even with registration disabled, so keep it private.
  • public_baseurl is the address clients use. Without it, Synapse assumes https://example.com/, which is not where it runs.

Make the files readable only by root and the matrix-synapse group, then start Synapse:

Bash
sudo chown root:matrix-synapse /etc/matrix-synapse/conf.d/*.yaml
sudo chmod 640 /etc/matrix-synapse/conf.d/*.yaml
sudo systemctl start matrix-synapse
sudo systemctl status matrix-synapse --no-pager
curl -s http://localhost:8008/_matrix/client/versions

The last command should print a JSON list of supported Matrix versions. Synapse listens only on localhost port 8008, and the packaged listener already trusts the X-Forwarded-For header from a local reverse proxy. If Synapse does not start, read sudo journalctl -u matrix-synapse -n 50 --no-pager.

Synapse can email password resets, address verification and (with enable_notifs: true) notifications about missed messages; to turn this on, add an email section to a file such as /etc/matrix-synapse/conf.d/email.yaml with smtp_host: smtp.example.com, smtp_port: 587, require_transport_security: true, smtp_user, smtp_pass and a notif_from address, as described under email in Synapse's configuration manual, then restart Synapse.

Step 4 — Configure Caddy for Synapse and delegation

Add two site blocks to /etc/caddy/Caddyfile. The first one proxies the Matrix client and federation APIs to Synapse; the second one serves the delegation files on your main domain:

Caddyfile
matrix.example.com {
    reverse_proxy /_matrix/* localhost:8008
    reverse_proxy /_synapse/client/* localhost:8008
}

example.com {
    header /.well-known/matrix/* Content-Type application/json
    header /.well-known/matrix/* Access-Control-Allow-Origin *
    respond /.well-known/matrix/server `{"m.server": "matrix.example.com:443"}`
    respond /.well-known/matrix/client `{"m.homeserver": {"base_url": "https://matrix.example.com"}}`
}
  • /.well-known/matrix/server tells other homeservers to send federation traffic for example.com to matrix.example.com on port 443, so port 8448 is not needed.
  • /.well-known/matrix/client lets apps such as Element find the homeserver when a user types only @alice:example.com.
  • /_synapse/admin is deliberately not proxied. Synapse's documentation does not recommend exposing the admin API to the internet; reach it through an SSH tunnel to localhost:8008 when you need it.

Reload Caddy and test all three addresses:

Bash
sudo systemctl reload caddy
curl https://example.com/.well-known/matrix/server
curl https://example.com/.well-known/matrix/client
curl https://matrix.example.com/_matrix/client/versions

Step 5 — Open the firewall and test federation

Synapse listens on localhost only, and delegation sends federation traffic to port 443, so the firewall needs SSH and web traffic only:

Bash
sudo ufw allow OpenSSH
sudo ufw allow 80/tcp
sudo ufw allow 443/tcp
sudo ufw enable
sudo ufw status verbose

Check federation with the official federation tester: enter example.com and every check should pass. The same test is available as a JSON API:

Bash
curl -s "https://matrix.org/federationtester/api/report?server_name=example.com" | grep -o '"FederationOK":[a-z]*'

The output should be "FederationOK":true.

Step 6 — Create the first administrator

Create your own account as an administrator. Pass both the main configuration file (for the listener address) and the registration file (for the shared secret):

Bash
sudo register_new_matrix_user -c /etc/matrix-synapse/homeserver.yaml -c /etc/matrix-synapse/conf.d/registration.yaml

The tool asks for a user name, a password and whether to make the user an admin; answer yes for your own account. Repeat the command without admin rights for every other person. The new user ID is @username:example.com.

Step 7 — Install Element Web

Element publishes Element Web as a Debian package that works on Debian and Ubuntu. Add the repository and install it:

Bash
sudo wget -O /usr/share/keyrings/element-io-archive-keyring.gpg https://packages.element.io/debian/element-io-archive-keyring.gpg
echo "deb [signed-by=/usr/share/keyrings/element-io-archive-keyring.gpg] https://packages.element.io/debian/ default main" | sudo tee /etc/apt/sources.list.d/element-io.list
sudo apt update
sudo apt install element-web

Point Element at your homeserver by replacing /etc/element-web/config.json with a minimal configuration:

Bash
sudo tee /etc/element-web/config.json > /dev/null <<'EOF'
{
  "default_server_config": {
    "m.homeserver": {
      "base_url": "https://matrix.example.com",
      "server_name": "example.com"
    }
  }
}
EOF

Then add a site block for element.example.com to the Caddyfile. It serves the static files from /usr/share/element-web, sets the security headers Element recommends and turns off caching for the files that change on every release:

Caddyfile
element.example.com {
    root * /usr/share/element-web
    file_server
    header {
        X-Frame-Options SAMEORIGIN
        X-Content-Type-Options nosniff
        X-XSS-Protection "1; mode=block"
        Content-Security-Policy "frame-ancestors 'self'"
    }
    @nocache path / /index.html /version /config.json /i18n/*
    header @nocache Cache-Control no-cache
}
Bash
sudo systemctl reload caddy
curl -I https://element.example.com

Open https://element.example.com, sign in with the account from Step 6, create a room and invite a second user to check that messages arrive in both directions.

Back up and restore

Synapse's backup guide lists what holds state: the PostgreSQL database, the configuration in /etc/matrix-synapse (including the server's signing key homeserver.signing.key), and the local media in /var/lib/matrix-synapse/media. Remote media, URL previews and their thumbnails are caches and can be skipped. The guide also recommends leaving out the data of the e2e_one_time_keys_json table, because restoring old one-time keys can break encrypted sessions. pg_dump takes a consistent snapshot, so Synapse can keep running:

Bash
sudo mkdir -p /opt/backups
sudo chown $USER:$USER /opt/backups
chmod 700 /opt/backups
sudo -u postgres pg_dump -Fc --exclude-table-data e2e_one_time_keys_json synapse > /opt/backups/synapse-db-$(date +%F).dump
sudo tar czf /opt/backups/synapse-files-$(date +%F).tar.gz --exclude='remote_*' --exclude='url_cache*' /etc/matrix-synapse /var/lib/matrix-synapse/media /etc/element-web /etc/caddy/Caddyfile
ls -lh /opt/backups

To restore on a new server, complete Steps 1 and 2 with the same server name and the same database password, but stop Synapse before it gets any data. Synapse's guide warns never to restore into a database that already contains tables; the database from Step 1 is still empty because Synapse used SQLite until now.

Bash
sudo systemctl stop matrix-synapse
sudo -u postgres pg_restore -d synapse < /opt/backups/synapse-db-2026-10-09.dump
sudo tar xzf /opt/backups/synapse-files-2026-10-09.tar.gz -C /
sudo chown -R matrix-synapse:matrix-synapse /var/lib/matrix-synapse
sudo systemctl start matrix-synapse

Then install Element Web (Step 7) and reload Caddy.

Update Synapse

Synapse is updated through apt like any other package. Read the upgrade notes for every version between yours and the new one first, because some releases change configuration or raise the minimum Python or PostgreSQL version, and take a backup:

Bash
sudo apt update
sudo apt upgrade
curl -s http://localhost:8008/_matrix/federation/v1/version

The last command prints the running Synapse version. You do not have to install every release you missed, but rollbacks are hard after database schema changes, so the backup is your way back. If apt asks whether to replace a configuration file, keep your version. The same apt upgrade updates Element Web.

Troubleshooting

register_new_matrix_user says no registration_shared_secret is defined

The tool only reads the files you pass with -c; it does not scan conf.d. Pass both homeserver.yaml and conf.d/registration.yaml as shown in Step 6.

Synapse does not start: Database has incorrect collation

The database was created with your system locale instead of C. Drop it with sudo -u postgres dropdb synapse (only on a new server without data), create it again with the createdb command from Step 1 and restart Synapse.

The federation tester reports a .well-known or certificate error

Run the three curl commands from Step 4. The server file must return JSON from https://example.com with a valid certificate, the m.server value must name matrix.example.com:443, and the request must not be redirected with HTTP 308. Check DNS for both names as well.

Element cannot reach the homeserver

Open https://matrix.example.com/_matrix/client/versions in the browser. If it fails, check the Caddy block and public_baseurl. If it works, check base_url in /etc/element-web/config.json and the Access-Control-Allow-Origin header on /.well-known/matrix/client.

Invited users from other servers cannot join your rooms

Federation needs connectivity in both directions, and Synapse's federation guide names a misconfigured reverse proxy as a common cause. Run the federation tester again, check sudo journalctl -u matrix-synapse for connection errors, and make sure the server can make outbound HTTPS connections.

Next steps

Frequently asked questions

Can I change my Matrix server name later?

No. Synapse's documentation states that the server_name cannot be changed later. It becomes part of every user ID and room alias, so decide on it, usually your main domain such as example.com, before you install.

Do I need to open port 8448?

Not with delegation. This guide publishes a .well-known/matrix/server file on your main domain that tells other servers to use matrix.example.com on port 443. Without delegation, other servers try port 8448 on the server_name host.

Why PostgreSQL instead of SQLite?

Synapse's documentation says SQLite should not be used in a production server and that almost all installations should use PostgreSQL. SQLite is only meant for testing.

How do other people get accounts?

Open registration stays off, which is Synapse's default. As the administrator you create accounts with register_new_matrix_user. If you later want self-service sign-up, read the registration options in Synapse's configuration manual, such as email verification or registration tokens, before you enable it.

Do voice and video calls need a TURN server?

Usually yes. Matrix calls use WebRTC, and users behind NAT or strict firewalls often cannot connect without a TURN relay. Synapse's TURN guide covers coturn and eturnal and the turn_uris and turn_shared_secret settings that hand TURN credentials to clients.

Sources

Generovat heslo

Please confirm