Sanitized mirror from private repository - 2026-04-04 11:21:25 UTC
Some checks failed
Documentation / Deploy to GitHub Pages (push) Has been cancelled
Documentation / Build Docusaurus (push) Has been cancelled

This commit is contained in:
Gitea Mirror Bot
2026-04-04 11:21:25 +00:00
commit 65d4234dd9
1319 changed files with 337177 additions and 0 deletions

View File

@@ -0,0 +1,171 @@
# Mastodon Federation Guide
## What is Federation?
Federation allows your Mastodon instance to communicate with other Mastodon instances (and other ActivityPub-compatible servers). Users can follow accounts on other servers, and posts are shared across the network.
## Federation Requirements
### 1. HTTPS (Required)
Federation only works over HTTPS. Cloudflare provides this automatically when proxying is enabled.
### 2. Correct Domain Configuration
```env
# .env.production
LOCAL_DOMAIN=mastodon.vish.gg
```
⚠️ **Warning**: Changing LOCAL_DOMAIN after setup will break existing accounts!
### 3. Webfinger Endpoint
Must respond correctly at:
```
https://mastodon.vish.gg/.well-known/webfinger?resource=acct:username@mastodon.vish.gg
```
Expected response:
```json
{
"subject": "acct:vish@mastodon.vish.gg",
"aliases": [
"https://mastodon.vish.gg/@vish",
"https://mastodon.vish.gg/users/vish"
],
"links": [
{
"rel": "http://webfinger.net/rel/profile-page",
"type": "text/html",
"href": "https://mastodon.vish.gg/@vish"
},
{
"rel": "self",
"type": "application/activity+json",
"href": "https://mastodon.vish.gg/users/vish"
}
]
}
```
### 4. ActivityPub Actor Endpoint
Must respond at:
```
https://mastodon.vish.gg/users/vish
```
With `Accept: application/activity+json` header.
## Testing Federation
### Test Webfinger (from external server)
```bash
curl "https://mastodon.vish.gg/.well-known/webfinger?resource=acct:vish@mastodon.vish.gg"
```
### Test Actor Endpoint
```bash
curl -H "Accept: application/activity+json" "https://mastodon.vish.gg/users/vish"
```
### Test Outbound Federation
Search for a remote user in your Mastodon instance:
1. Go to https://mastodon.vish.gg
2. Search for `@Gargron@mastodon.social`
3. If federation works, you'll see the user's profile
### Test from Another Instance
Go to any public Mastodon instance and search for:
```
@vish@mastodon.vish.gg
```
## Cloudflare Configuration
### Required Settings
1. **Proxy Status**: Orange cloud (Proxied) ✅
2. **SSL/TLS Mode**: Full (strict)
3. **Cache Level**: Standard (or Bypass for API endpoints)
### Origin Rules (if using non-standard ports)
Since nginx listens on port 8082, configure an origin rule:
**Rule**:
- If hostname equals `mastodon.vish.gg`
- Then: Override destination port to 8082
### Firewall Rules
Ensure port 8082 is accessible from Cloudflare IPs or use Cloudflare Tunnel.
## Common Federation Issues
### Issue: Remote users can't find your instance
**Cause**: DNS not properly configured or Cloudflare not proxying
**Fix**:
1. Verify DNS A record points to your server
2. Enable Cloudflare proxy (orange cloud)
3. Wait for DNS propagation
### Issue: Webfinger returns 301 redirect
**Normal behavior**: Mastodon redirects HTTP to HTTPS
**Solution**: Ensure requests come via HTTPS
### Issue: Cannot follow remote users
**Cause**: Outbound connections blocked
**Fix**:
1. Check firewall allows outbound HTTPS (443)
2. Verify sidekiq is running: `docker compose ps`
3. Check sidekiq logs: `docker compose logs sidekiq`
### Issue: Federation lag
**Cause**: High queue backlog in sidekiq
**Fix**:
```bash
# Check queue status
docker compose exec web bin/tootctl sidekiq status
# Clear dead jobs if needed
docker compose exec web bin/tootctl sidekiq kill
```
## Federation Debug Commands
```bash
# Check instance connectivity
cd /opt/mastodon
docker compose exec web bin/tootctl domains crawl mastodon.social
# Refresh a remote account
docker compose exec web bin/tootctl accounts refresh @Gargron@mastodon.social
# Clear delivery failures
docker compose exec web bin/tootctl domains purge <domain>
```
## Security Considerations
### Block/Allow Lists
Configure in Admin → Federation:
- Block specific domains
- Silence (limit) specific domains
- Allow specific domains (whitelist mode)
### Rate Limiting
Mastodon has built-in rate limiting for federation requests to prevent abuse.
## Monitoring Federation Health
### Check Sidekiq Queues
```bash
docker compose exec web bin/tootctl sidekiq stats
```
Healthy queues should have:
- Low `push` queue (outbound deliveries)
- Low `pull` queue (fetching remote content)
- Minimal retries
### Check Federation Stats
In Admin → Dashboard:
- Known instances count
- Active users (remote)
- Incoming/outgoing messages

View File

@@ -0,0 +1,21 @@
MIT License
Copyright (c) 2026 Vish
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.

View File

