Skip to content

Self-hosted Matrix messaging with LiveKit A/V calls, Element X, PostgreSQL, Redis. Blue/green deployment, production-ready, Docker-based.

Notifications You must be signed in to change notification settings

speakASAP/messenger

Repository files navigation

Messenger Service - Matrix Self-Hosted Deployment

Complete self-hosted Matrix messaging service with Synapse homeserver, PostgreSQL database, Element X clients, and LiveKit SFU with built-in TURN server for A/V calls.

Overview

This service can be deployed in two modes:

1. Microservice Mode (Recommended for Infrastructure Integration)

  • Integrated with nginx-microservice infrastructure
  • Blue/green deployment for zero-downtime updates
  • Automatic Matrix location block injection
  • All configuration managed in codebase
  • Use when: You have the microservices infrastructure

2. Standalone Mode (Recommended for Simple Deployments)

  • Self-contained deployment with built-in nginx
  • Automatic Let's Encrypt SSL certificate management
  • No external dependencies
  • Direct docker-compose deployment
  • Use when: You want a simple, standalone installation

Key Features:

  • Synapse Matrix homeserver
  • PostgreSQL database
  • Redis cache
  • LiveKit SFU for A/V calls
  • Element X web client
  • Automatic SSL certificate management
  • Non-root container execution

Architecture

  • Synapse - Matrix homeserver handling messaging, federation, and room management
  • PostgreSQL - Database for Synapse (optimized for production)
  • Redis - Caching and worker coordination
  • LiveKit - Modern SFU (Selective Forwarding Unit) with built-in TURN server for A/V calls
  • Element X Web - Modern web-based client
  • nginx-microservice - External reverse proxy with SSL termination and blue/green deployment (handled by nginx-microservice infrastructure)

Prerequisites

Common requirements:

  • Docker and Docker Compose installed
  • Domain name configured in DNS pointing to your server IP
  • Server with at least 2-4GB RAM (low-resource optimized)
  • Ports open: 80, 443 (TCP), 7882 (UDP), 50000-60000 (UDP)

For Microservice Mode:

  • nginx-microservice running and accessible at /home/statex/nginx-microservice
  • Service placed in /home/statex/messenger (or path specified in service registry)
  • Access to production server via SSH (ssh statex)

For Standalone Mode:

  • Root or sudo access (for initial setup)
  • No external dependencies required

Deployment Modes

Microservice Mode

This mode integrates with the microservices infrastructure:

  • Deployment: Uses nginx-microservice blue/green deployment system via scripts/deploy.sh
  • Configuration: All nginx configuration is managed in this codebase (nginx/gateway-proxy.conf)
  • Matrix Location Blocks: Automatically injected into nginx configs during deployment
  • Service Registry: Auto-created by nginx-microservice deployment script
  • Network: Uses nginx-network external network managed by nginx-microservice
  • SSL: Handled automatically by nginx-microservice with Let's Encrypt

See: docs/DEPLOYMENT.md for detailed microservice deployment guide.

Standalone Mode

This mode provides a self-contained deployment:

  • Deployment: Direct docker-compose deployment via scripts/deploy-standalone.sh
  • Nginx: Built-in nginx reverse proxy with SSL termination
  • SSL: Automatic Let's Encrypt certificate management via certbot container
  • Network: Internal Docker network (messenger-network)
  • Configuration: All services in docker-compose.standalone.yml

See: docs/STANDALONE_DEPLOYMENT.md for detailed standalone deployment guide.

Quick Start

1. Clone and Setup

git clone <repository-url>
cd matrix-deployment

2. Generate Secrets

./scripts/generate-secrets.sh > .env.tmp
# Review and add to .env file

3. Configure Environment

Copy .env.example to .env and fill in all required values:

cp .env.example .env
nano .env

Generate secrets:

./scripts/generate-secrets.sh > .env.tmp
# Review and add secrets to .env

