#!/bin/bash # ============================================================================= # Matrix Synapse + Element Web Bare-Metal Install Script # For Ubuntu 24.04 LTS # ============================================================================= # Usage: # export DOMAIN="mx.example.com" # export ADMIN_USER="admin" # export ADMIN_EMAIL="admin@example.com" # curl -sSL https://git.vish.gg/Vish/matrix-element/raw/branch/main/install-baremetal.sh | bash # # Run as root on a fresh Ubuntu 24.04 VM # ============================================================================= set -e # Configuration DOMAIN="${DOMAIN:-mx.example.com}" ADMIN_USER="${ADMIN_USER:-admin}" ADMIN_EMAIL="${ADMIN_EMAIL:-admin@example.com}" TURN_DOMAIN="${TURN_DOMAIN:-$DOMAIN}" TURN_PORT="${TURN_PORT:-3479}" TURN_TLS_PORT="${TURN_TLS_PORT:-5350}" TURN_MIN_PORT="${TURN_MIN_PORT:-49201}" TURN_MAX_PORT="${TURN_MAX_PORT:-49250}" ELEMENT_VERSION="${ELEMENT_VERSION:-v1.12.8}" LISTEN_PORT="${LISTEN_PORT:-8080}" echo "==========================================" echo "Matrix Synapse + Element Web Installer" echo "==========================================" echo "Domain: $DOMAIN" echo "Admin: $ADMIN_USER" echo "==========================================" # Check root if [[ $EUID -ne 0 ]]; then echo "This script must be run as root" exit 1 fi # Update system echo "[1/10] Updating system..." apt update && apt upgrade -y # Install dependencies echo "[2/10] Installing dependencies..." apt install -y postgresql postgresql-contrib nginx coturn \ python3-pip python3-venv python3-dev build-essential \ libffi-dev libssl-dev libjpeg-dev libxslt1-dev \ curl wget git jq # Create synapse user echo "[3/10] Creating synapse user..." useradd -r -m -d /opt/synapse -s /bin/bash synapse 2>/dev/null || true mkdir -p /opt/synapse /opt/element chown synapse:synapse /opt/synapse # Setup PostgreSQL echo "[4/10] Setting up PostgreSQL..." DB_PASS="REDACTED_PASSWORD" rand -hex 16) sudo -u postgres psql -c "CREATE USER synapse WITH PASSWORD 'REDACTED_PASSWORD';" 2>/dev/null || \ sudo -u postgres psql -c "ALTER USER synapse WITH PASSWORD 'REDACTED_PASSWORD';" sudo -u postgres psql -c "CREATE DATABASE synapse ENCODING 'UTF8' LC_COLLATE='C' LC_CTYPE='C' template=template0 OWNER synapse;" 2>/dev/null || true # Install Synapse echo "[5/10] Installing Synapse..." sudo -u synapse bash << SYNAPSE_INSTALL cd /opt/synapse python3 -m venv venv source venv/bin/activate pip install --upgrade pip setuptools wheel pip install matrix-synapse psycopg2-binary lxml 'prometheus-client<0.21' SYNAPSE_INSTALL # Generate config echo "[6/10] Generating Synapse configuration..." cd /opt/synapse sudo -u synapse /opt/synapse/venv/bin/python -m synapse.app.homeserver \ --server-name "$DOMAIN" \ --config-path homeserver.yaml \ --generate-config \ --report-stats=no # Get generated secrets REG_SECRET=$(grep 'registration_shared_secret' homeserver.yaml | head -1 | awk '{print $2}') MAC_SECRET=$(grep 'macaroon_secret_key' homeserver.yaml | head -1 | awk '{print $2}') FORM_SECRET=$(grep 'form_secret' homeserver.yaml | head -1 | awk '{print $2}') TURN_SECRET=$(openssl rand -hex 32) # Create production config cat > /opt/synapse/homeserver.yaml << YAML server_name: "$DOMAIN" pid_file: /opt/synapse/homeserver.pid public_baseurl: https://$DOMAIN/ listeners: - port: 8008 tls: false type: http x_forwarded: true resources: - names: [client, federation] compress: false database: name: psycopg2 args: user: synapse password: "REDACTED_PASSWORD" database: synapse host: localhost cp_min: 5 cp_max: 10 log_config: "/opt/synapse/$DOMAIN.log.config" media_store_path: /opt/synapse/media_store signing_key_path: "/opt/synapse/$DOMAIN.signing.key" trusted_key_servers: - server_name: "matrix.org" registration_shared_secret: $REG_SECRET macaroon_secret_key: $MAC_SECRET form_secret: $FORM_SECRET enable_registration: false enable_registration_without_verification: false turn_uris: - "turn:$TURN_DOMAIN:$TURN_PORT?transport=udp" - "turn:$TURN_DOMAIN:$TURN_PORT?transport=tcp" - "turns:$TURN_DOMAIN:$TURN_TLS_PORT?transport=udp" - "turns:$TURN_DOMAIN:$TURN_TLS_PORT?transport=tcp" turn_shared_secret: "$TURN_SECRET" turn_user_lifetime: 86400000 turn_allow_guests: true max_upload_size: 100M url_preview_enabled: true url_preview_ip_range_blacklist: - '127.0.0.0/8' - '10.0.0.0/8' - '172.16.0.0/12' - '192.168.0.0/16' - '100.64.0.0/10' - '169.254.0.0/16' - '::1/128' - 'fe80::/64' - 'fc00::/7' suppress_key_server_warning: true enable_metrics: false report_stats: false YAML # Validate YAML configuration echo "Validating Synapse configuration..." python3 -c "import yaml; yaml.safe_load(open('/opt/synapse/homeserver.yaml'))" || { echo "ERROR: Invalid YAML in homeserver.yaml" exit 1 } mkdir -p /opt/synapse/media_store chown -R synapse:synapse /opt/synapse # Configure coturn echo "[7/10] Configuring TURN server..." cat > /etc/turnserver.conf << TURN listening-port=$TURN_PORT tls-listening-port=$TURN_TLS_PORT fingerprint use-auth-secret static-auth-secret=$TURN_SECRET realm=$DOMAIN total-quota=100 bps-capacity=0 stale-nonce=600 no-multicast-peers min-port=$TURN_MIN_PORT max-port=$TURN_MAX_PORT log-file=/var/log/turnserver.log TURN # Download Element Web echo "[8/10] Installing Element Web..." cd /opt/element wget -q "https://github.com/element-hq/element-web/releases/download/$ELEMENT_VERSION/element-$ELEMENT_VERSION.tar.gz" tar xzf "element-$ELEMENT_VERSION.tar.gz" mv "element-$ELEMENT_VERSION" web rm "element-$ELEMENT_VERSION.tar.gz" cat > /opt/element/web/config.json << ELEMENT { "default_server_config": { "m.homeserver": { "base_url": "https://$DOMAIN", "server_name": "$DOMAIN" } }, "disable_guests": true, "default_theme": "dark", "room_directory": { "servers": ["matrix.org", "$DOMAIN"] } } ELEMENT # Configure nginx echo "[9/10] Configuring nginx..." cat > /etc/nginx/sites-available/matrix << NGINX server { listen $LISTEN_PORT; listen [::]:$LISTEN_PORT; server_name $DOMAIN; root /opt/element/web; index index.html; location ~ ^(/_matrix|/_synapse/client) { proxy_pass http://127.0.0.1:8008; proxy_set_header X-Forwarded-For \$remote_addr; proxy_set_header X-Forwarded-Proto \$scheme; proxy_set_header Host \$host; client_max_body_size 100M; proxy_http_version 1.1; } location /_matrix/federation { proxy_pass http://127.0.0.1:8008; proxy_set_header X-Forwarded-For \$remote_addr; proxy_set_header X-Forwarded-Proto \$scheme; proxy_set_header Host \$host; client_max_body_size 100M; } location /.well-known/matrix/server { default_type application/json; return 200 '{"m.server": "$DOMAIN:443"}'; } location /.well-known/matrix/client { default_type application/json; add_header Access-Control-Allow-Origin *; return 200 '{"m.homeserver": {"base_url": "https://$DOMAIN"}}'; } location / { try_files \$uri \$uri/ /index.html; } } NGINX ln -sf /etc/nginx/sites-available/matrix /etc/nginx/sites-enabled/matrix rm -f /etc/nginx/sites-enabled/default nginx -t # Create systemd service cat > /etc/systemd/system/synapse.service << SERVICE [Unit] Description=Synapse Matrix Homeserver After=network.target postgresql.service [Service] Type=notify User=synapse Group=synapse WorkingDirectory=/opt/synapse ExecStart=/opt/synapse/venv/bin/python -m synapse.app.homeserver --config-path=/opt/synapse/homeserver.yaml ExecReload=/bin/kill -HUP \$MAINPID Restart=on-failure RestartSec=5 [Install] WantedBy=multi-user.target SERVICE # Start services echo "[10/10] Starting services..." systemctl daemon-reload systemctl enable --now postgresql nginx coturn synapse # Create admin user sleep 3 ADMIN_PASS="REDACTED_PASSWORD" rand -hex 12) cd /opt/synapse sudo -u synapse /opt/synapse/venv/bin/register_new_matrix_user \ -c homeserver.yaml \ -u "$ADMIN_USER" \ -p "$ADMIN_PASS" \ -a \ http://localhost:8008 # Save secrets cat > /root/.matrix_secrets << SECRETS DOMAIN=$DOMAIN DB_PASS="REDACTED_PASSWORD" TURN_SECRET=$TURN_SECRET ADMIN_USER=$ADMIN_USER ADMIN_PASS="REDACTED_PASSWORD" SECRETS chmod 600 /root/.matrix_secrets # Download helper scripts echo "Downloading helper scripts..." REPO_BASE="https://git.vish.gg/Vish/matrix-element/raw/branch/main" mkdir -p /opt/matrix-scripts for script in verify-matrix.sh fix-matrix.sh backup-matrix.sh update-matrix.sh; do curl -sSL "$REPO_BASE/$script" -o "/opt/matrix-scripts/$script" 2>/dev/null || true chmod +x "/opt/matrix-scripts/$script" 2>/dev/null || true done echo "Helper scripts installed to /opt/matrix-scripts/" # Verify installation echo "" echo "Verifying installation..." sleep 2 VERIFY_FAILED=0 # Check services for svc in synapse nginx coturn postgresql; do if systemctl is-active --quiet $svc; then echo "✓ $svc is running" else echo "✗ $svc is NOT running" VERIFY_FAILED=1 fi done # Check Matrix API if curl -sf http://localhost:8008/_matrix/client/versions > /dev/null; then echo "✓ Matrix API responding" else echo "✗ Matrix API not responding" VERIFY_FAILED=1 fi # Check Element Web if curl -sf http://localhost:$LISTEN_PORT/ > /dev/null; then echo "✓ Element Web accessible" else echo "✗ Element Web not accessible" VERIFY_FAILED=1 fi echo "" echo "==========================================" if [ $VERIFY_FAILED -eq 0 ]; then echo "✅ Matrix Installation Complete!" else echo "⚠️ Installation complete with warnings" fi echo "==========================================" echo "" echo "Domain: $DOMAIN" echo "Admin User: @$ADMIN_USER:$DOMAIN" echo "Admin Password: "REDACTED_PASSWORD" echo "" echo "Listening on port $LISTEN_PORT (HTTP)" echo "" echo "Next steps:" echo "1. Configure reverse proxy: HTTPS:443 → HTTP:$LISTEN_PORT" echo "2. Forward TURN ports: $TURN_PORT, $TURN_TLS_PORT, $TURN_MIN_PORT-$TURN_MAX_PORT" echo "3. Login at https://$DOMAIN and change password" echo "" echo "Secrets saved to /root/.matrix_secrets" echo "" echo "Helper scripts installed to /opt/matrix-scripts/" echo " ./verify-matrix.sh - Check installation health" echo " ./fix-matrix.sh - Diagnose and fix issues" echo " ./backup-matrix.sh - Create full backup" echo " ./update-matrix.sh - Update Synapse and Element" echo "" echo "Useful commands:" echo " systemctl status synapse nginx coturn" echo " journalctl -u synapse -f" echo " curl http://localhost:8008/_matrix/client/versions"