@@ -0,0 +1,300 @@
# Matrix Synapse Setup
This VM runs **two Matrix Synapse instances**:
| Instance | server_name | Domain | Federation | Purpose |
|----------|-------------|--------|------------|---------|
| **Primary** | `mx.vish.gg` | https://mx.vish.gg | ✅ Yes | Main server with federation |
| **Legacy** | `vish` | https://matrix.thevish.io | ❌ No | Historical data archive |
## Architecture
```
Internet
┌────────┴────────┐
│ Cloudflare │
└────────┬────────┘
┌─────────────┴─────────────┐
│ │
▼ ▼
┌─────────────────┐ ┌─────────────────┐
│ mx.vish.gg │ │ matrix.thevish.io│
│ (port 443) │ │ (port 443) │
└────────┬────────┘ └────────┬─────────┘
│ │
▼ ▼
┌─────────────────┐ ┌─────────────────┐
│ Synology Reverse│ │ Synology Reverse│
│ Proxy → :8082 │ │ Proxy → :8081 │
└────────┬────────┘ └────────┬─────────┘
│ │
└───────────┬───────────────┘
┌─────────────────────────────────────┐
│ Ubuntu VM (192.168.0.154) │
│ ┌──────────────┐ ┌──────────────┐ │
│ │ Nginx :8082 │ │ Nginx :8081 │ │
│ │ mx.vish.gg │ │ thevish.io │ │
│ └──────┬───────┘ └──────┬───────┘ │
│ │ │ │
│ ▼ ▼ │
│ ┌──────────────┐ ┌──────────────┐ │
│ │ Synapse:8018 │ │ Synapse:8008 │ │
│ │ mx.vish.gg │ │ vish │ │
│ └──────┬───────┘ └──────┬───────┘ │
│ │ │ │
│ ▼ ▼ │
│ ┌──────────────┐ ┌──────────────┐ │
│ │ synapse_mx │ │ synapse │ │
│ │ PostgreSQL │ │ PostgreSQL │ │
│ └──────────────┘ └──────────────┘ │
└─────────────────────────────────────┘
```
## Primary Server: mx.vish.gg
**This is the main server with federation enabled.**
### Configuration
- **Location**: `/opt/synapse-mx/`
- **Config**: `/opt/synapse-mx/homeserver.yaml`
- **Signing Key**: `/opt/synapse-mx/mx.vish.gg.signing.key`
- **Media Store**: `/opt/synapse-mx/media_store/`
- **Database**: `synapse_mx` (user: `synapse_mx`)
- **Port**: 8018 (Synapse) → 8082 (Nginx)
### User IDs
Users on this server have IDs like: `@username:mx.vish.gg`
### Federation
- ✅ Can communicate with matrix.org and other federated servers
- ✅ Can join public rooms on other servers
- ✅ Other users can find and message your users
### Starting the Server
```bash
sudo -u synapse /opt/synapse/venv/bin/python -m synapse.app.homeserver \
--config-path=/opt/synapse-mx/homeserver.yaml --daemonize
```
### Stopping the Server
```bash
sudo pkill -f 'config-path=/opt/synapse-mx'
```
## Legacy Server: vish (matrix.thevish.io)
**This server contains historical data and cannot federate.**
### Why No Federation?
The `server_name` is `vish` which is not a valid domain. Other Matrix servers cannot discover it because:
- No DNS record for `vish`
- Cannot serve `.well-known` at `https://vish/`
### Configuration
- **Location**: `/opt/synapse/`
- **Config**: `/opt/synapse/homeserver.yaml`
- **Signing Key**: `/opt/synapse/vish.signing.key`
- **Media Store**: `/opt/synapse/media_store/`
- **Database**: `synapse` (user: `synapse`)
- **Port**: 8008 (Synapse) → 8081 (Nginx)
### User IDs
Users on this server have IDs like: `@username:vish`
### Starting the Server
```bash
sudo -u synapse /opt/synapse/venv/bin/python -m synapse.app.homeserver \
--config-path=/opt/synapse/homeserver.yaml --daemonize
```
## TURN Server (coturn)
TURN server enables voice/video calls to work through NAT.
### Configuration
- **Config**: `/etc/turnserver.conf`
- **Ports**: 3479 (TURN), 5350 (TURNS), 49201-49250 (Media relay UDP)
- **Realm**: `matrix.thevish.io`
- **Auth Secret**: Shared with Synapse (`turn_shared_secret`)
### Key Settings
```ini
listening-port=3479
tls-listening-port=5350
listening-ip=0.0.0.0
external-ip=YOUR_WAN_IP/192.168.0.154
static-auth-secret=<shared-secret>
realm=matrix.thevish.io
min-port=49201
max-port=49250
```
### Port Forwarding Required
| Port | Protocol | Purpose |
|------|----------|---------|
| 3479 | TCP/UDP | TURN |
| 5350 | TCP/UDP | TURNS (TLS) |
| 49201-49250 | UDP | Media relay |
## Element Web
Element Web is served by Nginx for both instances.
### mx.vish.gg
- **Location**: `/opt/element/web/`
- **Config**: `/opt/element/web/config.json`
- **URL**: https://mx.vish.gg/
### matrix.thevish.io
- **Location**: `/opt/element/web-thevish/`
- **Config**: `/opt/element/web-thevish/config.json`
- **URL**: https://matrix.thevish.io/
## Nginx Configuration
### mx.vish.gg (port 8082)
Location: `/etc/nginx/sites-available/mx-vish-gg`
```nginx
server {
listen 8082;
server_name mx.vish.gg;
root /opt/element/web;
location /health { proxy_pass http://127.0.0.1:8018; }
location ~ ^(/_matrix|/_synapse/client) { proxy_pass http://127.0.0.1:8018; }
location /_matrix/federation { proxy_pass http://127.0.0.1:8018; }
location /.well-known/matrix/server { return 200 '{"m.server": "mx.vish.gg:443"}'; }
location /.well-known/matrix/client { return 200 '{"m.homeserver": {"base_url": "https://mx.vish.gg"}}'; }
location / { try_files $uri $uri/ /index.html; }
}
```
### matrix.thevish.io (port 8081)
Location: `/etc/nginx/sites-available/matrix-thevish`
```nginx
server {
listen 8081;
server_name matrix.thevish.io;
root /opt/element/web-thevish;
location /health { proxy_pass http://127.0.0.1:8008; }
location ~ ^(/_matrix|/_synapse/client) { proxy_pass http://127.0.0.1:8008; }
location /.well-known/matrix/server { return 200 '{"m.server": "matrix.thevish.io:443"}'; }
location /.well-known/matrix/client { return 200 '{"m.homeserver": {"base_url": "https://matrix.thevish.io"}}'; }
location / { try_files $uri $uri/ /index.html; }
}
```
## Synology Reverse Proxy
| Name | Source (HTTPS) | Destination (HTTP) |
|------|----------------|-------------------|
| mx_vish_gg | mx.vish.gg:443 | 192.168.0.154:8082 |
| matrix_thevish | matrix.thevish.io:443 | 192.168.0.154:8081 |
## Cloudflare DNS
| Type | Name | Content | Proxy |
|------|------|---------|-------|
| A | mx.vish.gg | YOUR_WAN_IP | ✅ Proxied |
| A | matrix.thevish.io | YOUR_WAN_IP | ✅ Proxied |
## Database Backup
### Backup mx.vish.gg
```bash
sudo -u postgres pg_dump -Fc synapse_mx > synapse_mx_backup_$(date +%Y%m%d).dump
```
### Backup legacy vish
```bash
sudo -u postgres pg_dump -Fc synapse > synapse_vish_backup_$(date +%Y%m%d).dump
```
### Restore
```bash
sudo -u postgres pg_restore -d <database_name> <backup_file.dump>
```
## Testing Federation
Use the Matrix Federation Tester:
```bash
curl -s "https://federationtester.matrix.org/api/report?server_name=mx.vish.gg" | python3 -c "
import sys, json
d = json.load(sys.stdin)
print(f'Federation OK: {d.get(\"FederationOK\", False)}')
"
```
## Creating Users
### Via registration (if enabled)
Go to https://mx.vish.gg and click "Create account"
### Via command line
```bash
cd /opt/synapse-mx
sudo -u synapse /opt/synapse/venv/bin/register_new_matrix_user \
-c /opt/synapse-mx/homeserver.yaml \
-u <username> -p <password> -a
```
## Troubleshooting
### Check if Synapse is running
```bash
ps aux | grep synapse.app
```
### View logs
```bash
# mx.vish.gg
tail -f /opt/synapse-mx/homeserver.log
# legacy vish
tail -f /opt/synapse/homeserver.log
```
### Test health endpoints
```bash
curl http://localhost:8018/health # mx.vish.gg
curl http://localhost:8008/health # legacy vish
```
### Restart nginx
```bash
sudo nginx -t && sudo systemctl reload nginx
```