Required variables:

  • DOMAIN - Your Matrix domain (e.g., messenger.example.com)
  • ELEMENT_BASE_URL - Element web URL (usually same as DOMAIN)
  • LIVEKIT_URL - LiveKit server URL (usually same as DOMAIN)
  • POSTGRES_PASSWORD - Strong database password (generate with openssl rand -base64 32)
  • SYNAPSE_SECRET_KEY - Generated secret key (generate with openssl rand -base64 32)
  • SYNAPSE_REGISTRATION_SECRET - Registration secret (generate with openssl rand -base64 32)
  • LIVEKIT_API_KEY - LiveKit API key (generate with openssl rand -hex 16)
  • LIVEKIT_API_SECRET - LiveKit API secret (generate with openssl rand -base64 32)
  • REDIS_PASSWORD - Redis password (generate with openssl rand -base64 32)
  • CONTAINER_USER_UID - User ID for all containers (MUST NOT be 0/root, detect with ./scripts/detect-user.sh)
  • CONTAINER_USER_GID - Group ID for all containers (MUST NOT be 0/root, detect with ./scripts/detect-user.sh)
  • LETSENCRYPT_EMAIL - Email for Let's Encrypt notifications (required for standalone deployment)

See: docs/ENVIRONMENT_VARIABLES.md for complete environment variables reference.

IMPORTANT - Container User Configuration: All containers run as a non-root user for security. You must configure CONTAINER_USER_UID and CONTAINER_USER_GID in your .env file to match your host user's UID/GID.

To detect your user's UID/GID automatically:

./scripts/detect-user.sh

This will output the values to add to your .env file. Alternatively, run id to see your UID/GID manually.

Security Requirement: Containers are strictly forbidden from running as root (UID/GID 0). The setup script will validate this and fail if root is detected.

4. Configure DNS

Set up DNS A record pointing to your server IP:

  • messenger.statex.cz → Your server IP

Note: SSL certificates and reverse proxy are handled by nginx-microservice. No need to configure nginx or certbot in this service.

5. Update Configuration Files

Run the setup script to automatically update configuration files with values from .env:

./scripts/setup-config.sh

