Sanitized mirror from private repository - 2026-04-05 08:31:50 UTC
This commit is contained in:
28
hosts/vms/matrix-ubuntu-vm/.gitignore
vendored
Normal file
28
hosts/vms/matrix-ubuntu-vm/.gitignore
vendored
Normal file
@@ -0,0 +1,28 @@
|
||||
# Environment files with secrets
|
||||
.env
|
||||
.env.production
|
||||
*.env.local
|
||||
|
||||
# Database dumps
|
||||
*.sql
|
||||
*.dump
|
||||
|
||||
# Logs
|
||||
*.log
|
||||
logs/
|
||||
|
||||
# Media files
|
||||
public/system/
|
||||
media_store/
|
||||
|
||||
# Docker volumes
|
||||
redis/
|
||||
data/
|
||||
|
||||
# OS files
|
||||
.DS_Store
|
||||
Thumbs.db
|
||||
|
||||
# IDE
|
||||
.vscode/
|
||||
.idea/
|
||||
341
hosts/vms/matrix-ubuntu-vm/README.md
Normal file
341
hosts/vms/matrix-ubuntu-vm/README.md
Normal file
@@ -0,0 +1,341 @@
|
||||
# Ubuntu VM Homelab
|
||||
|
||||
Self-hosted communication platform with Mastodon, Mattermost, and Matrix/Element on a single Ubuntu VM sharing PostgreSQL.
|
||||
|
||||
## Current Deployment Status
|
||||
|
||||
| Service | Status | Domain | Internal Port | Nginx Port |
|
||||
|---------|--------|--------|---------------|------------|
|
||||
| ✅ Mastodon | Running | mastodon.vish.gg | 3000, 4000 | 8082 |
|
||||
| ✅ Mattermost | Running | mm.crista.love | 8065 | 8081 |
|
||||
| ✅ Matrix (mx.vish.gg) | Running | mx.vish.gg | 8018 | 8082 |
|
||||
| ✅ Matrix (vish - legacy) | Running | matrix.thevish.io | 8008 | 8081 |
|
||||
| ✅ PostgreSQL | Running | - | 5432 | - |
|
||||
| ✅ Redis | Running | - | 6379 | - |
|
||||
| ✅ TURN (coturn) | Running | mx.vish.gg:3479 | 3479 | - |
|
||||
|
||||
## VM Specifications
|
||||
|
||||
- **OS**: Ubuntu 24.04.4 LTS (x86_64)
|
||||
- **Hostname**: matrix-ubuntu
|
||||
- **LAN IP**: 192.168.0.154 (static) — `ssh ubuntu-matrix`
|
||||
- **Tailscale IP**: 100.85.21.51
|
||||
- **SSH user**: test
|
||||
- **RAM**: 7.7 GB
|
||||
- **CPU**: 4 cores
|
||||
- **Storage**: 96 GB
|
||||
- **Network**: Static IP set via netplan (`/etc/netplan/99-static.yaml`), cloud-init network management disabled
|
||||
|
||||
## Architecture
|
||||
|
||||
```
|
||||
┌─────────────────────────────────────────────────────────────┐
|
||||
│ Cloudflare Proxy │
|
||||
└─────────────────────────────────────────────────────────────┘
|
||||
│
|
||||
▼
|
||||
┌─────────────────────────────────────────────────────────────┐
|
||||
│ Nginx │
|
||||
│ ┌─────────────┐ ┌─────────────┐ ┌─────────────┐ │
|
||||
│ │ :8080 │ │ :8081 │ │ :8082 │ │
|
||||
│ │ Matrix │ │ Mattermost │ │ Mastodon │ │
|
||||
│ └─────────────┘ └─────────────┘ └─────────────┘ │
|
||||
└─────────────────────────────────────────────────────────────┘
|
||||
│ │ │
|
||||
▼ ▼ ▼
|
||||
┌─────────────┐ ┌─────────────┐ ┌─────────────┐
|
||||
│ Synapse │ │ Mattermost │ │ Mastodon │
|
||||
│ :8008 │ │ Docker │ │ Docker │
|
||||
│ + Element │ │ :8065 │ │ :3000 │
|
||||
└─────────────┘ └─────────────┘ │ :4000 │
|
||||
│ │ └─────────────┘
|
||||
│ │ │
|
||||
└───────────────────┴──────────────────┘
|
||||
│
|
||||
▼
|
||||
┌─────────────────┐
|
||||
│ PostgreSQL │
|
||||
│ :5432 │
|
||||
│ │
|
||||
│ - synapse │
|
||||
│ - mattermost │
|
||||
│ - mastodon │
|
||||
└─────────────────┘
|
||||
```
|
||||
|
||||
## Databases
|
||||
|
||||
All services share the same PostgreSQL 16 server:
|
||||
|
||||
| Database | User | Purpose |
|
||||
|----------|------|---------|
|
||||
| synapse | synapse | Matrix homeserver (vish - legacy) |
|
||||
| synapse_mx | synapse_mx | Matrix homeserver (mx.vish.gg - federated) |
|
||||
| mattermost | mmuser | Mattermost |
|
||||
| mastodon_production | mastodon | Mastodon |
|
||||
|
||||
## Docker Containers
|
||||
|
||||
```
|
||||
NAMES IMAGE STATUS
|
||||
mastodon-streaming-1 ghcr.io/mastodon/mastodon-streaming:v4.5.7 Up
|
||||
mastodon-web-1 ghcr.io/mastodon/mastodon:v4.5.7 Up
|
||||
mastodon-sidekiq-1 ghcr.io/mastodon/mastodon:v4.5.7 Up
|
||||
mastodon-redis-1 redis:7-alpine Up
|
||||
mattermost mattermost/mattermost-team-edition:11.4 Up (healthy)
|
||||
```
|
||||
|
||||
## Systemd Services (bare-metal)
|
||||
|
||||
```
|
||||
UNIT SERVICE VERSION
|
||||
synapse.service Synapse (legacy) 1.148.0 — /opt/synapse, port 8008
|
||||
synapse-mx.service Synapse (primary) 1.148.0 — /opt/synapse-mx, port 8018
|
||||
```
|
||||
|
||||
Both Synapse instances share the venv at `/opt/synapse/venv/`.
|
||||
|
||||
## Quick Start
|
||||
|
||||
1. Clone this repo to your VM
|
||||
2. Copy environment templates and edit with your values
|
||||
3. Run the setup script
|
||||
|
||||
```bash
|
||||
git clone https://git.vish.gg/Vish/Ubuntu-vm-homelab.git
|
||||
cd Ubuntu-vm-homelab
|
||||
./scripts/setup.sh
|
||||
```
|
||||
|
||||
## Directory Structure
|
||||
|
||||
```
|
||||
Ubuntu-vm-homelab/
|
||||
├── mastodon/
|
||||
│ ├── docker-compose.yml
|
||||
│ └── .env.production.template
|
||||
├── mattermost/
|
||||
│ ├── docker-compose.yml
|
||||
│ └── config.json.template
|
||||
├── matrix-element/
|
||||
│ ├── homeserver.yaml.template
|
||||
│ └── element-config.json.template
|
||||
├── nginx/
|
||||
│ ├── mastodon.conf
|
||||
│ ├── mattermost.conf
|
||||
│ └── matrix.conf
|
||||
├── scripts/
|
||||
│ ├── setup.sh
|
||||
│ ├── backup.sh
|
||||
│ └── update.sh
|
||||
└── README.md
|
||||
```
|
||||
|
||||
## Credentials
|
||||
|
||||
Stored securely on the server:
|
||||
- `/opt/mastodon/.env.production` - Mastodon secrets
|
||||
- `/opt/mattermost/config/config.json` - Mattermost config
|
||||
- `/opt/synapse/homeserver.yaml` - Matrix config
|
||||
|
||||
## Cloudflare Setup
|
||||
|
||||
Each service requires a DNS record pointing to the VM's public IP with Cloudflare proxy enabled.
|
||||
Configure origin rules to route to the correct nginx port.
|
||||
|
||||
## Maintenance
|
||||
|
||||
### Backup
|
||||
```bash
|
||||
./scripts/backup.sh
|
||||
```
|
||||
|
||||
### View Logs
|
||||
```bash
|
||||
# Mastodon
|
||||
cd /opt/mastodon && docker compose logs -f
|
||||
|
||||
# Mattermost
|
||||
docker logs -f mattermost
|
||||
|
||||
# Matrix (mx.vish.gg)
|
||||
tail -f /opt/synapse-mx/homeserver.log
|
||||
|
||||
# Matrix (legacy vish)
|
||||
tail -f /opt/synapse/homeserver.log
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Updating Services
|
||||
|
||||
### Update Mastodon
|
||||
|
||||
```bash
|
||||
cd /opt/mastodon
|
||||
|
||||
# Pull latest images
|
||||
docker compose pull
|
||||
|
||||
# Stop services
|
||||
docker compose down
|
||||
|
||||
# Run database migrations
|
||||
docker compose run --rm web bundle exec rails db:migrate
|
||||
|
||||
# Precompile assets (if needed)
|
||||
docker compose run --rm web bundle exec rails assets:precompile
|
||||
|
||||
# Start services
|
||||
docker compose up -d
|
||||
|
||||
# Verify
|
||||
docker compose ps
|
||||
```
|
||||
|
||||
**Check for release notes:** https://github.com/mastodon/mastodon/releases
|
||||
|
||||
### Update Mattermost
|
||||
|
||||
```bash
|
||||
cd /opt/mattermost
|
||||
|
||||
# Check current version
|
||||
docker exec mattermost mattermost version
|
||||
|
||||
# Pull latest image
|
||||
docker compose pull
|
||||
|
||||
# Stop and restart
|
||||
docker compose down
|
||||
docker compose up -d
|
||||
|
||||
# Verify
|
||||
docker logs mattermost | head -20
|
||||
```
|
||||
|
||||
**Check for release notes:** https://docs.mattermost.com/about/mattermost-server-releases.html
|
||||
|
||||
### Update Matrix Synapse (both instances share the same venv)
|
||||
|
||||
Both instances use `/opt/synapse/venv/` — upgrade once, restart both.
|
||||
|
||||
```bash
|
||||
# Check current version
|
||||
curl -s http://localhost:8018/_synapse/admin/v1/server_version
|
||||
|
||||
# Upgrade (pin to a specific version, e.g. 1.148.0)
|
||||
sudo /opt/synapse/venv/bin/pip install 'matrix-synapse==1.148.0'
|
||||
|
||||
# Restart both services
|
||||
sudo systemctl restart synapse synapse-mx
|
||||
|
||||
# Verify
|
||||
curl -s http://localhost:8008/_synapse/admin/v1/server_version # legacy
|
||||
curl -s http://localhost:8018/_synapse/admin/v1/server_version # mx
|
||||
```
|
||||
|
||||
**Check for release notes:** https://github.com/element-hq/synapse/releases
|
||||
|
||||
> **Note:** If startup fails with `InsufficientPrivilege: must be owner of table`, see
|
||||
> the DB ownership fix in `docs/MATRIX.md#db-ownership-fix`.
|
||||
|
||||
### Update Element Web
|
||||
|
||||
```bash
|
||||
# Check latest version at https://github.com/element-hq/element-web/releases
|
||||
ELEMENT_VERSION="v1.12.11" # Change to latest version
|
||||
|
||||
# Download and extract
|
||||
cd /tmp
|
||||
wget https://github.com/element-hq/element-web/releases/download/${ELEMENT_VERSION}/element-${ELEMENT_VERSION}.tar.gz
|
||||
tar -xzf element-${ELEMENT_VERSION}.tar.gz
|
||||
|
||||
# Backup current config
|
||||
cp /opt/element/web/config.json /tmp/element-config-backup.json
|
||||
|
||||
# Back up configs
|
||||
cp /opt/element/web/config.json /tmp/element-config-web.json
|
||||
cp /opt/element/web-thevish/config.json /tmp/element-config-thevish.json
|
||||
|
||||
# Replace files (both installs share the same release)
|
||||
sudo rm -rf /opt/element/web/* /opt/element/web-thevish/*
|
||||
sudo cp -r element-${ELEMENT_VERSION}/* /opt/element/web/
|
||||
sudo cp -r element-${ELEMENT_VERSION}/* /opt/element/web-thevish/
|
||||
|
||||
# Restore configs
|
||||
sudo cp /tmp/element-config-web.json /opt/element/web/config.json
|
||||
sudo cp /tmp/element-config-thevish.json /opt/element/web-thevish/config.json
|
||||
|
||||
# Verify (nginx serves static files, no restart needed)
|
||||
cat /opt/element/web/version
|
||||
cat /opt/element/web-thevish/version
|
||||
|
||||
# Cleanup
|
||||
rm -rf /tmp/element-${ELEMENT_VERSION}* /tmp/element-config-*.json
|
||||
```
|
||||
|
||||
### Update TURN Server (coturn)
|
||||
|
||||
```bash
|
||||
# Update via apt
|
||||
sudo apt update
|
||||
sudo apt upgrade coturn
|
||||
|
||||
# Restart
|
||||
sudo systemctl restart coturn
|
||||
|
||||
# Verify
|
||||
sudo systemctl status coturn
|
||||
```
|
||||
|
||||
### Update All Services (Quick Script)
|
||||
|
||||
```bash
|
||||
#!/bin/bash
|
||||
# Save as /opt/scripts/update-all.sh
|
||||
|
||||
echo "=== Updating Mastodon ==="
|
||||
cd /opt/mastodon
|
||||
docker compose pull
|
||||
docker compose down
|
||||
docker compose run --rm web bundle exec rails db:migrate
|
||||
docker compose up -d
|
||||
|
||||
echo "=== Updating Mattermost ==="
|
||||
cd /opt/mattermost
|
||||
docker compose pull
|
||||
docker compose down
|
||||
docker compose up -d
|
||||
|
||||
echo "=== Updating Synapse ==="
|
||||
cd /opt/synapse
|
||||
source venv/bin/activate
|
||||
pip install --upgrade matrix-synapse
|
||||
pkill -f 'synapse.app.homeserver'
|
||||
sleep 2
|
||||
sudo -u synapse /opt/synapse/venv/bin/python -m synapse.app.homeserver \
|
||||
--config-path=/opt/synapse-mx/homeserver.yaml --daemonize
|
||||
sudo -u synapse /opt/synapse/venv/bin/python -m synapse.app.homeserver \
|
||||
--config-path=/opt/synapse/homeserver.yaml --daemonize
|
||||
|
||||
echo "=== Updating System Packages ==="
|
||||
sudo apt update && sudo apt upgrade -y
|
||||
|
||||
echo "=== Done! ==="
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Federation Status
|
||||
|
||||
| Service | Protocol | Federation |
|
||||
|---------|----------|------------|
|
||||
| Matrix (mx.vish.gg) | Matrix | ✅ Enabled |
|
||||
| Matrix (vish) | Matrix | ❌ Disabled (invalid server_name) |
|
||||
| Mastodon | ActivityPub | ✅ Enabled |
|
||||
| Mattermost | Shared Channels | ❌ Enterprise only |
|
||||
|
||||
## License
|
||||
|
||||
MIT
|
||||
28
hosts/vms/matrix-ubuntu-vm/diun.yaml
Normal file
28
hosts/vms/matrix-ubuntu-vm/diun.yaml
Normal file
@@ -0,0 +1,28 @@
|
||||
# Diun — Docker Image Update Notifier
|
||||
#
|
||||
# Watches all running containers on this host and sends ntfy
|
||||
# notifications when upstream images update their digest.
|
||||
# Schedule: Mondays 09:00 (weekly cadence).
|
||||
#
|
||||
# ntfy topic: https://ntfy.vish.gg/diun
|
||||
|
||||
services:
|
||||
diun:
|
||||
image: crazymax/diun:latest
|
||||
container_name: diun
|
||||
volumes:
|
||||
- /var/run/docker.sock:/var/run/docker.sock
|
||||
- diun-data:/data
|
||||
environment:
|
||||
LOG_LEVEL: info
|
||||
DIUN_WATCH_WORKERS: "20"
|
||||
DIUN_WATCH_SCHEDULE: "0 9 * * 1"
|
||||
DIUN_WATCH_JITTER: 30s
|
||||
DIUN_PROVIDERS_DOCKER: "true"
|
||||
DIUN_PROVIDERS_DOCKER_WATCHBYDEFAULT: "true"
|
||||
DIUN_NOTIF_NTFY_ENDPOINT: "https://ntfy.vish.gg"
|
||||
DIUN_NOTIF_NTFY_TOPIC: "diun"
|
||||
restart: unless-stopped
|
||||
|
||||
volumes:
|
||||
diun-data:
|
||||
171
hosts/vms/matrix-ubuntu-vm/docs/FEDERATION.md
Normal file
171
hosts/vms/matrix-ubuntu-vm/docs/FEDERATION.md
Normal 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
|
||||
321
hosts/vms/matrix-ubuntu-vm/docs/MATRIX.md
Normal file
321
hosts/vms/matrix-ubuntu-vm/docs/MATRIX.md
Normal file
@@ -0,0 +1,321 @@
|
||||
# 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
|
||||
|
||||
### Managing the Service
|
||||
|
||||
```bash
|
||||
sudo systemctl start synapse-mx
|
||||
sudo systemctl stop synapse-mx
|
||||
sudo systemctl restart synapse-mx
|
||||
sudo systemctl status synapse-mx
|
||||
```
|
||||
|
||||
Service file: `/etc/systemd/system/synapse-mx.service`
|
||||
|
||||
## 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`
|
||||
|
||||
### Managing the Service
|
||||
|
||||
```bash
|
||||
sudo systemctl start synapse
|
||||
sudo systemctl stop synapse
|
||||
sudo systemctl restart synapse
|
||||
sudo systemctl status synapse
|
||||
```
|
||||
|
||||
Service file: `/etc/systemd/system/synapse.service`
|
||||
|
||||
## 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
|
||||
sudo systemctl status synapse synapse-mx
|
||||
curl -s http://localhost:8008/_synapse/admin/v1/server_version # legacy
|
||||
curl -s http://localhost:8018/_synapse/admin/v1/server_version # mx
|
||||
```
|
||||
|
||||
### View logs
|
||||
|
||||
```bash
|
||||
sudo journalctl -u synapse -f # mx.vish.gg
|
||||
sudo journalctl -u synapse-mx -f # legacy vish
|
||||
```
|
||||
|
||||
### 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
|
||||
```
|
||||
|
||||
### DB ownership fix (apply if migrations fail on upgrade)
|
||||
|
||||
If Synapse fails to start after upgrade with `InsufficientPrivilege: must be owner of table`,
|
||||
the DB tables need their ownership corrected. Run for the affected database:
|
||||
|
||||
```bash
|
||||
# For synapse (legacy) DB:
|
||||
sudo -u postgres psql synapse -t -c "
|
||||
SELECT 'ALTER TABLE public.' || tablename || ' OWNER TO synapse;'
|
||||
FROM pg_tables WHERE schemaname='public' AND tableowner <> 'synapse';
|
||||
" | sudo -u postgres psql synapse
|
||||
|
||||
sudo -u postgres psql synapse -t -c "
|
||||
SELECT 'ALTER SEQUENCE ' || sequence_name || ' OWNER TO synapse;'
|
||||
FROM information_schema.sequences WHERE sequence_schema='public';
|
||||
" | sudo -u postgres psql synapse
|
||||
|
||||
# For synapse_mx DB, replace 'synapse' with 'synapse_mx' throughout
|
||||
```
|
||||
259
hosts/vms/matrix-ubuntu-vm/docs/SETUP.md
Normal file
259
hosts/vms/matrix-ubuntu-vm/docs/SETUP.md
Normal 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
|
||||
```
|
||||
178
hosts/vms/matrix-ubuntu-vm/docs/SMTP.md
Normal file
178
hosts/vms/matrix-ubuntu-vm/docs/SMTP.md
Normal 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.
|
||||
15
hosts/vms/matrix-ubuntu-vm/dozzle-agent.yaml
Normal file
15
hosts/vms/matrix-ubuntu-vm/dozzle-agent.yaml
Normal file
@@ -0,0 +1,15 @@
|
||||
services:
|
||||
dozzle-agent:
|
||||
image: amir20/dozzle:latest
|
||||
container_name: dozzle-agent
|
||||
command: agent
|
||||
volumes:
|
||||
- /var/run/docker.sock:/var/run/docker.sock
|
||||
ports:
|
||||
- "7007:7007"
|
||||
restart: unless-stopped
|
||||
healthcheck:
|
||||
test: ["CMD", "/dozzle", "healthcheck"]
|
||||
interval: 30s
|
||||
timeout: 5s
|
||||
retries: 3
|
||||
45
hosts/vms/matrix-ubuntu-vm/mastodon/.env.production.template
Normal file
45
hosts/vms/matrix-ubuntu-vm/mastodon/.env.production.template
Normal file
@@ -0,0 +1,45 @@
|
||||
# Mastodon Environment Configuration
|
||||
# Copy to .env.production and fill in values
|
||||
|
||||
LOCAL_DOMAIN=mastodon.vish.gg
|
||||
SINGLE_USER_MODE=false
|
||||
|
||||
# Generate with: openssl rand -hex 64
|
||||
SECRET_KEY_BASE=<GENERATE_SECRET>
|
||||
OTP_SECRET=<GENERATE_SECRET>
|
||||
|
||||
# Database (using host PostgreSQL)
|
||||
DB_HOST=172.17.0.1
|
||||
DB_PORT=5432
|
||||
DB_NAME=mastodon_production
|
||||
DB_USER=mastodon
|
||||
DB_PASS=REDACTED_DB_PASSWORD
|
||||
|
||||
# Redis
|
||||
REDIS_HOST=redis
|
||||
REDIS_PORT=6379
|
||||
|
||||
# Locale
|
||||
DEFAULT_LOCALE=en
|
||||
|
||||
# SMTP Configuration (Gmail)
|
||||
# See docs/SMTP.md for setup instructions
|
||||
SMTP_SERVER=smtp.gmail.com
|
||||
SMTP_PORT=587
|
||||
SMTP_LOGIN=your-email@example.com
|
||||
SMTP_PASSWORD=REDACTED_SMTP_PASSWORD
|
||||
SMTP_AUTH_METHOD=plain
|
||||
SMTP_OPENSSL_VERIFY_MODE=none
|
||||
SMTP_ENABLE_STARTTLS=auto
|
||||
SMTP_FROM_ADDRESS="Mastodon <notifications@mastodon.vish.gg>"
|
||||
|
||||
# File storage
|
||||
PAPERCLIP_SECRET=<GENERATE_SECRET>
|
||||
|
||||
# Search (optional)
|
||||
ES_ENABLED=false
|
||||
|
||||
# Encryption keys - Generate with: docker compose run --rm web bin/rails db:encryption:init
|
||||
ACTIVE_RECORD_ENCRYPTION_DETERMINISTIC_KEY=<GENERATE>
|
||||
ACTIVE_RECORD_ENCRYPTION_KEY_DERIVATION_SALT=<GENERATE>
|
||||
ACTIVE_RECORD_ENCRYPTION_PRIMARY_KEY=<GENERATE>
|
||||
53
hosts/vms/matrix-ubuntu-vm/mastodon/docker-compose.yml
Normal file
53
hosts/vms/matrix-ubuntu-vm/mastodon/docker-compose.yml
Normal file
@@ -0,0 +1,53 @@
|
||||
services:
|
||||
redis:
|
||||
restart: unless-stopped
|
||||
image: redis:7-alpine
|
||||
networks:
|
||||
- internal_network
|
||||
volumes:
|
||||
- ./redis:/data
|
||||
|
||||
web:
|
||||
image: ghcr.io/mastodon/mastodon:v4.5.7
|
||||
restart: unless-stopped
|
||||
env_file: .env.production
|
||||
command: bundle exec puma -C config/puma.rb
|
||||
networks:
|
||||
- external_network
|
||||
- internal_network
|
||||
ports:
|
||||
- '3000:3000'
|
||||
depends_on:
|
||||
- redis
|
||||
volumes:
|
||||
- ./public/system:/mastodon/public/system
|
||||
|
||||
streaming:
|
||||
image: ghcr.io/mastodon/mastodon-streaming:v4.5.7
|
||||
restart: unless-stopped
|
||||
env_file: .env.production
|
||||
networks:
|
||||
- external_network
|
||||
- internal_network
|
||||
ports:
|
||||
- '4000:4000'
|
||||
depends_on:
|
||||
- redis
|
||||
|
||||
sidekiq:
|
||||
image: ghcr.io/mastodon/mastodon:v4.5.7
|
||||
restart: unless-stopped
|
||||
env_file: .env.production
|
||||
command: bundle exec sidekiq
|
||||
networks:
|
||||
- external_network
|
||||
- internal_network
|
||||
depends_on:
|
||||
- redis
|
||||
volumes:
|
||||
- ./public/system:/mastodon/public/system
|
||||
|
||||
networks:
|
||||
external_network:
|
||||
internal_network:
|
||||
internal: true
|
||||
@@ -0,0 +1,36 @@
|
||||
{
|
||||
"default_server_config": {
|
||||
"m.homeserver": {
|
||||
"base_url": "https://mx.vish.gg",
|
||||
"server_name": "mx.vish.gg"
|
||||
},
|
||||
"m.identity_server": {
|
||||
"base_url": "https://vector.im"
|
||||
}
|
||||
},
|
||||
"disable_custom_urls": false,
|
||||
"disable_guests": true,
|
||||
"disable_login_language_selector": false,
|
||||
"disable_3pid_login": false,
|
||||
"brand": "Element",
|
||||
"integrations_ui_url": "https://scalar.vector.im/",
|
||||
"integrations_rest_url": "https://scalar.vector.im/api",
|
||||
"integrations_widgets_urls": [
|
||||
"https://scalar.vector.im/_matrix/integrations/v1",
|
||||
"https://scalar.vector.im/api",
|
||||
"https://scalar-staging.vector.im/_matrix/integrations/v1",
|
||||
"https://scalar-staging.vector.im/api",
|
||||
"https://scalar-staging.riot.im/scalar/api"
|
||||
],
|
||||
"default_country_code": "US",
|
||||
"show_labs_settings": true,
|
||||
"features": {},
|
||||
"default_federate": true,
|
||||
"default_theme": "dark",
|
||||
"room_directory": {
|
||||
"servers": ["mx.vish.gg", "matrix.org"]
|
||||
},
|
||||
"enable_presence_by_hs_url": {
|
||||
"https://mx.vish.gg": true
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,69 @@
|
||||
# Matrix Synapse Homeserver Configuration Template
|
||||
# Copy to /opt/synapse-mx/homeserver.yaml and customize
|
||||
#
|
||||
# This is the PRIMARY federated server (mx.vish.gg)
|
||||
# For legacy server config, see homeserver-legacy.yaml.template
|
||||
|
||||
server_name: "mx.vish.gg"
|
||||
pid_file: /opt/synapse-mx/homeserver.pid
|
||||
public_baseurl: https://mx.vish.gg/
|
||||
|
||||
listeners:
|
||||
- port: 8018
|
||||
tls: false
|
||||
type: http
|
||||
x_forwarded: true
|
||||
resources:
|
||||
- names: [client, federation]
|
||||
compress: false
|
||||
|
||||
database:
|
||||
name: psycopg2
|
||||
args:
|
||||
user: synapse_mx
|
||||
password: "REDACTED_PASSWORD"
|
||||
database: synapse_mx
|
||||
host: localhost
|
||||
cp_min: 5
|
||||
cp_max: 10
|
||||
|
||||
log_config: "/opt/synapse-mx/mx.vish.gg.log.config"
|
||||
media_store_path: /opt/synapse-mx/media_store
|
||||
signing_key_path: "/opt/synapse-mx/mx.vish.gg.signing.key"
|
||||
|
||||
trusted_key_servers:
|
||||
- server_name: "matrix.org"
|
||||
|
||||
# Generate secrets with: python3 -c "import secrets; print(secrets.token_urlsafe(32))"
|
||||
registration_shared_secret: "<GENERATE_SECRET>"
|
||||
macaroon_secret_key: "<GENERATE_SECRET>"
|
||||
form_secret: "<GENERATE_SECRET>"
|
||||
|
||||
enable_registration: true
|
||||
enable_registration_without_verification: 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'
|
||||
|
||||
report_stats: false
|
||||
suppress_key_server_warning: true
|
||||
|
||||
# TURN server for voice/video calls
|
||||
turn_uris:
|
||||
- "turn:mx.vish.gg:3479?transport=udp"
|
||||
- "turn:mx.vish.gg:3479?transport=tcp"
|
||||
turn_shared_secret: "<TURN_SHARED_SECRET>"
|
||||
turn_user_lifetime: 86400000
|
||||
turn_allow_guests: true
|
||||
|
||||
enable_3pid_changes: true
|
||||
@@ -0,0 +1,33 @@
|
||||
# TURN Server Configuration (coturn)
|
||||
# Copy to /etc/turnserver.conf
|
||||
|
||||
# Ports
|
||||
listening-port=3479
|
||||
tls-listening-port=5350
|
||||
listening-ip=0.0.0.0
|
||||
|
||||
# External IP for NAT traversal
|
||||
# Format: external-ip=<public-ip>/<internal-ip>
|
||||
external-ip=YOUR_WAN_IP/192.168.0.154
|
||||
|
||||
# Authentication
|
||||
fingerprint
|
||||
use-auth-secret
|
||||
static-auth-secret=<TURN_SHARED_SECRET>
|
||||
realm=matrix.thevish.io
|
||||
|
||||
# Quotas
|
||||
total-quota=100
|
||||
bps-capacity=0
|
||||
stale-nonce=600
|
||||
|
||||
# Security
|
||||
no-multicast-peers
|
||||
|
||||
# Media relay ports (must be forwarded through firewall)
|
||||
min-port=49201
|
||||
max-port=49250
|
||||
|
||||
# Logging
|
||||
log-file=/var/log/turnserver.log
|
||||
verbose
|
||||
27
hosts/vms/matrix-ubuntu-vm/mattermost/docker-compose.yml
Normal file
27
hosts/vms/matrix-ubuntu-vm/mattermost/docker-compose.yml
Normal file
@@ -0,0 +1,27 @@
|
||||
services:
|
||||
mattermost:
|
||||
container_name: mattermost
|
||||
image: mattermost/mattermost-team-edition:11.3
|
||||
restart: unless-stopped
|
||||
ports:
|
||||
- "8065:8065"
|
||||
volumes:
|
||||
- ./config:/mattermost/config
|
||||
- ./data:/mattermost/data
|
||||
- ./logs:/mattermost/logs
|
||||
- ./plugins:/mattermost/plugins
|
||||
- ./client/plugins:/mattermost/client/plugins
|
||||
- ./bleve-indexes:/mattermost/bleve-indexes
|
||||
environment:
|
||||
- TZ=UTC
|
||||
- MM_SQLSETTINGS_DRIVERNAME=postgres
|
||||
- MM_SQLSETTINGS_DATASOURCE=postgres://mmuser:${MM_DB_PASSWORD}@172.17.0.1:5432/mattermost?sslmode=disable
|
||||
- MM_SERVICESETTINGS_SITEURL=https://mm.crista.love
|
||||
# Authentik OpenID Connect SSO - keeps local login working
|
||||
- MM_OPENIDSETTINGS_ENABLE=true
|
||||
- MM_OPENIDSETTINGS_BUTTONTEXT=Sign in with Authentik
|
||||
- MM_OPENIDSETTINGS_BUTTONCOLOR=#fd4b2d
|
||||
- MM_OPENIDSETTINGS_DISCOVERYSETTINGS_DISCOVERURL=https://sso.vish.gg/application/o/mattermost/.well-known/openid-configuration
|
||||
- MM_OPENIDSETTINGS_ID=OGxIdZLKqYKgf9Sf9zAFAyhKzBdDvonL7HHSBu1w
|
||||
- MM_OPENIDSETTINGS_SECRET=Dzi2iOFXMyzXrvbT2ZDSdqYYg6c6bX39mFihX4h20WKEV0lHBnKfF5bb6KWDH2P9HhlTpl1KPB5LbE9GYuJqGoTXO6aXWiNJJhqrCgJX2eaFRtne2J72mz4TfTxxKBCM
|
||||
- MM_OPENIDSETTINGS_SCOPE=openid profile email
|
||||
118
hosts/vms/matrix-ubuntu-vm/nginx/mastodon.conf
Normal file
118
hosts/vms/matrix-ubuntu-vm/nginx/mastodon.conf
Normal file
@@ -0,0 +1,118 @@
|
||||
map $http_upgrade $connection_upgrade {
|
||||
default upgrade;
|
||||
'' close;
|
||||
}
|
||||
|
||||
upstream mastodon_backend {
|
||||
server 127.0.0.1:3000 fail_timeout=0;
|
||||
}
|
||||
|
||||
upstream mastodon_streaming {
|
||||
server 127.0.0.1:4000 fail_timeout=0;
|
||||
}
|
||||
|
||||
server {
|
||||
listen 8082;
|
||||
listen [::]:8082;
|
||||
server_name mastodon.vish.gg;
|
||||
|
||||
keepalive_timeout 70;
|
||||
sendfile on;
|
||||
client_max_body_size 80m;
|
||||
|
||||
root /opt/mastodon/public;
|
||||
|
||||
gzip on;
|
||||
gzip_disable "msie6";
|
||||
gzip_vary on;
|
||||
gzip_proxied any;
|
||||
gzip_comp_level 6;
|
||||
gzip_buffers 16 8k;
|
||||
gzip_http_version 1.1;
|
||||
gzip_types text/plain text/css application/json application/javascript text/xml application/xml application/xml+rss text/javascript image/svg+xml image/x-icon;
|
||||
|
||||
location / {
|
||||
try_files $uri @proxy;
|
||||
}
|
||||
|
||||
location /sw.js {
|
||||
add_header Cache-Control "public, max-age=604800, must-revalidate";
|
||||
try_files $uri =404;
|
||||
}
|
||||
|
||||
location ~ ^/assets/ {
|
||||
add_header Cache-Control "public, max-age=2419200, must-revalidate";
|
||||
try_files $uri =404;
|
||||
}
|
||||
|
||||
location ~ ^/avatars/ {
|
||||
add_header Cache-Control "public, max-age=2419200, must-revalidate";
|
||||
try_files $uri =404;
|
||||
}
|
||||
|
||||
location ~ ^/emoji/ {
|
||||
add_header Cache-Control "public, max-age=2419200, must-revalidate";
|
||||
try_files $uri =404;
|
||||
}
|
||||
|
||||
location ~ ^/headers/ {
|
||||
add_header Cache-Control "public, max-age=2419200, must-revalidate";
|
||||
try_files $uri =404;
|
||||
}
|
||||
|
||||
location ~ ^/packs/ {
|
||||
add_header Cache-Control "public, max-age=2419200, must-revalidate";
|
||||
try_files $uri =404;
|
||||
}
|
||||
|
||||
location ~ ^/shortcuts/ {
|
||||
add_header Cache-Control "public, max-age=2419200, must-revalidate";
|
||||
try_files $uri =404;
|
||||
}
|
||||
|
||||
location ~ ^/sounds/ {
|
||||
add_header Cache-Control "public, max-age=2419200, must-revalidate";
|
||||
try_files $uri =404;
|
||||
}
|
||||
|
||||
location ~ ^/system/ {
|
||||
add_header Cache-Control "public, max-age=2419200, immutable";
|
||||
try_files $uri =404;
|
||||
}
|
||||
|
||||
location ^~ /api/v1/streaming {
|
||||
proxy_set_header Host $host;
|
||||
proxy_set_header X-Real-IP $remote_addr;
|
||||
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
|
||||
proxy_set_header X-Forwarded-Proto $scheme;
|
||||
proxy_set_header Upgrade $http_upgrade;
|
||||
proxy_set_header Connection $connection_upgrade;
|
||||
|
||||
proxy_pass http://mastodon_streaming;
|
||||
proxy_buffering off;
|
||||
proxy_redirect off;
|
||||
proxy_http_version 1.1;
|
||||
|
||||
tcp_nodelay on;
|
||||
}
|
||||
|
||||
location @proxy {
|
||||
proxy_set_header Host $host;
|
||||
proxy_set_header X-Real-IP $remote_addr;
|
||||
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
|
||||
proxy_set_header X-Forwarded-Proto $scheme;
|
||||
proxy_set_header Proxy "";
|
||||
proxy_pass_header Server;
|
||||
|
||||
proxy_pass http://mastodon_backend;
|
||||
proxy_buffering on;
|
||||
proxy_redirect off;
|
||||
proxy_http_version 1.1;
|
||||
|
||||
proxy_cache_bypass $http_upgrade;
|
||||
|
||||
tcp_nodelay on;
|
||||
}
|
||||
|
||||
error_page 404 500 501 502 503 504 /500.html;
|
||||
}
|
||||
54
hosts/vms/matrix-ubuntu-vm/nginx/matrix-legacy.conf
Normal file
54
hosts/vms/matrix-ubuntu-vm/nginx/matrix-legacy.conf
Normal file
@@ -0,0 +1,54 @@
|
||||
# matrix.thevish.io - Legacy Matrix server (no federation, historical data)
|
||||
server {
|
||||
listen 8081;
|
||||
listen [::]:8081;
|
||||
server_name matrix.thevish.io;
|
||||
|
||||
# Element Web client
|
||||
root /opt/element/web-thevish;
|
||||
index index.html;
|
||||
|
||||
# Health check
|
||||
location /health {
|
||||
proxy_pass http://127.0.0.1:8008;
|
||||
proxy_set_header X-Forwarded-For $remote_addr;
|
||||
proxy_set_header X-Forwarded-Proto https;
|
||||
proxy_set_header Host $host;
|
||||
}
|
||||
|
||||
# Client-Server API
|
||||
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 https;
|
||||
proxy_set_header Host $host;
|
||||
client_max_body_size 100M;
|
||||
proxy_http_version 1.1;
|
||||
}
|
||||
|
||||
# Federation API (won't work due to server_name being "vish")
|
||||
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 https;
|
||||
proxy_set_header Host $host;
|
||||
client_max_body_size 100M;
|
||||
}
|
||||
|
||||
# Well-known (for reference, federation won't work)
|
||||
location /.well-known/matrix/server {
|
||||
default_type application/json;
|
||||
return 200 '{"m.server": "matrix.thevish.io:443"}';
|
||||
}
|
||||
|
||||
location /.well-known/matrix/client {
|
||||
default_type application/json;
|
||||
add_header Access-Control-Allow-Origin *;
|
||||
return 200 '{"m.homeserver": {"base_url": "https://matrix.thevish.io"}}';
|
||||
}
|
||||
|
||||
# Element static files
|
||||
location / {
|
||||
try_files $uri $uri/ /index.html;
|
||||
}
|
||||
}
|
||||
54
hosts/vms/matrix-ubuntu-vm/nginx/matrix.conf
Normal file
54
hosts/vms/matrix-ubuntu-vm/nginx/matrix.conf
Normal file
@@ -0,0 +1,54 @@
|
||||
# mx.vish.gg - Primary Matrix server (federation enabled)
|
||||
server {
|
||||
listen 8082;
|
||||
listen [::]:8082;
|
||||
server_name mx.vish.gg;
|
||||
|
||||
# Element Web client
|
||||
root /opt/element/web;
|
||||
index index.html;
|
||||
|
||||
# Health check
|
||||
location /health {
|
||||
proxy_pass http://127.0.0.1:8018;
|
||||
proxy_set_header X-Forwarded-For $remote_addr;
|
||||
proxy_set_header X-Forwarded-Proto https;
|
||||
proxy_set_header Host $host;
|
||||
}
|
||||
|
||||
# Client-Server API
|
||||
location ~ ^(/_matrix|/_synapse/client) {
|
||||
proxy_pass http://127.0.0.1:8018;
|
||||
proxy_set_header X-Forwarded-For $remote_addr;
|
||||
proxy_set_header X-Forwarded-Proto https;
|
||||
proxy_set_header Host $host;
|
||||
client_max_body_size 100M;
|
||||
proxy_http_version 1.1;
|
||||
}
|
||||
|
||||
# Federation API
|
||||
location /_matrix/federation {
|
||||
proxy_pass http://127.0.0.1:8018;
|
||||
proxy_set_header X-Forwarded-For $remote_addr;
|
||||
proxy_set_header X-Forwarded-Proto https;
|
||||
proxy_set_header Host $host;
|
||||
client_max_body_size 100M;
|
||||
}
|
||||
|
||||
# Well-known for federation
|
||||
location /.well-known/matrix/server {
|
||||
default_type application/json;
|
||||
return 200 '{"m.server": "mx.vish.gg:443"}';
|
||||
}
|
||||
|
||||
location /.well-known/matrix/client {
|
||||
default_type application/json;
|
||||
add_header Access-Control-Allow-Origin *;
|
||||
return 200 '{"m.homeserver": {"base_url": "https://mx.vish.gg"}}';
|
||||
}
|
||||
|
||||
# Element static files
|
||||
location / {
|
||||
try_files $uri $uri/ /index.html;
|
||||
}
|
||||
}
|
||||
41
hosts/vms/matrix-ubuntu-vm/nginx/mattermost.conf
Normal file
41
hosts/vms/matrix-ubuntu-vm/nginx/mattermost.conf
Normal file
@@ -0,0 +1,41 @@
|
||||
upstream mattermost {
|
||||
server 127.0.0.1:8065;
|
||||
keepalive 32;
|
||||
}
|
||||
|
||||
server {
|
||||
listen 8081;
|
||||
listen [::]:8081;
|
||||
server_name mm.crista.love;
|
||||
|
||||
location ~ /api/v[0-9]+/(users/)?websocket$ {
|
||||
proxy_set_header Upgrade $http_upgrade;
|
||||
proxy_set_header Connection "upgrade";
|
||||
client_max_body_size 50M;
|
||||
proxy_set_header Host $http_host;
|
||||
proxy_set_header X-Real-IP $remote_addr;
|
||||
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
|
||||
proxy_set_header X-Forwarded-Proto $scheme;
|
||||
proxy_set_header X-Frame-Options SAMEORIGIN;
|
||||
proxy_buffers 256 16k;
|
||||
proxy_buffer_size 16k;
|
||||
proxy_read_timeout 600s;
|
||||
proxy_http_version 1.1;
|
||||
proxy_pass http://mattermost;
|
||||
}
|
||||
|
||||
location / {
|
||||
client_max_body_size 100M;
|
||||
proxy_set_header Connection "";
|
||||
proxy_set_header Host $http_host;
|
||||
proxy_set_header X-Real-IP $remote_addr;
|
||||
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
|
||||
proxy_set_header X-Forwarded-Proto $scheme;
|
||||
proxy_set_header X-Frame-Options SAMEORIGIN;
|
||||
proxy_buffers 256 16k;
|
||||
proxy_buffer_size 16k;
|
||||
proxy_read_timeout 600s;
|
||||
proxy_http_version 1.1;
|
||||
proxy_pass http://mattermost;
|
||||
}
|
||||
}
|
||||
30
hosts/vms/matrix-ubuntu-vm/scripts/backup.sh
Executable file
30
hosts/vms/matrix-ubuntu-vm/scripts/backup.sh
Executable file
@@ -0,0 +1,30 @@
|
||||
#!/bin/bash
|
||||
set -e
|
||||
|
||||
BACKUP_DIR="/backup/$(date +%Y%m%d_%H%M%S)"
|
||||
mkdir -p "$BACKUP_DIR"
|
||||
|
||||
echo "=== Homelab Backup ==="
|
||||
echo "Backup directory: $BACKUP_DIR"
|
||||
|
||||
# Backup PostgreSQL databases
|
||||
echo "[1/4] Backing up PostgreSQL databases..."
|
||||
sudo -u postgres pg_dump mattermost > "$BACKUP_DIR/mattermost.sql"
|
||||
sudo -u postgres pg_dump synapse > "$BACKUP_DIR/synapse.sql"
|
||||
sudo -u postgres pg_dump mastodon_production > "$BACKUP_DIR/mastodon.sql"
|
||||
|
||||
# Backup Mastodon media
|
||||
echo "[2/4] Backing up Mastodon media..."
|
||||
tar -czf "$BACKUP_DIR/mastodon_media.tar.gz" -C /opt/mastodon public/system 2>/dev/null || true
|
||||
|
||||
# Backup Mattermost data
|
||||
echo "[3/4] Backing up Mattermost data..."
|
||||
tar -czf "$BACKUP_DIR/mattermost_data.tar.gz" -C /opt/mattermost data config 2>/dev/null || true
|
||||
|
||||
# Backup Matrix/Synapse
|
||||
echo "[4/4] Backing up Matrix data..."
|
||||
tar -czf "$BACKUP_DIR/synapse_data.tar.gz" -C /opt synapse 2>/dev/null || true
|
||||
|
||||
echo ""
|
||||
echo "Backup complete: $BACKUP_DIR"
|
||||
ls -lh "$BACKUP_DIR"
|
||||
69
hosts/vms/matrix-ubuntu-vm/scripts/setup.sh
Executable file
69
hosts/vms/matrix-ubuntu-vm/scripts/setup.sh
Executable file
@@ -0,0 +1,69 @@
|
||||
#!/bin/bash
|
||||
set -e
|
||||
|
||||
echo "=== Ubuntu VM Homelab Setup ==="
|
||||
echo "This script sets up Mastodon, Mattermost, and Matrix/Element"
|
||||
|
||||
# Check if running as root
|
||||
if [ "$EUID" -ne 0 ]; then
|
||||
echo "Please run as root (sudo ./setup.sh)"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
# Update system
|
||||
echo "[1/8] Updating system..."
|
||||
apt-get update && apt-get upgrade -y
|
||||
|
||||
# Install dependencies
|
||||
echo "[2/8] Installing dependencies..."
|
||||
apt-get install -y \
|
||||
docker.io docker-compose-v2 \
|
||||
nginx \
|
||||
postgresql postgresql-contrib \
|
||||
curl wget git
|
||||
|
||||
# Start services
|
||||
echo "[3/8] Starting services..."
|
||||
systemctl enable --now docker
|
||||
systemctl enable --now postgresql
|
||||
systemctl enable --now nginx
|
||||
|
||||
# Setup PostgreSQL
|
||||
echo "[4/8] Setting up PostgreSQL..."
|
||||
sudo -u postgres psql -c "CREATE USER mmuser WITH PASSWORD 'REDACTED_PASSWORD';" 2>/dev/null || true
|
||||
sudo -u postgres psql -c "CREATE DATABASE mattermost OWNER mmuser;" 2>/dev/null || true
|
||||
sudo -u postgres psql -c "CREATE USER synapse WITH PASSWORD 'REDACTED_PASSWORD';" 2>/dev/null || true
|
||||
sudo -u postgres psql -c "CREATE DATABASE synapse OWNER synapse ENCODING 'UTF8' LC_COLLATE='C' LC_CTYPE='C' template=template0;" 2>/dev/null || true
|
||||
sudo -u postgres psql -c "CREATE USER mastodon WITH PASSWORD 'REDACTED_PASSWORD' CREATEDB;" 2>/dev/null || true
|
||||
sudo -u postgres psql -c "CREATE DATABASE mastodon_production OWNER mastodon;" 2>/dev/null || true
|
||||
|
||||
# Configure PostgreSQL for Docker access
|
||||
echo "[5/8] Configuring PostgreSQL..."
|
||||
echo "host all all 172.17.0.0/16 md5" >> /etc/postgresql/*/main/pg_hba.conf
|
||||
echo "host all all 0.0.0.0/0 md5" >> /etc/postgresql/*/main/pg_hba.conf
|
||||
sed -i "s/#listen_addresses = 'localhost'/listen_addresses = '*'/" /etc/postgresql/*/main/postgresql.conf
|
||||
systemctl restart postgresql
|
||||
|
||||
# Setup directories
|
||||
echo "[6/8] Creating directories..."
|
||||
mkdir -p /opt/mastodon /opt/mattermost /opt/synapse /opt/element/web
|
||||
|
||||
# Copy nginx configs
|
||||
echo "[7/8] Setting up Nginx..."
|
||||
cp nginx/*.conf /etc/nginx/sites-available/
|
||||
ln -sf /etc/nginx/sites-available/mastodon.conf /etc/nginx/sites-enabled/
|
||||
ln -sf /etc/nginx/sites-available/mattermost.conf /etc/nginx/sites-enabled/
|
||||
ln -sf /etc/nginx/sites-available/matrix.conf /etc/nginx/sites-enabled/
|
||||
nginx -t && systemctl reload nginx
|
||||
|
||||
echo "[8/8] Setup complete!"
|
||||
echo ""
|
||||
echo "Next steps:"
|
||||
echo "1. Copy docker-compose files to /opt directories"
|
||||
echo "2. Configure environment files with actual secrets"
|
||||
echo "3. Run migrations and start services"
|
||||
echo ""
|
||||
echo "Ports:"
|
||||
echo " - Mastodon: 8082"
|
||||
echo " - Mattermost: 8081"
|
||||
echo " - Matrix/Element: 8080"
|
||||
96
hosts/vms/matrix-ubuntu-vm/scripts/update.sh
Executable file
96
hosts/vms/matrix-ubuntu-vm/scripts/update.sh
Executable file
@@ -0,0 +1,96 @@
|
||||
#!/bin/bash
|
||||
|
||||
# Homelab Update Script
|
||||
# Updates Mastodon, Mattermost, Matrix Synapse, and system packages
|
||||
|
||||
echo "=== Homelab Update Script ==="
|
||||
echo "Started at: $(date)"
|
||||
echo ""
|
||||
|
||||
# Update Mastodon
|
||||
echo "[1/5] Updating Mastodon..."
|
||||
cd /opt/mastodon
|
||||
docker compose pull
|
||||
docker compose down
|
||||
docker compose run --rm web bundle exec rails db:migrate
|
||||
docker compose up -d
|
||||
echo "✅ Mastodon updated!"
|
||||
echo ""
|
||||
|
||||
# Update Mattermost
|
||||
echo "[2/5] Updating Mattermost..."
|
||||
cd /opt/mattermost
|
||||
docker compose pull
|
||||
docker compose down
|
||||
docker compose up -d
|
||||
echo "✅ Mattermost updated!"
|
||||
echo ""
|
||||
|
||||
# Update Matrix Synapse
|
||||
echo "[3/5] Updating Matrix Synapse..."
|
||||
cd /opt/synapse
|
||||
source venv/bin/activate
|
||||
|
||||
# Get current version
|
||||
CURRENT_VERSION=$(python -m synapse.app.homeserver --version 2>&1 | head -1)
|
||||
echo "Current version: $CURRENT_VERSION"
|
||||
|
||||
# Upgrade
|
||||
pip install --upgrade matrix-synapse
|
||||
|
||||
# Get new version
|
||||
NEW_VERSION=$(python -m synapse.app.homeserver --version 2>&1 | head -1)
|
||||
echo "New version: $NEW_VERSION"
|
||||
|
||||
# Restart both Synapse instances
|
||||
echo "Restarting Synapse instances..."
|
||||
pkill -f 'synapse.app.homeserver' || true
|
||||
sleep 2
|
||||
|
||||
# Start mx.vish.gg (primary)
|
||||
sudo -u synapse /opt/synapse/venv/bin/python -m synapse.app.homeserver \
|
||||
--config-path=/opt/synapse-mx/homeserver.yaml --daemonize
|
||||
echo " - mx.vish.gg started on port 8018"
|
||||
|
||||
# Start legacy vish
|
||||
sudo -u synapse /opt/synapse/venv/bin/python -m synapse.app.homeserver \
|
||||
--config-path=/opt/synapse/homeserver.yaml --daemonize
|
||||
echo " - vish (legacy) started on port 8008"
|
||||
|
||||
deactivate
|
||||
echo "✅ Matrix Synapse updated!"
|
||||
echo ""
|
||||
|
||||
# Update TURN server
|
||||
echo "[4/5] Updating TURN server (coturn)..."
|
||||
sudo apt-get update -qq
|
||||
sudo apt-get install -y coturn 2>/dev/null && echo "✅ Coturn updated!" || echo "⚠️ Coturn update skipped"
|
||||
sudo systemctl restart coturn 2>/dev/null || true
|
||||
echo ""
|
||||
|
||||
# Update system packages
|
||||
echo "[5/5] Updating system packages..."
|
||||
sudo apt-get update
|
||||
sudo apt-get upgrade -y
|
||||
sudo apt-get autoremove -y
|
||||
echo "✅ System packages updated!"
|
||||
echo ""
|
||||
|
||||
# Verification
|
||||
echo "=== Verification ==="
|
||||
echo ""
|
||||
echo "Mastodon:"
|
||||
docker compose -f /opt/mastodon/docker-compose.yml ps --format "table {{.Name}}\t{{.Status}}" 2>/dev/null | head -5
|
||||
|
||||
echo ""
|
||||
echo "Mattermost:"
|
||||
docker ps --filter "name=mattermost" --format "table {{.Names}}\t{{.Status}}"
|
||||
|
||||
echo ""
|
||||
echo "Matrix Synapse:"
|
||||
curl -s http://localhost:8018/_matrix/federation/v1/version 2>/dev/null && echo " (mx.vish.gg)" || echo "❌ mx.vish.gg not responding"
|
||||
curl -s http://localhost:8008/_matrix/federation/v1/version 2>/dev/null && echo " (vish legacy)" || echo "❌ vish not responding"
|
||||
|
||||
echo ""
|
||||
echo "=== Update Complete ==="
|
||||
echo "Finished at: $(date)"
|
||||
16
hosts/vms/matrix-ubuntu-vm/systemd/synapse-mx.service
Normal file
16
hosts/vms/matrix-ubuntu-vm/systemd/synapse-mx.service
Normal file
@@ -0,0 +1,16 @@
|
||||
[Unit]
|
||||
Description=Synapse Matrix Homeserver (mx.vish.gg)
|
||||
After=network.target postgresql.service
|
||||
|
||||
[Service]
|
||||
Type=notify
|
||||
User=synapse
|
||||
Group=synapse
|
||||
WorkingDirectory=/opt/synapse-mx
|
||||
ExecStart=/opt/synapse/venv/bin/python -m synapse.app.homeserver --config-path=/opt/synapse-mx/homeserver.yaml
|
||||
ExecReload=/bin/kill -HUP $MAINPID
|
||||
Restart=on-failure
|
||||
RestartSec=5
|
||||
|
||||
[Install]
|
||||
WantedBy=multi-user.target
|
||||
16
hosts/vms/matrix-ubuntu-vm/systemd/synapse.service
Normal file
16
hosts/vms/matrix-ubuntu-vm/systemd/synapse.service
Normal file
@@ -0,0 +1,16 @@
|
||||
[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
|
||||
Reference in New Issue
Block a user