View File

@@ -0,0 +1,197 @@
# Matrix Synapse + Element Web Bare-Metal Installation
Production-ready Matrix homeserver with Element Web client for Ubuntu 24.04 LTS.
## Features
- **Synapse** - Matrix homeserver with PostgreSQL backend
- **Element Web** - Modern web client (v1.12.8)
- **Coturn** - TURN server for voice/video calls
- **Federation** - Connect with other Matrix servers
- **Nginx** - Reverse proxy for HTTP traffic
- **Auto-validation** - YAML config validation during install
## Quick Install
```bash
# On a fresh Ubuntu 24.04 VM (run as root)
export DOMAIN="mx.example.com"
export ADMIN_USER="admin"
curl -sSL https://git.vish.gg/Vish/matrix-element/raw/branch/main/install-baremetal.sh | bash
```
### One-Liner (with defaults)
```bash
curl -sSL https://git.vish.gg/Vish/matrix-element/raw/branch/main/install-baremetal.sh | DOMAIN=mx.example.com bash
```
## Requirements
- Ubuntu 24.04 LTS
- 2+ CPU cores
- 4GB+ RAM
- 50GB+ disk space
- Domain with DNS pointing to your server
## Post-Installation
### 1. Configure Reverse Proxy
If using a reverse proxy (Synology, Cloudflare, etc.), point:
- `https://your-domain.com:443``http://server-ip:8080`
- Enable WebSocket support
### 2. Port Forwarding for TURN (Voice/Video Calls)
Forward these ports to your Matrix server:
| Port | Protocol | Purpose |
|------|----------|---------|
| 3479 | TCP/UDP | TURN |
| 5350 | TCP/UDP | TURNS (TLS) |
| 49201-49250 | UDP | Media relay |
### 3. Change Admin Password
Login at `https://your-domain.com` and change the default password immediately.
## Scripts
### Verify Installation
```bash
# Check health of all services
./verify-matrix.sh
```
This checks:
- All services (synapse, nginx, coturn, postgresql)
- Matrix Client and Federation APIs
- Well-known endpoints
- Element Web accessibility
- Database status
### Fix/Repair
```bash
# Diagnose and fix common issues
./fix-matrix.sh
```
This automatically fixes:
- YAML configuration errors in homeserver.yaml
- File ownership and permissions
- Stopped services
- Common configuration issues
### Backup
```bash
# Create a full backup
./backup-matrix.sh
# Or specify custom location
BACKUP_DIR=/mnt/backup ./backup-matrix.sh
```
Creates:
- PostgreSQL database dump
- Configuration files
- Media files
- Signing keys
- TURN configuration
### Update
```bash
# Update Synapse and Element to latest versions
./update-matrix.sh
```
This will:
1. Create a backup (optional)
2. Update Synapse via pip
3. Run database migrations
4. Download latest Element Web
5. Restart services
## Configuration Files
| File | Purpose |
|------|---------|
| `/opt/synapse/homeserver.yaml` | Main Synapse config |
| `/opt/synapse/*.signing.key` | Server signing key (CRITICAL - backup!) |
| `/opt/element/web/config.json` | Element Web config |
| `/etc/turnserver.conf` | TURN server config |
| `/etc/nginx/sites-available/matrix` | Nginx config |
| `/root/.matrix_secrets` | Passwords and secrets |
## Service Management
```bash
# Check status
systemctl status synapse nginx coturn
# Restart services
systemctl restart synapse
systemctl restart nginx
systemctl restart coturn
# View logs
journalctl -u synapse -f
journalctl -u coturn -f
```
## Federation Testing
Test federation status:
```bash
curl https://federationtester.matrix.org/api/report?server_name=your-domain.com
```
## Adding Users
```bash
# Create a new user
cd /opt/synapse
source venv/bin/activate
register_new_matrix_user -c homeserver.yaml http://localhost:8008
# Create admin user
register_new_matrix_user -c homeserver.yaml -a http://localhost:8008
```
## Troubleshooting
### Check if services are running
```bash
systemctl status synapse nginx coturn postgresql
```
### Test Matrix API locally
```bash
curl http://localhost:8008/_matrix/client/versions
```
### Test well-known endpoints
```bash
curl https://your-domain.com/.well-known/matrix/server
curl https://your-domain.com/.well-known/matrix/client
```
### Check Synapse logs
```bash
journalctl -u synapse -n 100
tail -f /opt/synapse/homeserver.log
```
## Security Notes
- Change the admin password immediately after installation
- Keep `/opt/synapse/*.signing.key` secure and backed up
- Consider enabling rate limiting in production
- Review `/opt/synapse/homeserver.yaml` for security settings
## License
MIT License