This will update:

  • synapse/config/homeserver.yaml - Database passwords, Redis password, registration secret, domain names
  • element/config.json - Domain names
  • livekit/config.yaml - Domain name
  • nginx/conf.d/*.conf - Domain names

IMPORTANT: After running the script, manually update livekit/config.yaml with your LiveKit API keys:

  • Replace API_KEY with your LIVEKIT_API_KEY from .env
  • Replace API_SECRET with your LIVEKIT_API_SECRET from .env

6. Deploy

Choose your deployment mode:

Option A: Microservice Deployment (with nginx-microservice infrastructure)

This microservice is deployed using the nginx-microservice blue/green deployment system. The deployment process is automated via the wrapper script scripts/deploy.sh.

Deployment Process:

  1. Pull latest changes from repository
  2. Deploy via nginx-microservice using blue/green deployment
  3. Inject Matrix location blocks automatically from nginx/gateway-proxy.conf
  4. Reload nginx to apply changes

Recommended: Use the wrapper script (automatically handles all steps):

cd /home/statex/messenger
./scripts/deploy.sh

The wrapper script (scripts/deploy.sh) will:

  • Pull latest changes from git repository
  • Deploy via nginx-microservice (./scripts/blue-green/deploy-smart.sh messenger)
  • Automatically inject Matrix location blocks from nginx/gateway-proxy.conf
  • Reload nginx to apply changes

Alternative: Manual deployment (if you need more control):

cd /home/statex/nginx-microservice
./scripts/blue-green/deploy-smart.sh messenger
cd /home/statex/messenger
./scripts/post-deploy-nginx.sh
cd /home/statex/nginx-microservice
./scripts/reload-nginx.sh

What the deployment does:

  • Auto-creates service registry in nginx-microservice/service-registry/messenger.json
  • Detects services from docker-compose.blue.yml and docker-compose.green.yml
  • Builds and starts containers (blue/green strategy)
  • Configures nginx routing automatically
  • Injects Matrix-specific location blocks (/_matrix, /_synapse, /.well-known/matrix/client)
  • Handles SSL certificates automatically via Let's Encrypt
  • Performs health checks
  • Switches traffic with zero downtime

Matrix Location Blocks: The file nginx/gateway-proxy.conf contains Matrix-specific nginx location blocks that are automatically injected into the nginx gateway configs during deployment. This ensures Matrix API requests are properly routed to Synapse. The ${ACTIVE_COLOR} variable is automatically substituted with the active deployment color (blue or green).

Option B: Standalone Deployment (without microservices infrastructure)

For standalone deployment without nginx-microservice:

cd /path/to/messenger
./scripts/deploy-standalone.sh

This will:

  • Start all services including built-in nginx
  • Obtain SSL certificate from Let's Encrypt
  • Configure automatic certificate renewal
  • Set up all Matrix location blocks

See: docs/STANDALONE_DEPLOYMENT.md for complete standalone deployment guide.

7. Generate Synapse Configuration

Use the initialization script:

./scripts/init-synapse.sh

Or manually:

docker exec -it ${SERVICE_NAME:-messenger}-synapse python -m synapse.app.homeserver \
    --config-path /data/homeserver.yaml \
    --generate-config \
    --report-stats=no \
    --server-name="${DOMAIN}"

docker exec -it ${SERVICE_NAME:-messenger}-synapse python -m synapse.app.homeserver \
    --config-path /data/homeserver.yaml \
    --generate-keys

Note: SSL certificates are automatically managed by nginx-microservice. No manual certificate setup needed.

8. Install LiveKit Integration

./scripts/install-livekit-integration.sh

Configure the integration in Synapse config and restart:

docker compose restart synapse

9. Create First User

docker exec -it ${SERVICE_NAME:-messenger}-synapse register_new_matrix_user \
    -c /data/homeserver.yaml \
    -a -u admin -p <password> http://localhost:${SYNAPSE_PORT:-3708}

11. Test A/V Calls

  1. Access Element X web client at https://messenger.statex.cz
  2. Log in with your admin account
  3. Start a call with another user
  4. Test group calls with 3+ participants

Client Setup

Mobile (iOS/Android)

  1. Download Element X from App Store/Play Store
  2. Open app settings
  3. Configure custom server URL: https://messenger.statex.cz
  4. A/V calls will automatically use LiveKit via Matrix integration

Desktop (Windows/macOS/Linux)

  1. Download Element X desktop app
  2. Configure custom server URL: https://messenger.statex.cz
  3. A/V calls will automatically use LiveKit via Matrix integration

Web

  1. Access https://messenger.statex.cz
  2. Log in with your account
  3. WebRTC calls work in modern browsers via LiveKit

Maintenance

Backup

cd /home/statex/messenger

# Backup PostgreSQL
docker exec ${SERVICE_NAME:-messenger}-postgres pg_dump -U synapse synapse > backup.sql

# Backup Synapse data
tar -czf synapse-backup.tar.gz synapse/data/

Update

To update the microservice:

cd /home/statex/messenger
# Update code
git pull

# Update .env if needed
nano .env

# Run setup script if configs changed
./scripts/setup-config.sh

# Redeploy (automatically handles Matrix location blocks)
./scripts/deploy.sh

The blue/green deployment system ensures zero-downtime updates.

View Logs

For blue/green deployments, use the appropriate compose file:

# Active deployment (check which color is active)
docker compose -f docker-compose.green.yml -p messenger_green logs -f synapse
docker compose -f docker-compose.green.yml -p messenger_green logs -f livekit
docker compose -f docker-compose.green.yml -p messenger_green logs -f frontend

# Or view by container name
docker logs messenger-synapse-green -f
docker logs messenger-livekit-green -f
docker logs messenger-element-green -f

See docs/LOGGING.md for detailed logging information.

Troubleshooting

A/V Calls Not Working

  1. Fix "Call is not supported" / "MISSING_MATRIX_RTC_FOCUS" error:

    ./scripts/fix-rtc-config.sh

    This script will:

    • Verify required environment variables (LIVEKIT_URL, LIVEKIT_API_KEY, LIVEKIT_API_SECRET)
    • Install matrix-livekit-integration module if needed
    • Regenerate homeserver.yaml with RTC focus configuration
    • Restart Synapse to apply changes
  2. Check LiveKit server accessibility:

    ./scripts/test-livekit-server.sh
  3. Verify firewall rules for UDP ports (configured in nginx-microservice):

    • 7882 (TURN/STUN)
    • 50000-60000 (RTC)
  4. Check LiveKit logs:

    docker logs ${SERVICE_NAME:-messenger}-livekit
  5. Verify LiveKit integration in Synapse:

    docker logs ${SERVICE_NAME:-messenger}-synapse | grep -i livekit
    docker logs ${SERVICE_NAME:-messenger}-synapse | grep -i rtc

SSL Certificate Issues

SSL certificates are managed by nginx-microservice. Check nginx-microservice logs:

cd /path/to/nginx-microservice
docker compose logs nginx
docker compose logs certbot

Database Connection Issues

  1. Check PostgreSQL health:

    docker exec ${SERVICE_NAME:-messenger}-postgres pg_isready -U synapse
  2. View PostgreSQL logs:

    docker logs ${SERVICE_NAME:-messenger}-postgres

Volume Permission Issues

If containers fail to start due to permission errors on bind-mounted volumes:

  1. Check your .env file has correct CONTAINER_USER_UID and CONTAINER_USER_GID:

    grep CONTAINER_USER .env
  2. Set correct ownership on bind-mounted directories:

    # Replace 1000:1000 with your CONTAINER_USER_UID:GID from .env
    sudo chown -R 1000:1000 ./synapse/data
  3. Verify container user matches host user:

    ./scripts/detect-user.sh
    # Compare output with values in .env
  4. Check container is not running as root:

    docker exec ${SERVICE_NAME:-messenger}-synapse id
    # Should show non-zero UID/GID

Security

  • All containers run as non-root user - Configured via CONTAINER_USER_UID and CONTAINER_USER_GID in .env
  • Change all default passwords
  • Restrict registration after initial setup
  • Use strong secrets (32+ characters)
  • Keep Docker images updated
  • Configure firewall rules
  • Enable rate limiting
  • Use HTTPS only

Container User Security

All containers are configured to run as a non-root user for security. This is enforced at multiple levels:

  1. docker-compose.yml - All services have user: "${CONTAINER_USER_UID:-1000}:${CONTAINER_USER_GID:-1000}" directive
  2. setup-config.sh - Validates that UID/GID are not 0 (root) and fails if root is detected
  3. Default fallback - If not configured, defaults to UID/GID 1000 (non-root)

Volume Permissions: When using bind mounts (e.g., ./synapse/data), ensure the directories have correct permissions for the container user:

# Set ownership to match container user (replace 1000:1000 with your CONTAINER_USER_UID:GID)
sudo chown -R 1000:1000 ./synapse/data

For named volumes (postgres_data, redis_data), Docker handles permissions automatically.

Deployment Script

The main deployment script is scripts/deploy.sh. This wrapper script automates the entire deployment process:

  1. Pulls latest changes from git repository
  2. Deploys via nginx-microservice using blue/green deployment
  3. Injects Matrix location blocks from nginx/gateway-proxy.conf
  4. Reloads nginx to apply changes

Usage:

cd /home/statex/messenger
./scripts/deploy.sh

Environment Variables:

The script reads from .env file:

  • SERVICE_NAME - Service name (default: messenger)
  • DOMAIN - Domain name (default: messenger.statex.cz)
  • NGINX_MICROSERVICE_DIR - Path to nginx-microservice (default: /home/statex/nginx-microservice)

What it does:

  • Calls nginx-microservice/scripts/blue-green/deploy-smart.sh to deploy
  • Calls scripts/post-deploy-nginx.sh to inject Matrix location blocks
  • Calls nginx-microservice/scripts/reload-nginx.sh to reload nginx

See docs/DEPLOYMENT.md for detailed deployment information.

Documentation

Resources

License

[Your License Here]

About

Self-hosted Matrix messaging with LiveKit A/V calls, Element X, PostgreSQL, Redis. Blue/green deployment, production-ready, Docker-based.

Topics

Resources

Stars

Watchers

Forks

Releases

No releases published

Packages

No packages published

Languages