Sanitized mirror from private repository - 2026-04-20 01:32:01 UTC
This commit is contained in:
74
docs/services/mattermost/README.md
Normal file
74
docs/services/mattermost/README.md
Normal file
@@ -0,0 +1,74 @@
|
||||
# Mattermost Production Deployment
|
||||
|
||||
Production-ready Mattermost Team Edition deployment for **mm.crista.love**
|
||||
|
||||
## Architecture
|
||||
|
||||
- **Mattermost Team Edition** - Running in Docker
|
||||
- **PostgreSQL 15** - Database (Docker)
|
||||
- **Nginx** - Reverse proxy with SSL termination
|
||||
- **Cloudflare** - DNS and SSL (Full Strict mode with Origin Certificate)
|
||||
- **Backblaze B2** - File storage (S3-compatible)
|
||||
- **Automated Backups** - Daily to Backblaze B2
|
||||
|
||||
## Server Details
|
||||
|
||||
- **Server**: YOUR_WAN_IP
|
||||
- **Domain**: mm.crista.love
|
||||
- **OS**: Ubuntu 24.04 LTS
|
||||
|
||||
## Files
|
||||
|
||||
| File | Description |
|
||||
|------|-------------|
|
||||
| `deploy-mattermost.sh` | Main deployment script |
|
||||
| `mattermost-nginx.conf` | Nginx reverse proxy configuration |
|
||||
| `mattermost-backup.sh` | Automated backup script |
|
||||
| `mm-crista-love.crt` | Cloudflare Origin SSL certificate |
|
||||
| `mm-crista-love.key` | SSL private key |
|
||||
|
||||
## Deployment
|
||||
|
||||
1. Copy all files to server
|
||||
2. Run `deploy-mattermost.sh` as root
|
||||
3. Visit https://mm.crista.love to create admin account
|
||||
|
||||
## Configuration
|
||||
|
||||
### Email (SMTP)
|
||||
- Gmail with app password
|
||||
- STARTTLS on port 587
|
||||
|
||||
### File Storage
|
||||
- Backblaze B2 (S3-compatible)
|
||||
- Bucket: `vk-mattermost`
|
||||
|
||||
### Backups
|
||||
- Daily at 3 AM UTC
|
||||
- Stored in B2: `vk-mattermost/backups/`
|
||||
- Retention: 30 days remote, 7 days local
|
||||
|
||||
## Management Commands
|
||||
|
||||
```bash
|
||||
# View logs
|
||||
docker compose -f /opt/mattermost/docker-compose.yml logs -f
|
||||
|
||||
# Restart services
|
||||
docker compose -f /opt/mattermost/docker-compose.yml restart
|
||||
|
||||
# Manual backup
|
||||
/opt/mattermost/backup.sh
|
||||
|
||||
# Check status
|
||||
docker compose -f /opt/mattermost/docker-compose.yml ps
|
||||
```
|
||||
|
||||
## Security Notes
|
||||
|
||||
⚠️ **Important**: The actual credentials are stored in:
|
||||
- `/opt/mattermost/.env` - PostgreSQL password
|
||||
- `~/.aws/credentials` - B2 credentials
|
||||
- Docker environment variables - SMTP credentials
|
||||
|
||||
The files in this repo contain placeholder references. Actual secrets should never be committed.
|
||||
182
docs/services/mattermost/deploy-mattermost-synology.sh
Normal file
182
docs/services/mattermost/deploy-mattermost-synology.sh
Normal file
@@ -0,0 +1,182 @@
|
||||
#!/bin/bash
|
||||
# Mattermost Deployment Script for Synology Reverse Proxy Setup
|
||||
# Uses local storage (no B2) and external PostgreSQL
|
||||
|
||||
echo "=============================================="
|
||||
echo "Mattermost Production Deployment (Synology)"
|
||||
echo "Domain: mm.crista.love"
|
||||
echo "=============================================="
|
||||
|
||||
# Variables - UPDATE THESE
|
||||
SMTP_HOST="${SMTP_HOST:-smtp.gmail.com}"
|
||||
SMTP_PORT="${SMTP_PORT:-587}"
|
||||
SMTP_USER="${SMTP_USER:-your-email@example.com}"
|
||||
SMTP_PASS="REDACTED_PASSWORD"
|
||||
DB_PASSWORD="REDACTED_PASSWORD"
|
||||
SITE_URL="${SITE_URL:-https://mm.crista.love}"
|
||||
|
||||
echo "=== Step 1: Install Docker ==="
|
||||
if ! command -v docker &> /dev/null; then
|
||||
curl -fsSL https://get.docker.com | sh
|
||||
systemctl enable docker
|
||||
systemctl start docker
|
||||
fi
|
||||
|
||||
# Install docker compose plugin if needed
|
||||
apt-get update
|
||||
apt-get install -y docker-compose-plugin || true
|
||||
|
||||
echo "=== Step 2: Install and configure PostgreSQL ==="
|
||||
if ! command -v psql &> /dev/null; then
|
||||
apt-get install -y postgresql postgresql-contrib
|
||||
systemctl enable postgresql
|
||||
systemctl start postgresql
|
||||
fi
|
||||
|
||||
# Create database and user
|
||||
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 "GRANT ALL PRIVILEGES ON DATABASE mattermost TO mmuser;" 2>/dev/null || true
|
||||
|
||||
# Configure PostgreSQL to accept Docker connections
|
||||
PG_HBA=$(find /etc/postgresql -name pg_hba.conf | head -1)
|
||||
PG_CONF=$(find /etc/postgresql -name postgresql.conf | head -1)
|
||||
|
||||
if ! grep -q "172.17.0.0/16" "$PG_HBA"; then
|
||||
echo "# Docker networks for Mattermost" >> "$PG_HBA"
|
||||
echo "host mattermost mmuser 172.17.0.0/16 scram-sha-256" >> "$PG_HBA"
|
||||
echo "host mattermost mmuser 172.18.0.0/16 scram-sha-256" >> "$PG_HBA"
|
||||
echo "host mattermost mmuser 172.19.0.0/16 scram-sha-256" >> "$PG_HBA"
|
||||
fi
|
||||
|
||||
# Configure PostgreSQL to listen on all interfaces
|
||||
if ! grep -q "listen_addresses = '\*'" "$PG_CONF"; then
|
||||
sed -i "s/#listen_addresses = 'localhost'/listen_addresses = '*'/" "$PG_CONF"
|
||||
fi
|
||||
|
||||
systemctl restart postgresql
|
||||
|
||||
echo "=== Step 3: Create directory structure ==="
|
||||
mkdir -p /opt/mattermost/{config,data,logs,plugins,client-plugins,backups}
|
||||
|
||||
echo "=== Step 4: Create environment file ==="
|
||||
cat > /opt/mattermost/.env << EOF
|
||||
MM_EMAILSETTINGS_SMTPPASSWORD="REDACTED_PASSWORD"
|
||||
EOF
|
||||
chmod 600 /opt/mattermost/.env
|
||||
|
||||
echo "=== Step 5: Create Docker Compose file ==="
|
||||
# Get Docker bridge IP
|
||||
DOCKER_HOST_IP=$(ip -4 addr show docker0 | grep -oP '(?<=inet\s)\d+(\.\d+){3}' || echo "172.17.0.1")
|
||||
|
||||
cat > /opt/mattermost/docker-compose.yml << EOF
|
||||
services:
|
||||
mattermost:
|
||||
image: mattermost/mattermost-team-edition:11.3
|
||||
container_name: mattermost
|
||||
restart: unless-stopped
|
||||
security_opt:
|
||||
- no-new-privileges:true
|
||||
pids_limit: 200
|
||||
read_only: false
|
||||
tmpfs:
|
||||
- /tmp
|
||||
ports:
|
||||
- "8065:8065"
|
||||
environment:
|
||||
TZ: UTC
|
||||
MM_SQLSETTINGS_DRIVERNAME: postgres
|
||||
MM_SQLSETTINGS_DATASOURCE: "postgres://mmuser:${DB_PASSWORD}@${DOCKER_HOST_IP}:5432/mattermost?sslmode=disable&connect_timeout=10"
|
||||
MM_SERVICESETTINGS_SITEURL: ${SITE_URL}
|
||||
MM_SERVICESETTINGS_LISTENADDRESS: ":8065"
|
||||
MM_FILESETTINGS_DRIVERNAME: local
|
||||
MM_FILESETTINGS_DIRECTORY: /mattermost/data
|
||||
MM_LOGSETTINGS_CONSOLELEVEL: INFO
|
||||
MM_LOGSETTINGS_FILELEVEL: INFO
|
||||
MM_EMAILSETTINGS_ENABLESMTPAUTH: "true"
|
||||
MM_EMAILSETTINGS_SMTPSERVER: ${SMTP_HOST}
|
||||
MM_EMAILSETTINGS_SMTPPORT: "${SMTP_PORT}"
|
||||
MM_EMAILSETTINGS_CONNECTIONSECURITY: STARTTLS
|
||||
MM_EMAILSETTINGS_SMTPUSERNAME: ${SMTP_USER}
|
||||
MM_EMAILSETTINGS_FEEDBACKEMAIL: ${SMTP_USER}
|
||||
MM_EMAILSETTINGS_FEEDBACKNAME: Mattermost
|
||||
MM_EMAILSETTINGS_SENDEMAILNOTIFICATIONS: "true"
|
||||
MM_TEAMSETTINGS_ENABLEOPENSERVER: "true"
|
||||
MM_TEAMSETTINGS_MAXUSERSPERTEAM: "50"
|
||||
env_file:
|
||||
- .env
|
||||
volumes:
|
||||
- /opt/mattermost/config:/mattermost/config:rw
|
||||
- /opt/mattermost/data:/mattermost/data:rw
|
||||
- /opt/mattermost/logs:/mattermost/logs:rw
|
||||
- /opt/mattermost/plugins:/mattermost/plugins:rw
|
||||
- /opt/mattermost/client-plugins:/mattermost/client/plugins:rw
|
||||
healthcheck:
|
||||
test: ["CMD", "curl", "-f", "http://localhost:8065/api/v4/system/ping"]
|
||||
interval: 30s
|
||||
timeout: 10s
|
||||
retries: 3
|
||||
start_period: 60s
|
||||
extra_hosts:
|
||||
- "host.docker.internal:host-gateway"
|
||||
EOF
|
||||
|
||||
echo "=== Step 6: Create backup script ==="
|
||||
cat > /opt/mattermost/backup.sh << 'BACKUP'
|
||||
#!/bin/bash
|
||||
BACKUP_DIR=/opt/mattermost/backups
|
||||
DATE=$(date +%Y%m%d_%H%M%S)
|
||||
sudo -u postgres pg_dump mattermost | gzip > $BACKUP_DIR/mattermost_db_$DATE.sql.gz
|
||||
tar -czf $BACKUP_DIR/mattermost_data_$DATE.tar.gz -C /opt/mattermost data config
|
||||
find $BACKUP_DIR -name "*.gz" -mtime +7 -delete
|
||||
echo "Backup completed: $DATE"
|
||||
BACKUP
|
||||
chmod +x /opt/mattermost/backup.sh
|
||||
|
||||
echo "=== Step 7: Set up backup cron job ==="
|
||||
echo '0 3 * * * root /opt/mattermost/backup.sh >> /var/log/mattermost-backup.log 2>&1' > /etc/cron.d/mattermost-backup
|
||||
chmod 644 /etc/cron.d/mattermost-backup
|
||||
|
||||
echo "=== Step 8: Start Mattermost ==="
|
||||
cd /opt/mattermost
|
||||
docker compose pull
|
||||
docker compose up -d
|
||||
|
||||
echo "=== Step 9: Wait for Mattermost to be healthy ==="
|
||||
echo "Waiting for services to start..."
|
||||
sleep 30
|
||||
|
||||
MAX_ATTEMPTS=30
|
||||
ATTEMPT=0
|
||||
until curl -sf http://127.0.0.1:8065/api/v4/system/ping > /dev/null 2>&1; do
|
||||
ATTEMPT=$((ATTEMPT + 1))
|
||||
if [ $ATTEMPT -ge $MAX_ATTEMPTS ]; then
|
||||
echo "Mattermost did not become healthy in time. Checking logs..."
|
||||
docker compose logs --tail=100
|
||||
exit 1
|
||||
fi
|
||||
echo "Waiting for Mattermost to be ready... (attempt $ATTEMPT/$MAX_ATTEMPTS)"
|
||||
sleep 5
|
||||
done
|
||||
echo "Mattermost is healthy!"
|
||||
|
||||
echo "=============================================="
|
||||
echo "Mattermost Deployment Complete!"
|
||||
echo "=============================================="
|
||||
echo ""
|
||||
echo "Mattermost is running on port 8065"
|
||||
echo ""
|
||||
echo "Configure your Synology Reverse Proxy:"
|
||||
echo " Source: HTTPS, mm.crista.love, port 443"
|
||||
echo " Destination: HTTP, <this-machine-ip>, port 8065"
|
||||
echo ""
|
||||
echo "Backup schedule: Daily at 3 AM UTC"
|
||||
echo "Backups stored in: /opt/mattermost/backups/"
|
||||
echo ""
|
||||
echo "Useful commands:"
|
||||
echo " View logs: docker compose -f /opt/mattermost/docker-compose.yml logs -f"
|
||||
echo " Restart: docker compose -f /opt/mattermost/docker-compose.yml restart"
|
||||
echo " Manual backup: /opt/mattermost/backup.sh"
|
||||
echo ""
|
||||
|
||||
docker compose ps
|
||||
219
docs/services/mattermost/deploy-mattermost.sh
Normal file
219
docs/services/mattermost/deploy-mattermost.sh
Normal file
@@ -0,0 +1,219 @@
|
||||
#!/bin/bash
|
||||
# Complete Mattermost Deployment Script
|
||||
|
||||
set -e
|
||||
|
||||
echo "=============================================="
|
||||
echo "Mattermost Production Deployment"
|
||||
echo "Domain: mm.crista.love"
|
||||
echo "=============================================="
|
||||
|
||||
# Variables - UPDATE THESE WITH YOUR ACTUAL VALUES
|
||||
B2_KEY_ID="${B2_KEY_ID:-your-b2-key-id}"
|
||||
B2_APP_KEY="${B2_APP_KEY:REDACTED_APP_KEY}"
|
||||
B2_ENDPOINT="${B2_ENDPOINT:-s3.us-west-004.backblazeb2.com}"
|
||||
B2_BUCKET="${B2_BUCKET:-your-bucket-name}"
|
||||
SMTP_HOST="${SMTP_HOST:-smtp.gmail.com}"
|
||||
SMTP_PORT="${SMTP_PORT:-587}"
|
||||
SMTP_USER="${SMTP_USER:-your-email@gmail.com}"
|
||||
SMTP_PASS="REDACTED_PASSWORD"
|
||||
|
||||
echo "=== Step 1: Install Docker Compose plugin ==="
|
||||
apt-get update
|
||||
apt-get install -y docker-compose-plugin unzip
|
||||
|
||||
echo "=== Step 2: Install AWS CLI for B2 backups ==="
|
||||
if ! command -v aws &> /dev/null; then
|
||||
curl "https://awscli.amazonaws.com/awscli-exe-linux-x86_64.zip" -o "/tmp/awscliv2.zip"
|
||||
unzip -q /tmp/awscliv2.zip -d /tmp
|
||||
/tmp/aws/install
|
||||
rm -rf /tmp/aws /tmp/awscliv2.zip
|
||||
fi
|
||||
|
||||
# Configure AWS CLI for Backblaze B2
|
||||
mkdir -p ~/.aws
|
||||
cat > ~/.aws/credentials << EOF
|
||||
[default]
|
||||
aws_access_key_id = ${B2_KEY_ID}
|
||||
aws_secret_access_key = ${B2_APP_KEY}
|
||||
EOF
|
||||
|
||||
cat > ~/.aws/config << EOF
|
||||
[default]
|
||||
region = us-west-004
|
||||
EOF
|
||||
|
||||
echo "=== Step 3: Create directory structure ==="
|
||||
mkdir -p /opt/mattermost/{config,data,logs,plugins,client/plugins,bleve-indexes,backups}
|
||||
mkdir -p /etc/nginx/ssl
|
||||
mkdir -p /var/cache/nginx/mattermost
|
||||
|
||||
echo "=== Step 4: Generate PostgreSQL password ==="
|
||||
POSTGRES_PASSWORD="REDACTED_PASSWORD" rand -base64 32 | tr -dc 'a-zA-Z0-9' | head -c 32)
|
||||
echo "POSTGRES_PASSWORD="REDACTED_PASSWORD" > /opt/mattermost/.env
|
||||
chmod 600 /opt/mattermost/.env
|
||||
|
||||
echo "=== Step 5: Create Docker Compose file ==="
|
||||
cat > /opt/mattermost/docker-compose.yml << EOF
|
||||
services:
|
||||
postgres:
|
||||
image: postgres:15-alpine
|
||||
container_name: mattermost-postgres
|
||||
restart: unless-stopped
|
||||
security_opt:
|
||||
- no-new-privileges:true
|
||||
pids_limit: 100
|
||||
read_only: true
|
||||
tmpfs:
|
||||
- /tmp
|
||||
- /var/run/postgresql
|
||||
volumes:
|
||||
- postgres_data:/var/lib/postgresql/data
|
||||
environment:
|
||||
- POSTGRES_USER=mmuser
|
||||
- POSTGRES_PASSWORD="REDACTED_PASSWORD"
|
||||
- POSTGRES_DB=mattermost
|
||||
networks:
|
||||
- mattermost-network
|
||||
healthcheck:
|
||||
test: ["CMD-SHELL", "pg_isready -U mmuser -d mattermost"]
|
||||
interval: 10s
|
||||
timeout: 5s
|
||||
retries: 5
|
||||
|
||||
mattermost:
|
||||
image: mattermost/mattermost-team-edition:latest
|
||||
container_name: mattermost
|
||||
restart: unless-stopped
|
||||
depends_on:
|
||||
postgres:
|
||||
condition: service_healthy
|
||||
security_opt:
|
||||
- no-new-privileges:true
|
||||
pids_limit: 200
|
||||
tmpfs:
|
||||
- /tmp
|
||||
volumes:
|
||||
- /opt/mattermost/config:/mattermost/config:rw
|
||||
- /opt/mattermost/data:/mattermost/data:rw
|
||||
- /opt/mattermost/logs:/mattermost/logs:rw
|
||||
- /opt/mattermost/plugins:/mattermost/plugins:rw
|
||||
- /opt/mattermost/client/plugins:/mattermost/client/plugins:rw
|
||||
- /opt/mattermost/bleve-indexes:/mattermost/bleve-indexes:rw
|
||||
environment:
|
||||
- TZ=UTC
|
||||
- MM_SQLSETTINGS_DRIVERNAME=postgres
|
||||
- MM_SQLSETTINGS_DATASOURCE=postgres://mmuser:${POSTGRES_PASSWORD}@postgres:5432/mattermost?sslmode=disable&connect_timeout=10
|
||||
- MM_BLEVESETTINGS_INDEXDIR=/mattermost/bleve-indexes
|
||||
- MM_SERVICESETTINGS_SITEURL=https://mm.crista.love
|
||||
- MM_SERVICESETTINGS_LISTENADDRESS=:8065
|
||||
# Email Settings
|
||||
- MM_EMAILSETTINGS_ENABLESMTPAUTH=true
|
||||
- MM_EMAILSETTINGS_SMTPUSERNAME=${SMTP_USER}
|
||||
- MM_EMAILSETTINGS_SMTPPASSWORD="REDACTED_PASSWORD"
|
||||
- MM_EMAILSETTINGS_SMTPSERVER=${SMTP_HOST}
|
||||
- MM_EMAILSETTINGS_SMTPPORT=${SMTP_PORT}
|
||||
- MM_EMAILSETTINGS_CONNECTIONSECURITY=STARTTLS
|
||||
- MM_EMAILSETTINGS_FEEDBACKEMAIL=${SMTP_USER}
|
||||
- MM_EMAILSETTINGS_REPLYTOADDRESS=${SMTP_USER}
|
||||
- MM_EMAILSETTINGS_SENDEMAILNOTIFICATIONS=true
|
||||
# File Storage - Backblaze B2
|
||||
- MM_FILESETTINGS_DRIVERNAME=amazons3
|
||||
- MM_FILESETTINGS_AMAZONS3ACCESSKEYID=${B2_KEY_ID}
|
||||
- MM_FILESETTINGS_AMAZONS3SECRETACCESSKEY=${B2_APP_KEY}
|
||||
- MM_FILESETTINGS_AMAZONS3BUCKET=${B2_BUCKET}
|
||||
- MM_FILESETTINGS_AMAZONS3ENDPOINT=${B2_ENDPOINT}
|
||||
- MM_FILESETTINGS_AMAZONS3SSL=true
|
||||
- MM_FILESETTINGS_AMAZONS3SIGNV2=false
|
||||
- MM_FILESETTINGS_AMAZONS3REGION=us-west-004
|
||||
# Security
|
||||
- MM_SERVICESETTINGS_ENABLESECURITYFIXALERT=true
|
||||
- MM_PASSWORDSETTINGS_MINIMUMLENGTH=10
|
||||
ports:
|
||||
- "127.0.0.1:8065:8065"
|
||||
networks:
|
||||
- mattermost-network
|
||||
|
||||
networks:
|
||||
mattermost-network:
|
||||
driver: bridge
|
||||
|
||||
volumes:
|
||||
postgres_data:
|
||||
EOF
|
||||
|
||||
echo "=== Step 6: Set directory permissions ==="
|
||||
chown -R 2000:2000 /opt/mattermost/config /opt/mattermost/data /opt/mattermost/logs /opt/mattermost/plugins /opt/mattermost/client/plugins /opt/mattermost/bleve-indexes
|
||||
|
||||
echo "=== Step 7: Start Mattermost containers ==="
|
||||
cd /opt/mattermost
|
||||
docker compose pull
|
||||
docker compose up -d
|
||||
|
||||
echo "=== Step 8: Wait for Mattermost to be healthy ==="
|
||||
echo "Waiting for services to start..."
|
||||
sleep 15
|
||||
|
||||
# Wait for Mattermost to be ready
|
||||
MAX_ATTEMPTS=30
|
||||
ATTEMPT=0
|
||||
until curl -sf http://127.0.0.1:8065/api/v4/system/ping > /dev/null 2>&1; do
|
||||
ATTEMPT=$((ATTEMPT + 1))
|
||||
if [ $ATTEMPT -ge $MAX_ATTEMPTS ]; then
|
||||
echo "Mattermost did not become healthy in time. Checking logs..."
|
||||
docker compose logs --tail=100
|
||||
exit 1
|
||||
fi
|
||||
echo "Waiting for Mattermost to be ready... (attempt $ATTEMPT/$MAX_ATTEMPTS)"
|
||||
sleep 5
|
||||
done
|
||||
echo "Mattermost is healthy!"
|
||||
|
||||
echo "=== Step 9: Configure Nginx ==="
|
||||
# Nginx config should already be copied
|
||||
|
||||
# Create cache directory
|
||||
mkdir -p /var/cache/nginx/mattermost
|
||||
chown www-data:www-data /var/cache/nginx/mattermost
|
||||
|
||||
# Enable the site
|
||||
ln -sf /etc/nginx/sites-available/mattermost /etc/nginx/sites-enabled/mattermost
|
||||
|
||||
# Test nginx config
|
||||
nginx -t
|
||||
|
||||
# Reload nginx
|
||||
systemctl reload nginx
|
||||
|
||||
echo "=== Step 10: Set up automated backups ==="
|
||||
chmod +x /opt/mattermost/backup.sh
|
||||
|
||||
# Add cron job for daily backups at 3 AM
|
||||
(crontab -l 2>/dev/null | grep -v "mattermost/backup.sh"; echo "0 3 * * * /opt/mattermost/backup.sh >> /var/log/mattermost-backup.log 2>&1") | crontab -
|
||||
|
||||
echo "=== Step 11: Enable open signups ==="
|
||||
docker exec mattermost /mattermost/bin/mmctl config set TeamSettings.REDACTED_APP_PASSWORD true --local
|
||||
docker restart mattermost
|
||||
sleep 15
|
||||
|
||||
echo "=============================================="
|
||||
echo "Mattermost Deployment Complete!"
|
||||
echo "=============================================="
|
||||
echo ""
|
||||
echo "Access Mattermost at: https://mm.crista.love"
|
||||
echo ""
|
||||
echo "Next steps:"
|
||||
echo "1. Visit https://mm.crista.love to create your admin account"
|
||||
echo "2. The first user to sign up becomes the system admin"
|
||||
echo ""
|
||||
echo "Backup schedule: Daily at 3 AM UTC"
|
||||
echo "Backups stored in: Backblaze B2 (${B2_BUCKET}/backups/)"
|
||||
echo ""
|
||||
echo "Useful commands:"
|
||||
echo " View logs: docker compose -f /opt/mattermost/docker-compose.yml logs -f"
|
||||
echo " Restart: docker compose -f /opt/mattermost/docker-compose.yml restart"
|
||||
echo " Manual backup: /opt/mattermost/backup.sh"
|
||||
echo ""
|
||||
|
||||
# Show container status
|
||||
docker compose ps
|
||||
56
docs/services/mattermost/mattermost-backup.sh
Normal file
56
docs/services/mattermost/mattermost-backup.sh
Normal file
@@ -0,0 +1,56 @@
|
||||
#!/bin/bash
|
||||
# Mattermost Automated Backup Script
|
||||
# Backs up PostgreSQL database and uploads to Backblaze B2
|
||||
|
||||
set -e
|
||||
|
||||
BACKUP_DIR="/opt/mattermost/backups"
|
||||
DATE=$(date +%Y%m%d_%H%M%S)
|
||||
BACKUP_FILE="mattermost_backup_${DATE}.sql.gz"
|
||||
RETENTION_DAYS=30
|
||||
|
||||
# Create backup directory
|
||||
mkdir -p ${BACKUP_DIR}
|
||||
|
||||
echo "[$(date)] Starting Mattermost backup..."
|
||||
|
||||
# Get PostgreSQL password
|
||||
source /opt/mattermost/.env
|
||||
|
||||
# Backup PostgreSQL database
|
||||
echo "[$(date)] Backing up PostgreSQL database..."
|
||||
docker exec mattermost-postgres pg_dump -U mmuser -d mattermost | gzip > ${BACKUP_DIR}/${BACKUP_FILE}
|
||||
|
||||
# Check backup size
|
||||
BACKUP_SIZE=$(ls -lh ${BACKUP_DIR}/${BACKUP_FILE} | awk '{print $5}')
|
||||
echo "[$(date)] Backup created: ${BACKUP_FILE} (${BACKUP_SIZE})"
|
||||
|
||||
# Upload to Backblaze B2 using S3 API
|
||||
echo "[$(date)] Uploading to Backblaze B2..."
|
||||
/usr/local/bin/aws s3 cp ${BACKUP_DIR}/${BACKUP_FILE} s3://vk-mattermost/backups/${BACKUP_FILE} \
|
||||
--endpoint-url https://s3.us-west-004.backblazeb2.com
|
||||
|
||||
if [ $? -eq 0 ]; then
|
||||
echo "[$(date)] Upload successful!"
|
||||
else
|
||||
echo "[$(date)] Upload failed!"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
# Clean up old local backups (keep last 7 days locally)
|
||||
echo "[$(date)] Cleaning up old local backups..."
|
||||
find ${BACKUP_DIR} -name "mattermost_backup_*.sql.gz" -mtime +7 -delete
|
||||
|
||||
# Clean up old remote backups (keep last 30 days)
|
||||
echo "[$(date)] Cleaning up old remote backups..."
|
||||
CUTOFF_DATE=$(date -d "-${RETENTION_DAYS} days" +%Y%m%d)
|
||||
/usr/local/bin/aws s3 ls s3://vk-mattermost/backups/ --endpoint-url https://s3.us-west-004.backblazeb2.com | while read -r line; do
|
||||
FILE_DATE=$(echo "$line" | awk '{print $4}' | grep -oP '\d{8}' | head -1)
|
||||
FILE_NAME=$(echo "$line" | awk '{print $4}')
|
||||
if [[ -n "$FILE_DATE" && "$FILE_DATE" < "$CUTOFF_DATE" ]]; then
|
||||
echo "[$(date)] Deleting old backup: ${FILE_NAME}"
|
||||
/usr/local/bin/aws s3 rm s3://vk-mattermost/backups/${FILE_NAME} --endpoint-url https://s3.us-west-004.backblazeb2.com
|
||||
fi
|
||||
done
|
||||
|
||||
echo "[$(date)] Backup completed successfully!"
|
||||
100
docs/services/mattermost/mattermost-nginx.conf
Normal file
100
docs/services/mattermost/mattermost-nginx.conf
Normal file
@@ -0,0 +1,100 @@
|
||||
upstream mattermost_backend {
|
||||
server 127.0.0.1:8065;
|
||||
keepalive 32;
|
||||
}
|
||||
|
||||
proxy_cache_path /var/cache/nginx/mattermost levels=1:2 keys_zone=mattermost_cache:10m max_size=3g inactive=120m use_temp_path=off;
|
||||
|
||||
server {
|
||||
listen 80;
|
||||
listen [::]:80;
|
||||
server_name mm.crista.love;
|
||||
|
||||
# Redirect all HTTP to HTTPS
|
||||
return 301 https://$server_name$request_uri;
|
||||
}
|
||||
|
||||
server {
|
||||
listen 443 ssl http2;
|
||||
listen [::]:443 ssl http2;
|
||||
server_name mm.crista.love;
|
||||
|
||||
# SSL Configuration - Cloudflare Origin Certificate
|
||||
ssl_certificate /etc/nginx/ssl/mm-crista-love.crt;
|
||||
ssl_certificate_key /etc/nginx/ssl/mm-crista-love.key;
|
||||
|
||||
# Modern SSL configuration
|
||||
ssl_protocols TLSv1.2 TLSv1.3;
|
||||
ssl_ciphers ECDHE-ECDSA-AES128-GCM-SHA256:ECDHE-RSA-AES128-GCM-SHA256:ECDHE-ECDSA-AES256-GCM-SHA384:ECDHE-RSA-AES256-GCM-SHA384:ECDHE-ECDSA-CHACHA20-POLY1305:ECDHE-RSA-CHACHA20-POLY1305:DHE-RSA-AES128-GCM-SHA256:DHE-RSA-AES256-GCM-SHA384;
|
||||
ssl_prefer_server_ciphers off;
|
||||
ssl_session_timeout 1d;
|
||||
ssl_session_cache shared:SSL:50m;
|
||||
ssl_session_tickets off;
|
||||
|
||||
# Security Headers
|
||||
add_header X-Frame-Options "SAMEORIGIN" always;
|
||||
add_header X-Content-Type-Options "nosniff" always;
|
||||
add_header X-XSS-Protection "1; mode=block" always;
|
||||
add_header Referrer-Policy "strict-origin-when-cross-origin" always;
|
||||
|
||||
# Logging
|
||||
access_log /var/log/nginx/mattermost-access.log;
|
||||
error_log /var/log/nginx/mattermost-error.log;
|
||||
|
||||
# Disable server tokens
|
||||
server_tokens off;
|
||||
|
||||
# Max upload size (for file attachments)
|
||||
client_max_body_size 100M;
|
||||
|
||||
location ~ /api/v[0-9]+/(users/)?websocket$ {
|
||||
proxy_set_header Upgrade $http_upgrade;
|
||||
proxy_set_header Connection "upgrade";
|
||||
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;
|
||||
client_body_timeout 60;
|
||||
send_timeout 300;
|
||||
lingering_timeout 5;
|
||||
proxy_connect_timeout 90;
|
||||
proxy_send_timeout 300;
|
||||
proxy_read_timeout 90s;
|
||||
proxy_http_version 1.1;
|
||||
proxy_pass http://mattermost_backend;
|
||||
}
|
||||
|
||||
location / {
|
||||
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_backend;
|
||||
|
||||
# Static asset caching
|
||||
location ~ ^/static/ {
|
||||
proxy_pass http://mattermost_backend;
|
||||
proxy_cache mattermost_cache;
|
||||
proxy_cache_valid 200 1d;
|
||||
proxy_cache_use_stale error timeout updating http_500 http_502 http_503 http_504;
|
||||
proxy_cache_revalidate on;
|
||||
proxy_cache_background_update on;
|
||||
add_header X-Cache-Status $upstream_cache_status;
|
||||
}
|
||||
}
|
||||
|
||||
# Health check endpoint
|
||||
location = /health {
|
||||
proxy_pass http://mattermost_backend;
|
||||
proxy_http_version 1.1;
|
||||
proxy_set_header Host $http_host;
|
||||
}
|
||||
}
|
||||
27
docs/services/mattermost/mm-crista-love.crt
Normal file
27
docs/services/mattermost/mm-crista-love.crt
Normal file
@@ -0,0 +1,27 @@
|
||||
-----BEGIN CERTIFICATE-----
|
||||
MIIEojCCA4qgAwIBAgIUPrDC9IZU5unV4kUy0cBsm9DlEJAwDQYJKoZIhvcNAQEL
|
||||
BQAwgYsxCzAJBgNVBAYTAlVTMRkwFwYDVQQKExBDbG91ZEZsYXJlLCBJbmMuMTQw
|
||||
MgYDVQQLEytDbG91ZEZsYXJlIE9yaWdpbiBTU0wgQ2VydGlmaWNhdGUgQXV0aG9y
|
||||
aXR5MRYwFAYDVQQHEw1TYW4gRnJhbmNpc2NvMRMwEQYDVQQIEwpDYWxpZm9ybmlh
|
||||
MB4XDTI2MDEyNTA5MDEwMFoXDTQxMDEyMTA5MDEwMFowYjEZMBcGA1UEChMQQ2xv
|
||||
dWRGbGFyZSwgSW5jLjEdMBsGA1UECxMUQ2xvdWRGbGFyZSBPcmlnaW4gQ0ExJjAk
|
||||
BgNVBAMTHUNsb3VkRmxhcmUgT3JpZ2luIENlcnRpZmljYXRlMIIBIjANBgkqhkiG
|
||||
9w0BAQEFAAOCAQ8AMIIBCgKCAQEA0e+rmGiUAH71cuXDd2xOaIqkYPeHIsDDtG1b
|
||||
dbdrtHdsInTNhWpIUqayMot53NeixfKNit++P4D9mUmdeSwPUDuzcYsTmvcFZPiY
|
||||
WATgp8nWF8PAkGNgd43kJqBylSis5TfCyRrBghHVIgt3WZ8ynbQVfmROf1YUnsa1
|
||||
KtO6WtkaKx8Oz6FeQHiamhj/k0XKritidl+CO7UXDzFi2xIe10H4+grhMs1SaK+8
|
||||
5Xib7ohyQTxyY5ELuAXq1R8bDmcBkatYbtwSdHeEEDmJtW7ILNJZ85uqG1Tp+RcG
|
||||
WQ1AjXzoqITAv6qO/ubyp3lcBPkVoeZlufYqGKf6Yu6m71SlAQIDAQABo4IBJDCC
|
||||
ASAwDgYDVR0PAQH/BAQDAgWgMB0GA1UdJQQWMBQGCCsGAQUFBwMCBggrBgEFBQcD
|
||||
ATAMBgNVHRMBAf8EAjAAMB0GA1UdDgQWBBRB+YxBgtPDtcWedv62/8Xd3uR/rjAf
|
||||
BgNVHSMEGDAWgBQk6FNXXXw0QIep65TbuuEWePwppDBABggrBgEFBQcBAQQ0MDIw
|
||||
MAYIKwYBBQUHMAGGJGh0dHA6Ly9vY3NwLmNsb3VkZmxhcmUuY29tL29yaWdpbl9j
|
||||
YTAlBgNVHREEHjAcgg0qLmNyaXN0YS5sb3ZlggtjcmlzdGEubG92ZTA4BgNVHR8E
|
||||
MTAvMC2gK6AphidodHRwOi8vY3JsLmNsb3VkZmxhcmUuY29tL29yaWdpbl9jYS5j
|
||||
cmwwDQYJKoZIhvcNAQELBQADggEBAJ23KhTb+/EMa6WIskydfxbGJvnjVn+Ggs9L
|
||||
H3tNP3W+gVi5yjghMBTwN8rLHfIl122CSgI8SLg7tWm9d+EUsQdqR1KfoBideeCj
|
||||
EIITw6cHrJgCFP8x8SbO6b1t+qcgFW4d5aV5mRGj3UMZ+E5T9njG74c3xOQVIJ70
|
||||
T14ZU9KF/vnGimOUCJNvlRjgjfcrccv7e0p8+i/mBvqgZeAsSg1X7/zW7gzR/fJW
|
||||
FQO3ir4FKcKt4ItDCGnHA8FDA9PVuuxclAbOxZcW5i8ZBOxkQv37vScexGeeOI7b
|
||||
u2L9lRuLtyelvH8Pbt7p79RCGHcm+BslG41+uBKPNPxLGke3RjI=
|
||||
-----END CERTIFICATE-----
|
||||
Reference in New Issue
Block a user