View File

@@ -0,0 +1,259 @@
# Deployment Documentation
Complete setup guide for the Ubuntu VM Homelab with Mastodon, Mattermost, and Matrix/Element.
## Server Access
```
IP: YOUR_WAN_IP
SSH Port: 65533
Username: test
Password: "REDACTED_PASSWORD"
```
## Service Credentials
### Mastodon Admin
- **Username**: vish
- **Email**: your-email@example.com
- **Password**: `c16a0236e5a5da1e0c80bb296a290fc3`
- **URL**: https://mastodon.vish.gg
### Mattermost
- **URL**: https://mm.crista.love
- **Admin**: (configured during first access)
### Matrix/Element
- **URL**: https://mx.vish.gg
- **Homeserver**: mx.vish.gg
## PostgreSQL Configuration
PostgreSQL 16 is configured to allow Docker container connections:
```
# /etc/postgresql/16/main/pg_hba.conf
host all all 172.17.0.0/16 md5
host all all 0.0.0.0/0 md5
# /etc/postgresql/16/main/postgresql.conf
listen_addresses = '*'
```
### Database Credentials
| Database | User | Password |
|----------|------|----------|
| mastodon_production | mastodon | mastodon_pass_2026 |
| mattermost | mmuser | (check /opt/mattermost/config/config.json) |
| synapse | synapse | (check /opt/synapse/homeserver.yaml) |
## Nginx Configuration
### Ports
- **8080**: Matrix/Element (mx.vish.gg)
- **8081**: Mattermost (mm.crista.love)
- **8082**: Mastodon (mastodon.vish.gg)
### Site Configs
```
/etc/nginx/sites-enabled/
├── mastodon -> /etc/nginx/sites-available/mastodon
├── matrix -> /etc/nginx/sites-available/matrix
└── mattermost -> /etc/nginx/sites-available/mattermost
```
## Mastodon Setup Details
### Directory Structure
```
/opt/mastodon/
├── docker-compose.yml
├── .env.production
├── public/
│ └── system/ # Media uploads
└── redis/ # Redis data
```
### Environment Variables
```env
LOCAL_DOMAIN=mastodon.vish.gg
SINGLE_USER_MODE=false
# Database
DB_HOST=172.17.0.1
DB_PORT=5432
DB_NAME=mastodon_production
DB_USER=mastodon
DB_PASS="REDACTED_PASSWORD"
# Redis
REDIS_HOST=redis
REDIS_PORT=6379
# SMTP (Gmail) - CONFIGURED AND WORKING ✅
SMTP_SERVER=smtp.gmail.com
SMTP_PORT=587
SMTP_LOGIN=your-email@example.com
SMTP_PASSWORD="REDACTED_PASSWORD"
SMTP_AUTH_METHOD=plain
SMTP_ENABLE_STARTTLS=auto
SMTP_FROM_ADDRESS="Mastodon <notifications@mastodon.vish.gg>"
# Search
ES_ENABLED=false
```
### Common Commands
```bash
# View logs
cd /opt/mastodon && docker compose logs -f
# Restart services
cd /opt/mastodon && docker compose restart
# Run admin commands
cd /opt/mastodon && docker compose exec web bin/tootctl <command>
# Create new user
docker compose run --rm web bin/tootctl accounts create USERNAME --email=EMAIL --confirmed --role=Owner
# Database migration
docker compose run --rm web bundle exec rake db:migrate
```
## Mattermost Setup Details
### Directory Structure
```
/opt/mattermost/
├── config/
│ └── config.json
├── data/
├── logs/
├── plugins/
└── client/plugins/
```
### Docker Command
```bash
docker run -d --name mattermost \
-p 8065:8065 \
-v /opt/mattermost/config:/mattermost/config \
-v /opt/mattermost/data:/mattermost/data \
-v /opt/mattermost/logs:/mattermost/logs \
-v /opt/mattermost/plugins:/mattermost/plugins \
--restart=always \
mattermost/mattermost-team-edition:11.3
```
## Matrix/Synapse Setup Details
### Directory Structure
```
/opt/synapse/
├── homeserver.yaml
├── *.signing.key
└── media_store/
/opt/element/web/
└── (Element Web static files)
```
### Synapse Service
```bash
# Status
systemctl status matrix-synapse
# Restart
systemctl restart matrix-synapse
# Logs
journalctl -u matrix-synapse -f
```
## Cloudflare Configuration
For each service, configure Cloudflare:
1. **DNS Records** (A records pointing to VM public IP)
- mastodon.vish.gg
- mm.crista.love
- mx.vish.gg
2. **Origin Rules** (Route to correct nginx port)
- mastodon.vish.gg → Port 8082
- mm.crista.love → Port 8081
- mx.vish.gg → Port 8080
3. **SSL/TLS**: Full (strict)
## Federation (Mastodon)
Federation requires:
1. ✅ Proper LOCAL_DOMAIN in .env.production
2. ✅ HTTPS via Cloudflare
3. ✅ Webfinger endpoint responding at `/.well-known/webfinger`
4. ⏳ DNS properly configured
Test federation:
```bash
# From another server
curl "https://mastodon.vish.gg/.well-known/webfinger?resource=acct:vish@mastodon.vish.gg"
```
## SMTP Configuration (Gmail)
To send emails via Gmail:
1. Enable 2-Factor Authentication on your Google account
2. Generate an App Password:
- Go to https://myaccount.google.com/apppasswords
- Create a new app password for "Mail"
3. Update `/opt/mastodon/.env.production`:
```
SMTP_PASSWORD="REDACTED_PASSWORD"
```
4. Restart Mastodon:
```bash
cd /opt/mastodon && docker compose restart
```
## Backup Locations
```
/backup/
├── YYYYMMDD_HHMMSS/
│ ├── mattermost.sql
│ ├── synapse.sql
│ ├── mastodon.sql
│ ├── mastodon_media.tar.gz
│ ├── mattermost_data.tar.gz
│ └── synapse_data.tar.gz
```
## Troubleshooting
### Mastodon 403 Forbidden
- Normal when accessing with wrong Host header
- Always access via proper domain or use `-H "Host: mastodon.vish.gg"`
### Federation Not Working
- Check Cloudflare proxy is enabled
- Verify DNS resolves correctly
- Test webfinger endpoint externally
### Database Connection Errors
- Verify PostgreSQL is listening on all interfaces
- Check pg_hba.conf allows Docker network
- Restart PostgreSQL: `systemctl restart postgresql`
### Container Won't Start
```bash
# Check logs
docker logs <container_name>
# Check Docker network
docker network ls
docker network inspect mastodon_internal_network
```

