Sanitized mirror from private repository - 2026-04-20 01:32:01 UTC
This commit is contained in:
723
docs/services/mastodon/install.sh
Normal file
723
docs/services/mastodon/install.sh
Normal file
@@ -0,0 +1,723 @@
|
||||
#!/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 "$@"
|
||||
Reference in New Issue
Block a user