724 lines
20 KiB
Bash
724 lines
20 KiB
Bash
#!/bin/bash
|
|
# =============================================================================
|
|
# Mastodon Production Installer
|
|
# =============================================================================
|
|
# Self-hosted Mastodon instance - production ready with Docker
|
|
#
|
|
# Supported: Ubuntu, Debian, Fedora, Rocky/Alma/RHEL 8+, Arch, openSUSE
|
|
# Deploys via Docker Compose
|
|
#
|
|
# Usage:
|
|
# curl -fsSL <url>/install.sh | sudo bash
|
|
#
|
|
# Options:
|
|
# --domain <domain> Your domain (required)
|
|
# --email <email> Admin email / Let's Encrypt
|
|
# --no-ssl Skip SSL (local testing only)
|
|
# --single-user Single user mode
|
|
# --s3 Enable S3 storage configuration
|
|
# =============================================================================
|
|
|
|
set -o pipefail
|
|
|
|
# Colors
|
|
RED='\033[0;31m'
|
|
GREEN='\033[0;32m'
|
|
YELLOW='\033[1;33m'
|
|
BLUE='\033[0;34m'
|
|
NC='\033[0m'
|
|
|
|
log() { echo -e "${BLUE}[INFO]${NC} $1"; }
|
|
success() { echo -e "${GREEN}[OK]${NC} $1"; }
|
|
warn() { echo -e "${YELLOW}[WARN]${NC} $1"; }
|
|
error() { echo -e "${RED}[ERROR]${NC} $1" >&2; exit 1; }
|
|
|
|
# Configuration
|
|
INSTALL_DIR="/opt/mastodon"
|
|
DATA_DIR="/opt/mastodon-data"
|
|
DOMAIN=""
|
|
ADMIN_EMAIL=""
|
|
ENABLE_SSL=true
|
|
SINGLE_USER_MODE=false
|
|
ENABLE_S3=false
|
|
|
|
# Parse arguments
|
|
while [ $# -gt 0 ]; do
|
|
case $1 in
|
|
--domain) DOMAIN="$2"; shift 2 ;;
|
|
--email) ADMIN_EMAIL="$2"; shift 2 ;;
|
|
--no-ssl) ENABLE_SSL=false; shift ;;
|
|
--single-user) SINGLE_USER_MODE=true; shift ;;
|
|
--s3) ENABLE_S3=true; shift ;;
|
|
--help|-h)
|
|
echo "Mastodon Production Installer"
|
|
echo ""
|
|
echo "Usage: install.sh [options]"
|
|
echo ""
|
|
echo "Options:"
|
|
echo " --domain <domain> Your domain (e.g., mastodon.example.com)"
|
|
echo " --email <email> Admin email for Let's Encrypt"
|
|
echo " --no-ssl Skip SSL (testing only)"
|
|
echo " --single-user Single user mode"
|
|
echo " --s3 Configure S3 storage"
|
|
exit 0
|
|
;;
|
|
*) shift ;;
|
|
esac
|
|
done
|
|
|
|
# Check root
|
|
[ "$(id -u)" -ne 0 ] && error "Run as root: sudo bash install.sh"
|
|
|
|
# Detect OS
|
|
detect_os() {
|
|
if [ -f /etc/os-release ]; then
|
|
. /etc/os-release
|
|
OS=$ID
|
|
OS_VERSION=${VERSION_ID:-}
|
|
else
|
|
error "Cannot detect OS"
|
|
fi
|
|
log "Detected: $OS $OS_VERSION"
|
|
}
|
|
|
|
# Wait for package manager locks
|
|
wait_for_lock() {
|
|
case $OS in
|
|
ubuntu|debian|linuxmint|pop)
|
|
while fuser /var/lib/dpkg/lock-frontend >/dev/null 2>&1; do
|
|
sleep 2
|
|
done
|
|
;;
|
|
esac
|
|
}
|
|
|
|
# Install Docker
|
|
install_docker() {
|
|
if command -v docker >/dev/null 2>&1; then
|
|
success "Docker already installed"
|
|
systemctl enable --now docker 2>/dev/null || true
|
|
return
|
|
fi
|
|
|
|
log "Installing Docker..."
|
|
|
|
case $OS in
|
|
ubuntu|debian|linuxmint|pop)
|
|
export DEBIAN_FRONTEND=noninteractive
|
|
wait_for_lock
|
|
apt-get update -qq
|
|
apt-get install -y -qq ca-certificates curl gnupg
|
|
|
|
install -m 0755 -d /etc/apt/keyrings
|
|
DOCKER_OS=$OS
|
|
case "$OS" in linuxmint|pop) DOCKER_OS="ubuntu" ;; esac
|
|
|
|
curl -fsSL https://download.docker.com/linux/$DOCKER_OS/gpg | gpg --dearmor -o /etc/apt/keyrings/docker.gpg 2>/dev/null
|
|
chmod a+r /etc/apt/keyrings/docker.gpg
|
|
|
|
CODENAME=${VERSION_CODENAME:-jammy}
|
|
case "$OS" in linuxmint|pop) CODENAME="jammy" ;; esac
|
|
[ "$OS" = "debian" ] && case "$CODENAME" in trixie|sid) CODENAME="bookworm" ;; esac
|
|
|
|
echo "deb [arch=$(dpkg --print-architecture) signed-by=/etc/apt/keyrings/docker.gpg] https://download.docker.com/linux/$DOCKER_OS $CODENAME stable" > /etc/apt/sources.list.d/docker.list
|
|
|
|
wait_for_lock
|
|
apt-get update -qq
|
|
apt-get install -y -qq docker-ce docker-ce-cli containerd.io docker-buildx-plugin docker-compose-plugin
|
|
;;
|
|
|
|
fedora)
|
|
dnf install -y -q dnf-plugins-core
|
|
dnf config-manager --add-repo https://download.docker.com/linux/fedora/docker-ce.repo
|
|
dnf install -y -q docker-ce docker-ce-cli containerd.io docker-buildx-plugin docker-compose-plugin
|
|
;;
|
|
|
|
rocky|almalinux|rhel|centos)
|
|
dnf install -y -q dnf-plugins-core || yum install -y yum-utils
|
|
dnf config-manager --add-repo https://download.docker.com/linux/centos/docker-ce.repo 2>/dev/null || \
|
|
yum-config-manager --add-repo https://download.docker.com/linux/centos/docker-ce.repo
|
|
dnf install -y -q docker-ce docker-ce-cli containerd.io docker-buildx-plugin docker-compose-plugin || \
|
|
yum install -y docker-ce docker-ce-cli containerd.io docker-buildx-plugin docker-compose-plugin
|
|
;;
|
|
|
|
arch|manjaro|endeavouros)
|
|
pacman -Sy --noconfirm docker docker-compose
|
|
;;
|
|
|
|
opensuse*|sles)
|
|
zypper install -y docker docker-compose
|
|
;;
|
|
|
|
*)
|
|
error "Unsupported OS: $OS"
|
|
;;
|
|
esac
|
|
|
|
systemctl enable --now docker
|
|
success "Docker installed"
|
|
}
|
|
|
|
# Generate secrets
|
|
generate_secrets() {
|
|
SECRET_KEY_BASE=$(openssl rand -hex 64)
|
|
OTP_SECRET=$(openssl rand -hex 64)
|
|
|
|
# Generate VAPID keys
|
|
VAPID_KEYS=$(docker run --rm tootsuite/mastodon:latest bundle exec rake mastodon:webpush:generate_vapid_key 2>/dev/null || echo "")
|
|
if [ -n "$VAPID_KEYS" ]; then
|
|
VAPID_PRIVATE_KEY=$(echo "$VAPID_KEYS" | grep VAPID_PRIVATE_KEY | cut -d= -f2)
|
|
VAPID_PUBLIC_KEY=$(echo "$VAPID_KEYS" | grep VAPID_PUBLIC_KEY | cut -d= -f2)
|
|
else
|
|
VAPID_PRIVATE_KEY=$(openssl rand -hex 32)
|
|
VAPID_PUBLIC_KEY=$(openssl rand -hex 32)
|
|
fi
|
|
|
|
POSTGRES_PASSWORD="REDACTED_PASSWORD" rand -hex 32)
|
|
REDIS_PASSWORD="REDACTED_PASSWORD" rand -hex 32)
|
|
}
|
|
|
|
# Get domain interactively
|
|
get_domain() {
|
|
if [ -z "$DOMAIN" ]; then
|
|
echo ""
|
|
echo "========================================"
|
|
echo " Domain Configuration"
|
|
echo "========================================"
|
|
echo ""
|
|
echo "Enter your domain for Mastodon (e.g., mastodon.example.com)"
|
|
echo "A domain is REQUIRED for Mastodon to work properly."
|
|
echo ""
|
|
read -p "Domain: " DOMAIN
|
|
if [ -z "$DOMAIN" ]; then
|
|
error "Domain is required for Mastodon"
|
|
fi
|
|
fi
|
|
|
|
if [ -z "$ADMIN_EMAIL" ]; then
|
|
read -p "Admin email: " ADMIN_EMAIL
|
|
if [ -z "$ADMIN_EMAIL" ]; then
|
|
warn "No email provided - SSL may not work"
|
|
ADMIN_EMAIL="admin@$DOMAIN"
|
|
fi
|
|
fi
|
|
}
|
|
|
|
# Create directories
|
|
create_directories() {
|
|
log "Creating directories..."
|
|
mkdir -p "$INSTALL_DIR"
|
|
mkdir -p "$DATA_DIR"/{postgres,redis,mastodon/{public/system,live}}
|
|
mkdir -p "$DATA_DIR"/caddy/{data,config}
|
|
chmod -R 755 "$DATA_DIR"
|
|
success "Directories created"
|
|
}
|
|
|
|
# Create .env file
|
|
create_env() {
|
|
log "Creating environment configuration..."
|
|
|
|
local protocol="https"
|
|
[ "$ENABLE_SSL" != true ] && protocol="http"
|
|
|
|
cat > "$INSTALL_DIR/.env.production" << EOF
|
|
# Federation
|
|
LOCAL_DOMAIN=$DOMAIN
|
|
SINGLE_USER_MODE=$SINGLE_USER_MODE
|
|
|
|
# Redis
|
|
REDIS_HOST=redis
|
|
REDIS_PORT=6379
|
|
REDIS_PASSWORD="REDACTED_PASSWORD"
|
|
|
|
# PostgreSQL
|
|
DB_HOST=db
|
|
DB_USER=mastodon
|
|
DB_NAME=mastodon
|
|
DB_PASS="REDACTED_PASSWORD"
|
|
DB_PORT=5432
|
|
|
|
# Secrets
|
|
SECRET_KEY_BASE=$SECRET_KEY_BASE
|
|
OTP_SECRET=$OTP_SECRET
|
|
VAPID_PRIVATE_KEY=$VAPID_PRIVATE_KEY
|
|
VAPID_PUBLIC_KEY=$VAPID_PUBLIC_KEY
|
|
|
|
# Web
|
|
WEB_DOMAIN=$DOMAIN
|
|
ALTERNATE_DOMAINS=
|
|
|
|
# Email (configure for production)
|
|
SMTP_SERVER=smtp.mailgun.org
|
|
SMTP_PORT=587
|
|
SMTP_LOGIN=
|
|
SMTP_PASSWORD=
|
|
"REDACTED_PASSWORD"
|
|
SMTP_AUTH_METHOD=plain
|
|
SMTP_OPENSSL_VERIFY_MODE=none
|
|
SMTP_ENABLE_STARTTLS=auto
|
|
|
|
# File storage
|
|
# For S3 storage, uncomment and configure:
|
|
# S3_ENABLED=true
|
|
# S3_BUCKET=your-bucket
|
|
# AWS_ACCESS_KEY_ID=
|
|
# AWS_SECRET_ACCESS_KEY=
|
|
# S3_REGION=us-east-1
|
|
# S3_PROTOCOL=https
|
|
# S3_HOSTNAME=s3.amazonaws.com
|
|
|
|
# Elasticsearch (optional, for full-text search)
|
|
# ES_ENABLED=true
|
|
# ES_HOST=elasticsearch
|
|
# ES_PORT=9200
|
|
|
|
# Performance
|
|
RAILS_ENV=production
|
|
NODE_ENV=production
|
|
RAILS_LOG_LEVEL=warn
|
|
TRUSTED_PROXY_IP=172.16.0.0/12
|
|
|
|
# IP and session
|
|
IP_RETENTION_PERIOD=31556952
|
|
SESSION_RETENTION_PERIOD=31556952
|
|
EOF
|
|
|
|
chmod 600 "$INSTALL_DIR/.env.production"
|
|
success "Environment configuration created"
|
|
}
|
|
|
|
# Create docker-compose.yml
|
|
create_compose() {
|
|
log "Creating Docker Compose file..."
|
|
|
|
cat > "$INSTALL_DIR/docker-compose.yml" << 'EOF'
|
|
services:
|
|
db:
|
|
image: postgres:16-alpine
|
|
container_name: mastodon-db
|
|
shm_size: 256mb
|
|
environment:
|
|
POSTGRES_USER: mastodon
|
|
POSTGRES_PASSWORD: "REDACTED_PASSWORD"
|
|
POSTGRES_DB: mastodon
|
|
volumes:
|
|
- ./data/postgres:/var/lib/postgresql/data
|
|
restart: unless-stopped
|
|
healthcheck:
|
|
test: ["CMD", "pg_isready", "-U", "mastodon"]
|
|
interval: 10s
|
|
timeout: 5s
|
|
retries: 5
|
|
networks:
|
|
- internal
|
|
|
|
redis:
|
|
image: redis:7-alpine
|
|
container_name: mastodon-redis
|
|
command: redis-server --requirepass REDACTED_PASSWORD
|
|
volumes:
|
|
- ./data/redis:/data
|
|
restart: unless-stopped
|
|
healthcheck:
|
|
test: ["CMD", "redis-cli", "-a", "${REDIS_PASSWORD:"REDACTED_PASSWORD" "ping"]
|
|
interval: 10s
|
|
timeout: 5s
|
|
retries: 5
|
|
networks:
|
|
- internal
|
|
|
|
web:
|
|
image: tootsuite/mastodon:latest
|
|
container_name: mastodon-web
|
|
env_file: .env.production
|
|
command: bash -c "rm -f /mastodon/tmp/pids/server.pid; bundle exec rails s -p 3000"
|
|
volumes:
|
|
- ./data/mastodon/public/system:/mastodon/public/system
|
|
depends_on:
|
|
db:
|
|
condition: service_healthy
|
|
redis:
|
|
condition: service_healthy
|
|
restart: unless-stopped
|
|
healthcheck:
|
|
test: ["CMD-SHELL", "wget -q --spider --proxy=off localhost:3000/health || exit 1"]
|
|
interval: 30s
|
|
timeout: 10s
|
|
retries: 3
|
|
networks:
|
|
- internal
|
|
- external
|
|
|
|
streaming:
|
|
image: tootsuite/mastodon:latest
|
|
container_name: mastodon-streaming
|
|
env_file: .env.production
|
|
command: node ./streaming
|
|
depends_on:
|
|
db:
|
|
condition: service_healthy
|
|
redis:
|
|
condition: service_healthy
|
|
restart: unless-stopped
|
|
healthcheck:
|
|
test: ["CMD-SHELL", "wget -q --spider --proxy=off localhost:4000/api/v1/streaming/health || exit 1"]
|
|
interval: 30s
|
|
timeout: 10s
|
|
retries: 3
|
|
networks:
|
|
- internal
|
|
- external
|
|
|
|
sidekiq:
|
|
image: tootsuite/mastodon:latest
|
|
container_name: mastodon-sidekiq
|
|
env_file: .env.production
|
|
command: bundle exec sidekiq
|
|
volumes:
|
|
- ./data/mastodon/public/system:/mastodon/public/system
|
|
depends_on:
|
|
db:
|
|
condition: service_healthy
|
|
redis:
|
|
condition: service_healthy
|
|
restart: unless-stopped
|
|
healthcheck:
|
|
test: ["CMD-SHELL", "ps aux | grep '[s]idekiq 6' || exit 1"]
|
|
interval: 30s
|
|
timeout: 10s
|
|
retries: 3
|
|
networks:
|
|
- internal
|
|
- external
|
|
|
|
caddy:
|
|
image: caddy:2-alpine
|
|
container_name: mastodon-caddy
|
|
ports:
|
|
- "80:80"
|
|
- "443:443"
|
|
volumes:
|
|
- ./Caddyfile:/etc/caddy/Caddyfile:ro
|
|
- ./data/caddy/data:/data
|
|
- ./data/caddy/config:/config
|
|
- ./data/mastodon/public:/mastodon/public:ro
|
|
depends_on:
|
|
- web
|
|
- streaming
|
|
restart: unless-stopped
|
|
networks:
|
|
- external
|
|
|
|
watchtower:
|
|
image: containrrr/watchtower:latest
|
|
container_name: mastodon-watchtower
|
|
environment:
|
|
WATCHTOWER_CLEANUP: "true"
|
|
WATCHTOWER_SCHEDULE: "0 0 4 * * *"
|
|
WATCHTOWER_LABEL_ENABLE: "false"
|
|
volumes:
|
|
- /var/run/docker.sock:/var/run/docker.sock
|
|
restart: unless-stopped
|
|
|
|
networks:
|
|
internal:
|
|
internal: true
|
|
external:
|
|
EOF
|
|
|
|
# Extract DB_PASS for compose
|
|
echo "DB_PASS="REDACTED_PASSWORD" > "$INSTALL_DIR/.env"
|
|
echo "REDIS_PASSWORD="REDACTED_PASSWORD" >> "$INSTALL_DIR/.env"
|
|
|
|
success "Docker Compose file created"
|
|
}
|
|
|
|
# Create Caddyfile
|
|
create_caddyfile() {
|
|
log "Creating Caddy configuration..."
|
|
|
|
if [ "$ENABLE_SSL" = true ]; then
|
|
cat > "$INSTALL_DIR/Caddyfile" << EOF
|
|
$DOMAIN {
|
|
encode gzip
|
|
|
|
handle_path /system/* {
|
|
file_server {
|
|
root /mastodon/public
|
|
}
|
|
}
|
|
|
|
handle /api/v1/streaming/* {
|
|
reverse_proxy streaming:4000
|
|
}
|
|
|
|
handle /* {
|
|
reverse_proxy web:3000
|
|
}
|
|
|
|
header {
|
|
Strict-Transport-Security "max-age=31536000; includeSubDomains; preload"
|
|
X-Frame-Options "SAMEORIGIN"
|
|
X-Content-Type-Options "nosniff"
|
|
X-XSS-Protection "1; mode=block"
|
|
Referrer-Policy "strict-origin-when-cross-origin"
|
|
}
|
|
|
|
log {
|
|
output stdout
|
|
}
|
|
}
|
|
EOF
|
|
else
|
|
cat > "$INSTALL_DIR/Caddyfile" << EOF
|
|
:80 {
|
|
encode gzip
|
|
|
|
handle_path /system/* {
|
|
file_server {
|
|
root /mastodon/public
|
|
}
|
|
}
|
|
|
|
handle /api/v1/streaming/* {
|
|
reverse_proxy streaming:4000
|
|
}
|
|
|
|
handle /* {
|
|
reverse_proxy web:3000
|
|
}
|
|
}
|
|
EOF
|
|
fi
|
|
|
|
success "Caddy configuration created"
|
|
}
|
|
|
|
# Initialize database
|
|
init_database() {
|
|
log "Initializing database..."
|
|
cd "$INSTALL_DIR"
|
|
|
|
# Start database first
|
|
docker compose up -d db redis
|
|
sleep 10
|
|
|
|
# Run migrations
|
|
docker compose run --rm web bundle exec rails db:setup SAFETY_ASSURED=1 2>/dev/null || \
|
|
docker compose run --rm web bundle exec rails db:migrate SAFETY_ASSURED=1
|
|
|
|
# Precompile assets
|
|
docker compose run --rm web bundle exec rails assets:precompile
|
|
|
|
success "Database initialized"
|
|
}
|
|
|
|
# Create management script
|
|
create_management_script() {
|
|
log "Creating management script..."
|
|
|
|
cat > /usr/local/bin/mastodon << 'EOF'
|
|
#!/bin/bash
|
|
cd /opt/mastodon || exit 1
|
|
|
|
case "${1:-help}" in
|
|
start) docker compose up -d ;;
|
|
stop) docker compose down ;;
|
|
restart) docker compose restart ${2:-} ;;
|
|
status) docker compose ps ;;
|
|
logs) docker compose logs -f ${2:-} ;;
|
|
update)
|
|
docker compose pull
|
|
docker compose up -d
|
|
docker compose run --rm web bundle exec rails db:migrate
|
|
docker compose run --rm web bundle exec rails assets:precompile
|
|
docker compose restart
|
|
;;
|
|
edit) ${EDITOR:-nano} /opt/mastodon/.env.production ;;
|
|
|
|
admin)
|
|
if [ -z "$2" ]; then
|
|
echo "Usage: mastodon admin <username>"
|
|
exit 1
|
|
fi
|
|
docker compose run --rm web bin/tootctl accounts create "$2" --email "${3:-admin@localhost}" --confirmed --role Owner
|
|
;;
|
|
|
|
reset-password)
|
|
if [ -z "$2" ]; then
|
|
echo "Usage: mastodon reset-password <username>"
|
|
exit 1
|
|
fi
|
|
docker compose run --rm web bin/tootctl accounts modify "$2" --reset-password
|
|
;;
|
|
|
|
tootctl)
|
|
shift
|
|
docker compose run --rm web bin/tootctl "$@"
|
|
;;
|
|
|
|
console)
|
|
docker compose run --rm web bin/rails console
|
|
;;
|
|
|
|
shell)
|
|
docker compose run --rm web /bin/bash
|
|
;;
|
|
|
|
backup)
|
|
timestamp=$(date +"%Y%m%d_%H%M%S")
|
|
backup_dir="/opt/mastodon-data/backups"
|
|
mkdir -p "$backup_dir"
|
|
|
|
echo "Backing up database..."
|
|
docker compose exec -T db pg_dump -U mastodon mastodon > "$backup_dir/mastodon_db_$timestamp.sql"
|
|
|
|
echo "Backing up media..."
|
|
tar -czf "$backup_dir/mastodon_media_$timestamp.tar.gz" -C /opt/mastodon-data mastodon/public/system
|
|
|
|
echo "Backup complete: $backup_dir"
|
|
ls -la "$backup_dir"/*$timestamp*
|
|
;;
|
|
|
|
cleanup)
|
|
echo "Cleaning up old media..."
|
|
docker compose run --rm web bin/tootctl media remove --days=7
|
|
docker compose run --rm web bin/tootctl preview_cards remove --days=30
|
|
docker compose run --rm web bin/tootctl statuses remove --days=90
|
|
;;
|
|
|
|
*)
|
|
echo "Mastodon Management"
|
|
echo ""
|
|
echo "Usage: mastodon <command>"
|
|
echo ""
|
|
echo "Commands:"
|
|
echo " start Start all services"
|
|
echo " stop Stop all services"
|
|
echo " restart [service] Restart services"
|
|
echo " status Show status"
|
|
echo " logs [service] View logs"
|
|
echo " update Update and migrate"
|
|
echo " edit Edit configuration"
|
|
echo " admin <user> Create admin user"
|
|
echo " reset-password <u> Reset user password"
|
|
echo " tootctl <args> Run tootctl command"
|
|
echo " console Rails console"
|
|
echo " shell Bash shell"
|
|
echo " backup Backup database and media"
|
|
echo " cleanup Clean old media/statuses"
|
|
;;
|
|
esac
|
|
EOF
|
|
|
|
chmod +x /usr/local/bin/mastodon
|
|
success "Management script created"
|
|
}
|
|
|
|
# Configure firewall
|
|
configure_firewall() {
|
|
log "Configuring firewall..."
|
|
|
|
if command -v firewall-cmd >/dev/null 2>&1 && systemctl is-active --quiet firewalld 2>/dev/null; then
|
|
firewall-cmd --permanent --add-service=http 2>/dev/null || true
|
|
firewall-cmd --permanent --add-service=https 2>/dev/null || true
|
|
firewall-cmd --reload 2>/dev/null || true
|
|
success "Firewall configured (firewalld)"
|
|
elif command -v ufw >/dev/null 2>&1 && ufw status | grep -q "active"; then
|
|
ufw allow 80/tcp 2>/dev/null || true
|
|
ufw allow 443/tcp 2>/dev/null || true
|
|
success "Firewall configured (ufw)"
|
|
else
|
|
warn "No active firewall detected"
|
|
fi
|
|
}
|
|
|
|
# Deploy
|
|
deploy() {
|
|
log "Deploying Mastodon..."
|
|
cd "$INSTALL_DIR"
|
|
|
|
# Copy data directory reference
|
|
ln -sf "$DATA_DIR" "$INSTALL_DIR/data" 2>/dev/null || true
|
|
mkdir -p "$INSTALL_DIR/data"
|
|
ln -sf "$DATA_DIR/postgres" "$INSTALL_DIR/data/postgres"
|
|
ln -sf "$DATA_DIR/redis" "$INSTALL_DIR/data/redis"
|
|
ln -sf "$DATA_DIR/mastodon" "$INSTALL_DIR/data/mastodon"
|
|
ln -sf "$DATA_DIR/caddy" "$INSTALL_DIR/data/caddy"
|
|
|
|
docker compose pull
|
|
|
|
# Initialize database
|
|
init_database
|
|
|
|
# Start all services
|
|
docker compose up -d
|
|
|
|
# Wait for services
|
|
log "Waiting for services to start..."
|
|
sleep 15
|
|
|
|
success "Mastodon deployed!"
|
|
}
|
|
|
|
# Show completion message
|
|
show_complete() {
|
|
local protocol="https"
|
|
[ "$ENABLE_SSL" != true ] && protocol="http"
|
|
|
|
echo ""
|
|
echo "========================================"
|
|
echo " Mastodon Installation Complete!"
|
|
echo "========================================"
|
|
echo ""
|
|
echo "Access:"
|
|
echo " Web Interface: ${protocol}://${DOMAIN}"
|
|
echo ""
|
|
echo "Create your admin account:"
|
|
echo " mastodon admin yourusername your@email.com"
|
|
echo ""
|
|
echo "Then reset password to get initial password:"
|
|
echo " mastodon reset-password yourusername"
|
|
echo ""
|
|
echo "Commands:"
|
|
echo " mastodon status - Show service status"
|
|
echo " mastodon logs - View logs"
|
|
echo " mastodon update - Update Mastodon"
|
|
echo " mastodon backup - Backup database"
|
|
echo " mastodon cleanup - Clean old media"
|
|
echo " mastodon tootctl - Run tootctl commands"
|
|
echo ""
|
|
echo "Config: $INSTALL_DIR/.env.production"
|
|
echo "Data: $DATA_DIR"
|
|
echo ""
|
|
echo "⚠️ Configure email in .env.production for:"
|
|
echo " - Email notifications"
|
|
echo " - Password resets"
|
|
echo " - Account confirmations"
|
|
echo ""
|
|
}
|
|
|
|
# Main
|
|
main() {
|
|
echo ""
|
|
echo "========================================"
|
|
echo " Mastodon Production Installer"
|
|
echo "========================================"
|
|
echo ""
|
|
|
|
detect_os
|
|
get_domain
|
|
generate_secrets
|
|
install_docker
|
|
create_directories
|
|
create_env
|
|
create_compose
|
|
create_caddyfile
|
|
create_management_script
|
|
configure_firewall
|
|
deploy
|
|
show_complete
|
|
}
|
|
|
|
main "$@"
|