View File

@@ -0,0 +1,178 @@
# SMTP Email Configuration
Guide for configuring email delivery for Mastodon and Mattermost.
## Gmail SMTP Setup
### Prerequisites
1. Google account with 2-Factor Authentication enabled
2. App Password generated for "Mail"
### Generate Gmail App Password
1. Go to [Google Account Security](https://myaccount.google.com/security)
2. Enable 2-Step Verification if not already enabled
3. Go to [App Passwords](https://myaccount.google.com/apppasswords)
4. Select "Mail" and your device
5. Click "Generate"
6. Copy the 16-character password
### Mastodon Configuration
Edit `/opt/mastodon/.env.production`:
```env
# SMTP Configuration (Gmail)
SMTP_SERVER=smtp.gmail.com
SMTP_PORT=587
SMTP_LOGIN=your-email@example.com
SMTP_PASSWORD="REDACTED_PASSWORD"
SMTP_AUTH_METHOD=plain
SMTP_OPENSSL_VERIFY_MODE=none
SMTP_ENABLE_STARTTLS=auto
SMTP_FROM_ADDRESS="Mastodon <notifications@mastodon.vish.gg>"
```
Apply changes:
```bash
cd /opt/mastodon && docker compose restart
```
### Test Email Delivery
```bash
# Send test email
cd /opt/mastodon
docker compose exec web bin/tootctl accounts modify vish --confirm
# Or trigger password reset
# Go to login page and click "Forgot password"
```
## Mattermost Email Configuration
Edit `/opt/mattermost/config/config.json`:
```json
{
"EmailSettings": {
"EnableSignUpWithEmail": true,
"EnableSignInWithEmail": true,
"EnableSignInWithUsername": true,
"SendEmailNotifications": true,
"RequireEmailVerification": false,
"FeedbackName": "Mattermost",
"FeedbackEmail": "notifications@mm.crista.love",
"SMTPUsername": "your-email@example.com",
"SMTPPassword": "your_16_char_app_password",
"SMTPServer": "smtp.gmail.com",
"SMTPPort": "587",
"ConnectionSecurity": "STARTTLS",
"SendPushNotifications": true
}
}
```
Restart Mattermost:
```bash
docker restart mattermost
```
## Alternative: SendGrid
### Setup
1. Create SendGrid account at https://sendgrid.com
2. Generate API key with "Mail Send" permission
### Mastodon Configuration
```env
SMTP_SERVER=smtp.sendgrid.net
SMTP_PORT=587
SMTP_LOGIN=apikey
SMTP_PASSWORD="REDACTED_PASSWORD"
SMTP_AUTH_METHOD=plain
SMTP_OPENSSL_VERIFY_MODE=peer
SMTP_ENABLE_STARTTLS=auto
SMTP_FROM_ADDRESS="Mastodon <notifications@mastodon.vish.gg>"
```
## Alternative: Mailgun
### Setup
1. Create Mailgun account at https://mailgun.com
2. Verify your domain
3. Get SMTP credentials
### Mastodon Configuration
```env
SMTP_SERVER=smtp.mailgun.org
SMTP_PORT=587
SMTP_LOGIN=postmaster@mg.yourdomain.com
SMTP_PASSWORD="REDACTED_PASSWORD"
SMTP_AUTH_METHOD=plain
SMTP_OPENSSL_VERIFY_MODE=peer
SMTP_ENABLE_STARTTLS=auto
SMTP_FROM_ADDRESS="Mastodon <notifications@mastodon.vish.gg>"
```
## Troubleshooting
### Check SMTP Connection
```bash
# Test from container
docker compose exec web bash -c "echo 'test' | openssl s_client -connect smtp.gmail.com:587 -starttls smtp"
```
### Check Sidekiq Mail Queue
```bash
# View failed email jobs
docker compose exec web bin/tootctl sidekiq status
```
### Common Errors
#### "Username and Password not accepted"
- Verify App Password is correct (not your regular password)
- Ensure 2FA is enabled on Google account
- Check no extra spaces in password
#### "Connection refused"
- Firewall blocking outbound port 587
- Try port 465 with SSL instead
#### "Certificate verify failed"
- Set `SMTP_OPENSSL_VERIFY_MODE=none` (less secure)
- Or ensure CA certificates are up to date
### Gmail-Specific Issues
#### "Less secure app access"
- Not needed when using App Passwords
- App Passwords bypass this requirement
#### "Critical security alert"
- Normal for first connection from new IP
- Confirm it was you in Google Security settings
## Email Content Customization
### Mastodon
Email templates are in the Mastodon source code. Custom templates require forking.
### Mattermost
Edit in System Console → Site Configuration → Customization
- Support Email
- Notification Footer
- Custom Branding
## SPF/DKIM/DMARC
For better deliverability, configure DNS records:
### SPF Record
```
TXT @ "v=spf1 include:_spf.google.com ~all"
```
### Note on Gmail Sending
When using Gmail SMTP, emails are sent "via gmail.com" which has good deliverability. Custom domain email requires additional DNS setup.

View File

@@ -0,0 +1,119 @@
#!/bin/bash
# =============================================================================
# Matrix Synapse Backup Script
# Creates a complete backup for migration
# =============================================================================
# Run as root
set -e
BACKUP_DIR="${BACKUP_DIR:-/opt/synapse/backups}"
TIMESTAMP=$(date +%Y%m%d_%H%M%S)
BACKUP_NAME="matrix_backup_${TIMESTAMP}"
BACKUP_PATH="${BACKUP_DIR}/${BACKUP_NAME}"
echo "=========================================="
echo "Matrix Synapse Backup Script"
echo "Backup location: ${BACKUP_PATH}"
echo "=========================================="
mkdir -p "${BACKUP_PATH}"
# 1. Backup PostgreSQL
echo "[1/5] Backing up PostgreSQL database..."
sudo -u postgres pg_dump -Fc synapse > "${BACKUP_PATH}/synapse.dump"
echo " Database: $(du -h ${BACKUP_PATH}/synapse.dump | cut -f1)"
# 2. Backup Synapse config and keys
echo "[2/5] Backing up configuration..."
cp /opt/synapse/homeserver.yaml "${BACKUP_PATH}/"
cp /opt/synapse/*.signing.key "${BACKUP_PATH}/" 2>/dev/null || true
cp /opt/synapse/*.log.config "${BACKUP_PATH}/" 2>/dev/null || true
cp /root/.matrix_secrets "${BACKUP_PATH}/" 2>/dev/null || true
# 3. Backup media
echo "[3/5] Backing up media files (this may take a while)..."
if [ -d /opt/synapse/media_store ]; then
tar -czf "${BACKUP_PATH}/media_store.tar.gz" -C /opt/synapse media_store
echo " Media: $(du -h ${BACKUP_PATH}/media_store.tar.gz | cut -f1)"
else
echo " No media directory found"
fi
# 4. Backup Element config
echo "[4/5] Backing up Element config..."
cp /opt/element/web/config.json "${BACKUP_PATH}/element_config.json" 2>/dev/null || true
# 5. Backup TURN config
echo "[5/5] Backing up TURN config..."
cp /etc/turnserver.conf "${BACKUP_PATH}/" 2>/dev/null || true
# Create restore instructions
cat > "${BACKUP_PATH}/RESTORE.md" << 'RESTORE'
# Matrix Restore Instructions
## On the new server:
1. Run the install script first (it will create a fresh install)
2. Stop services:
```
systemctl stop synapse nginx coturn
```
3. Restore database:
```
sudo -u postgres dropdb synapse
sudo -u postgres createdb -O synapse -E UTF8 -l C -T template0 synapse
sudo -u postgres pg_restore -d synapse synapse.dump
```
4. Restore config files:
```
cp homeserver.yaml /opt/synapse/
cp *.signing.key /opt/synapse/
cp *.log.config /opt/synapse/
chown -R synapse:synapse /opt/synapse
```
5. Restore media:
```
cd /opt/synapse
tar -xzf /path/to/backup/media_store.tar.gz
chown -R synapse:synapse media_store
```
6. Restore TURN config:
```
cp turnserver.conf /etc/turnserver.conf
```
7. Restore Element config:
```
cp element_config.json /opt/element/web/config.json
```
8. Start services:
```
systemctl start coturn nginx synapse
```
RESTORE
# Create archive
echo ""
echo "Creating final archive..."
cd "${BACKUP_DIR}"
tar -czf "${BACKUP_NAME}.tar.gz" "${BACKUP_NAME}"
rm -rf "${BACKUP_NAME}"
FINAL_SIZE=$(du -h "${BACKUP_DIR}/${BACKUP_NAME}.tar.gz" | cut -f1)
echo ""
echo "=========================================="
echo "✅ Backup Complete!"
echo "=========================================="
echo ""
echo "Backup file: ${BACKUP_DIR}/${BACKUP_NAME}.tar.gz"
echo "Size: ${FINAL_SIZE}"
echo ""
echo "Download: scp root@server:${BACKUP_DIR}/${BACKUP_NAME}.tar.gz ."

View File

@@ -0,0 +1,196 @@
#!/bin/bash
# =============================================================================
# Matrix Synapse Fix/Repair Script
# Diagnoses and fixes common issues
# =============================================================================
# Run as root
echo "=========================================="
echo "Matrix Synapse Fix/Repair Tool"
echo "=========================================="
# Check root
if [[ $EUID -ne 0 ]]; then
echo "This script must be run as root"
exit 1
fi
FIXED=0
ERRORS=0
# 1. Check and fix YAML configuration
echo ""
echo "[1/6] Checking Synapse configuration..."
if [ -f /opt/synapse/homeserver.yaml ]; then
if python3 -c "import yaml; yaml.safe_load(open('/opt/synapse/homeserver.yaml'))" 2>/dev/null; then
echo " ✓ homeserver.yaml is valid YAML"
else
echo " ✗ homeserver.yaml has YAML errors!"
echo " Creating backup at /opt/synapse/homeserver.yaml.broken"
cp /opt/synapse/homeserver.yaml /opt/synapse/homeserver.yaml.broken
# Try to fix common issues
echo " Attempting automatic fix..."
# Remove duplicate keys and fix indentation issues
python3 << 'PYFIX'
import yaml
import re
try:
with open('/opt/synapse/homeserver.yaml', 'r') as f:
content = f.read()
# Try to parse and re-write
# First, try to fix common issues
lines = content.split('\n')
fixed_lines = []
in_list = False
for line in lines:
# Skip empty turn_uris followed by list items not indented under it
if line.strip() == 'turn_uris:':
in_list = True
fixed_lines.append(line)
elif in_list and line.strip().startswith('- "turn:'):
fixed_lines.append(' ' + line.strip())
elif in_list and line.strip().startswith('- "turns:'):
fixed_lines.append(' ' + line.strip())
elif in_list and not line.strip().startswith('-') and line.strip():
in_list = False
fixed_lines.append(line)
else:
fixed_lines.append(line)
fixed_content = '\n'.join(fixed_lines)
# Validate the fix
yaml.safe_load(fixed_content)
with open('/opt/synapse/homeserver.yaml', 'w') as f:
f.write(fixed_content)
print(" ✓ Configuration fixed automatically")
except Exception as e:
print(f" ✗ Auto-fix failed: {e}")
print(" Please manually fix /opt/synapse/homeserver.yaml")
print(" Backup saved at /opt/synapse/homeserver.yaml.broken")
PYFIX
FIXED=$((FIXED + 1))
fi
else
echo " ✗ homeserver.yaml not found!"
ERRORS=$((ERRORS + 1))
fi
# 2. Check file permissions
echo ""
echo "[2/6] Checking file permissions..."
if [ -d /opt/synapse ]; then
OWNER=$(stat -c '%U' /opt/synapse)
if [ "$OWNER" = "synapse" ]; then
echo " ✓ /opt/synapse owned by synapse user"
else
echo " ✗ Fixing ownership of /opt/synapse..."
chown -R synapse:synapse /opt/synapse
FIXED=$((FIXED + 1))
fi
# Check config file permissions
if [ -f /opt/synapse/homeserver.yaml ]; then
PERMS=$(stat -c '%a' /opt/synapse/homeserver.yaml)
if [ "$PERMS" = "600" ] || [ "$PERMS" = "640" ]; then
echo " ✓ homeserver.yaml has correct permissions"
else
echo " ✗ Fixing homeserver.yaml permissions..."
chmod 600 /opt/synapse/homeserver.yaml
FIXED=$((FIXED + 1))
fi
fi
fi
# 3. Check services
echo ""
echo "[3/6] Checking services..."
for svc in postgresql synapse nginx coturn; do
if systemctl is-active --quiet $svc 2>/dev/null; then
echo "$svc is running"
else
echo "$svc is not running, attempting to start..."
systemctl start $svc 2>/dev/null
sleep 2
if systemctl is-active --quiet $svc; then
echo "$svc started successfully"
FIXED=$((FIXED + 1))
else
echo " ✗ Failed to start $svc"
echo " Check logs: journalctl -u $svc -n 50"
ERRORS=$((ERRORS + 1))
fi
fi
done
# 4. Check database connection
echo ""
echo "[4/6] Checking database..."
if sudo -u postgres psql -c "SELECT 1" synapse > /dev/null 2>&1; then
echo " ✓ PostgreSQL connection successful"
else
echo " ✗ Cannot connect to synapse database"
ERRORS=$((ERRORS + 1))
fi
# 5. Check nginx configuration
echo ""
echo "[5/6] Checking nginx configuration..."
if nginx -t 2>/dev/null; then
echo " ✓ Nginx configuration is valid"
else
echo " ✗ Nginx configuration has errors"
nginx -t
ERRORS=$((ERRORS + 1))
fi
# 6. Check API endpoints
echo ""
echo "[6/6] Checking API endpoints..."
sleep 1
if curl -sf http://localhost:8008/_matrix/client/versions > /dev/null 2>&1; then
echo " ✓ Matrix Client API responding"
else
echo " ✗ Matrix Client API not responding"
echo " Checking Synapse logs..."
journalctl -u synapse -n 10 --no-pager 2>/dev/null | tail -5
ERRORS=$((ERRORS + 1))
fi
LISTEN_PORT=$(grep -oP '^ listen \K\d+' /etc/nginx/sites-enabled/matrix 2>/dev/null | head -1 || echo "8080")
if curl -sf http://localhost:$LISTEN_PORT/ > /dev/null 2>&1; then
echo " ✓ Element Web accessible on port $LISTEN_PORT"
else
echo " ✗ Element Web not accessible"
ERRORS=$((ERRORS + 1))
fi
# Summary
echo ""
echo "=========================================="
if [ $ERRORS -eq 0 ]; then
if [ $FIXED -eq 0 ]; then
echo "✅ All checks passed! No issues found."
else
echo "✅ Fixed $FIXED issue(s). All checks now pass."
echo ""
echo "You may want to restart services:"
echo " systemctl restart synapse nginx"
fi
else
echo "⚠️ Found $ERRORS error(s) that need manual attention."
echo ""
echo "Common fixes:"
echo " - Check logs: journalctl -u synapse -f"
echo " - Validate YAML: python3 -c \"import yaml; yaml.safe_load(open('/opt/synapse/homeserver.yaml'))\""
echo " - Restart services: systemctl restart postgresql synapse nginx coturn"
fi
echo "=========================================="
exit $ERRORS

View File

@@ -0,0 +1,377 @@
#!/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"

View File

@@ -0,0 +1,103 @@
#!/bin/bash
# =============================================================================
# Matrix Synapse + Element Web Update Script
# =============================================================================
# Run as root
set -e
echo "=========================================="
echo "Matrix Synapse + Element Update Script"
echo "=========================================="
# Check current versions
CURRENT_SYNAPSE=$(/opt/synapse/venv/bin/python -c "import synapse; print(synapse.__version__)" 2>/dev/null || echo "unknown")
CURRENT_ELEMENT=$(cat /opt/element/web/version 2>/dev/null || ls /opt/element/ | grep -oP 'v[\d.]+' | head -1 || echo "unknown")
echo "Current Synapse: $CURRENT_SYNAPSE"
echo "Current Element: $CURRENT_ELEMENT"
# Get latest versions
echo ""
echo "Checking for updates..."
LATEST_ELEMENT=$(curl -s https://api.github.com/repos/element-hq/element-web/releases/latest | jq -r '.tag_name')
echo "Latest Element: $LATEST_ELEMENT"
read -p "Proceed with update? (y/N) " -n 1 -r
echo
if [[ ! $REPLY =~ ^[Yy]$ ]]; then
echo "Update cancelled."
exit 0
fi
# Backup first
echo ""
echo "[1/4] Creating backup..."
if [ -f ./backup-matrix.sh ]; then
./backup-matrix.sh
elif [ -f /opt/matrix-scripts/backup-matrix.sh ]; then
/opt/matrix-scripts/backup-matrix.sh
else
echo "Backup script not found, skipping..."
fi
# Update Synapse
echo ""
echo "[2/4] Updating Synapse..."
systemctl stop synapse
cd /opt/synapse
sudo -u synapse bash << 'UPDATE_SYNAPSE'
source venv/bin/activate
pip install --upgrade matrix-synapse psycopg2-binary lxml 'prometheus-client<0.21'
UPDATE_SYNAPSE
# Run database migrations
echo ""
echo "[3/4] Running database migrations..."
sudo -u synapse /opt/synapse/venv/bin/python -m synapse.app.homeserver \
--config-path /opt/synapse/homeserver.yaml \
--generate-keys-if-missing
# Update Element Web
echo ""
echo "[4/4] Updating Element Web..."
cd /opt/element
if [ -n "$LATEST_ELEMENT" ] && [ "$LATEST_ELEMENT" != "null" ]; then
# Backup old config
cp web/config.json /tmp/element_config_backup.json
# Download new version
wget -q "https://github.com/element-hq/element-web/releases/download/$LATEST_ELEMENT/element-$LATEST_ELEMENT.tar.gz"
# Remove old, extract new
rm -rf web
tar xzf "element-$LATEST_ELEMENT.tar.gz"
mv "element-$LATEST_ELEMENT" web
rm "element-$LATEST_ELEMENT.tar.gz"
# Restore config
cp /tmp/element_config_backup.json web/config.json
echo "Element updated to $LATEST_ELEMENT"
else
echo "Could not determine latest Element version, skipping Element update"
fi
# Start services
echo ""
echo "Starting services..."
systemctl start synapse
systemctl restart nginx
# Verify
sleep 3
NEW_SYNAPSE=$(/opt/synapse/venv/bin/python -c "import synapse; print(synapse.__version__)" 2>/dev/null || echo "unknown")
echo ""
echo "=========================================="
echo "✅ Update Complete!"
echo "=========================================="
echo ""
echo "Synapse: $CURRENT_SYNAPSE$NEW_SYNAPSE"
echo "Element: $CURRENT_ELEMENT$LATEST_ELEMENT"
echo ""
echo "Please verify your instance is working correctly."

View File

@@ -0,0 +1,126 @@
#!/bin/bash
# =============================================================================
# Matrix Synapse + Element Web Verification Script
# =============================================================================
# Run as root or with sudo
echo "=========================================="
echo "Matrix/Element Health Check"
echo "=========================================="
echo ""
FAILED=0
WARN=0
# Load domain from secrets if available
if [ -f /root/.matrix_secrets ]; then
source /root/.matrix_secrets
echo "Domain: ${DOMAIN:-unknown}"
fi
echo ""
echo "[Service Status]"
for svc in synapse nginx coturn postgresql; do
STATUS=$(systemctl is-active $svc 2>/dev/null || echo "not-found")
if [ "$STATUS" = "active" ]; then
echo "$svc: running"
elif [ "$STATUS" = "not-found" ]; then
echo " - $svc: not installed"
else
echo "$svc: $STATUS"
FAILED=1
fi
done
echo ""
echo "[Matrix API]"
# Client API
if curl -sf http://localhost:8008/_matrix/client/versions > /dev/null 2>&1; then
VERSION_COUNT=$(curl -s http://localhost:8008/_matrix/client/versions | python3 -c "import sys,json; print(len(json.load(sys.stdin).get('versions',[])))" 2>/dev/null || echo "0")
echo " ✓ Client API: responding ($VERSION_COUNT protocol versions)"
else
echo " ✗ Client API: not responding"
FAILED=1
fi
# Federation API
FED_RESULT=$(curl -sf http://localhost:8008/_matrix/federation/v1/version 2>/dev/null)
if [ -n "$FED_RESULT" ]; then
SYNAPSE_VER=$(echo "$FED_RESULT" | python3 -c "import sys,json; print(json.load(sys.stdin).get('server',{}).get('version','unknown'))" 2>/dev/null)
echo " ✓ Federation API: responding (Synapse $SYNAPSE_VER)"
else
echo " ✗ Federation API: not responding"
FAILED=1
fi
echo ""
echo "[Well-Known Endpoints]"
# Check nginx port
LISTEN_PORT=$(grep -oP 'listen \K\d+' /etc/nginx/sites-enabled/matrix 2>/dev/null | head -1 || echo "8080")
SERVER_WK=$(curl -sf http://localhost:$LISTEN_PORT/.well-known/matrix/server 2>/dev/null)
if [ -n "$SERVER_WK" ]; then
echo " ✓ /.well-known/matrix/server: $SERVER_WK"
else
echo " ✗ /.well-known/matrix/server: not configured"
WARN=1
fi
CLIENT_WK=$(curl -sf http://localhost:$LISTEN_PORT/.well-known/matrix/client 2>/dev/null)
if [ -n "$CLIENT_WK" ]; then
echo " ✓ /.well-known/matrix/client: configured"
else
echo " ✗ /.well-known/matrix/client: not configured"
WARN=1
fi
echo ""
echo "[Element Web]"
if curl -sf http://localhost:$LISTEN_PORT/ > /dev/null 2>&1; then
echo " ✓ Element Web: accessible on port $LISTEN_PORT"
else
echo " ✗ Element Web: not accessible"
FAILED=1
fi
# Check Element config
if [ -f /opt/element/web/config.json ]; then
HOMESERVER=$(python3 -c "import json; print(json.load(open('/opt/element/web/config.json')).get('default_server_config',{}).get('m.homeserver',{}).get('base_url','not set'))" 2>/dev/null)
echo " ✓ Element config: homeserver=$HOMESERVER"
else
echo " ✗ Element config: /opt/element/web/config.json not found"
WARN=1
fi
echo ""
echo "[TURN Server]"
if systemctl is-active --quiet coturn; then
TURN_PORT=$(grep -oP '^listening-port=\K\d+' /etc/turnserver.conf 2>/dev/null | head -1 || echo "3479")
echo " ✓ Coturn: running on port $TURN_PORT"
else
echo " - Coturn: not running (voice/video calls may not work behind NAT)"
WARN=1
fi
echo ""
echo "[Database]"
if systemctl is-active --quiet postgresql; then
DB_SIZE=$(sudo -u postgres psql -t -c "SELECT pg_size_pretty(pg_database_size('synapse'));" 2>/dev/null | xargs)
echo " ✓ PostgreSQL: running (synapse db: ${DB_SIZE:-unknown})"
else
echo " ✗ PostgreSQL: not running"
FAILED=1
fi
echo ""
echo "=========================================="
if [ $FAILED -eq 0 ] && [ $WARN -eq 0 ]; then
echo "✅ All checks passed!"
elif [ $FAILED -eq 0 ]; then
echo "⚠️ Passed with warnings"
else
echo "❌ Some checks failed"
fi
echo "=========================================="
exit $FAILED