Mastodon is a free, open-source federated social network server that provides a decentralized alternative to proprietary platforms like Twitter/X. Built on Ruby on Rails and using ActivityPub protocol, Mastodon allows users to run their own social media instance while connecting to the broader fediverse.
FOSS Alternatives
Mastodon is part of the broader fediverse ecosystem. Consider these FOSS alternatives for decentralized social networking:
1. Prerequisites
Hardware Requirements
Software Dependencies
Network Requirements
System Access
Supported Operating Systems
This guide supports installation on:
2. Installation
RHEL/CentOS/Rocky Linux/AlmaLinux
# Install EPEL and PowerTools repositories
sudo dnf install -y epel-release
sudo dnf config-manager --set-enabled powertools # CentOS Stream 8
sudo dnf config-manager --set-enabled crb # CentOS Stream 9, Rocky, AlmaLinux
# Install Node.js repository
curl -fsSL https://rpm.nodesource.com/setup_lts.x | sudo bash -
# Install Yarn repository
curl -sL https://dl.yarnpkg.com/rpm/yarn.repo | sudo tee /etc/yum.repos.d/yarn.repo
# Install PostgreSQL repository (for latest version)
sudo dnf install -y https://download.postgresql.org/pub/repos/yum/reporpms/EL-$(rpm -E %{rhel})-x86_64/pgdg-redhat-repo-latest.noarch.rpm
# Install required packages
sudo dnf install -y \
git curl wget gnupg2 \
gcc gcc-c++ make automake autoconf libtool \
postgresql15-server postgresql15-devel \
redis \
nodejs yarn \
nginx \
ImageMagick ImageMagick-devel \
ffmpeg \
libxml2-devel libxslt-devel \
libidn-devel \
openssl-devel \
readline-devel \
zlib-devel \
libyaml-devel \
gdbm-devel \
ncurses-devel \
libffi-devel
# Initialize PostgreSQL
sudo postgresql-15-setup initdb
sudo systemctl enable --now postgresql-15 redis
# Configure PostgreSQL
sudo -u postgres createuser mastodon --createdb
sudo -u postgres psql -c "ALTER USER mastodon WITH ENCRYPTED PASSWORD 'secure_random_password_here';"
# Create mastodon user
sudo useradd -m -s /bin/bash mastodon
sudo usermod -a -G mastodon nginx
# Install Ruby (using rbenv for version management)
sudo -u mastodon bash << 'EOF'
cd /home/mastodon
git clone https://github.com/rbenv/rbenv.git ~/.rbenv
echo 'export PATH="$HOME/.rbenv/bin:$PATH"' >> ~/.bashrc
echo 'eval "$(rbenv init -)"' >> ~/.bashrc
git clone https://github.com/rbenv/ruby-build.git ~/.rbenv/plugins/ruby-build
export PATH="$HOME/.rbenv/bin:$PATH"
eval "$(rbenv init -)"
rbenv install 3.2.2
rbenv global 3.2.2
gem install bundler
EOF
# Clone Mastodon
sudo -u mastodon git clone https://github.com/mastodon/mastodon.git /home/mastodon/live
cd /home/mastodon/live
sudo -u mastodon git checkout $(git tag -l | grep -v 'rc' | sort -V | tail -n 1)
# Install Ruby dependencies
sudo -u mastodon bash << 'EOF'
cd /home/mastodon/live
export PATH="$HOME/.rbenv/bin:$PATH"
eval "$(rbenv init -)"
bundle config deployment 'true'
bundle config without 'development test'
bundle install
EOF
# Install Node.js dependencies
sudo -u mastodon bash << 'EOF'
cd /home/mastodon/live
yarn install --pure-lockfile
EOF
Debian/Ubuntu
# Update package list
sudo apt update
# Install curl and gnupg (if not already installed)
sudo apt install -y curl gnupg
# Add Node.js repository
curl -fsSL https://deb.nodesource.com/setup_lts.x | sudo -E bash -
# Add Yarn repository
curl -sS https://dl.yarnpkg.com/debian/pubkey.gpg | sudo apt-key add -
echo "deb https://dl.yarnpkg.com/debian/ stable main" | sudo tee /etc/apt/sources.list.d/yarn.list
# Add PostgreSQL repository (for latest version)
wget --quiet -O - https://www.postgresql.org/media/keys/ACCC4CF8.asc | sudo apt-key add -
echo "deb http://apt.postgresql.org/pub/repos/apt/ $(lsb_release -cs)-pgdg main" | sudo tee /etc/apt/sources.list.d/pgdg.list
# Update package list
sudo apt update
# Install required packages
sudo apt install -y \
git curl wget gnupg \
build-essential \
postgresql-15 postgresql-contrib-15 postgresql-server-dev-15 \
redis-server \
nodejs yarn \
nginx \
imagemagick ffmpeg \
libxml2-dev libxslt1-dev \
libidn11-dev \
libssl-dev \
libreadline-dev \
zlib1g-dev \
libyaml-dev \
libgdbm-dev \
libncurses5-dev \
libffi-dev \
libpq-dev
# Start and enable services
sudo systemctl enable --now postgresql redis-server
# Configure PostgreSQL
sudo -u postgres createuser mastodon --createdb
sudo -u postgres psql -c "ALTER USER mastodon WITH ENCRYPTED PASSWORD 'secure_random_password_here';"
# Create mastodon user
sudo useradd -m -s /bin/bash mastodon
sudo usermod -a -G mastodon www-data
# Install Ruby (using rbenv)
sudo -u mastodon bash << 'EOF'
cd /home/mastodon
git clone https://github.com/rbenv/rbenv.git ~/.rbenv
echo 'export PATH="$HOME/.rbenv/bin:$PATH"' >> ~/.bashrc
echo 'eval "$(rbenv init -)"' >> ~/.bashrc
git clone https://github.com/rbenv/ruby-build.git ~/.rbenv/plugins/ruby-build
export PATH="$HOME/.rbenv/bin:$PATH"
eval "$(rbenv init -)"
rbenv install 3.2.2
rbenv global 3.2.2
gem install bundler
EOF
# Clone Mastodon
sudo -u mastodon git clone https://github.com/mastodon/mastodon.git /home/mastodon/live
cd /home/mastodon/live
sudo -u mastodon git checkout $(git tag -l | grep -v 'rc' | sort -V | tail -n 1)
# Install dependencies
sudo -u mastodon bash << 'EOF'
cd /home/mastodon/live
export PATH="$HOME/.rbenv/bin:$PATH"
eval "$(rbenv init -)"
bundle config deployment 'true'
bundle config without 'development test'
bundle install
yarn install --pure-lockfile
EOF
Arch Linux
# Update system
sudo pacman -Syu
# Install required packages
sudo pacman -S --needed \
git curl wget gnupg \
base-devel \
postgresql redis \
nodejs npm yarn \
nginx \
imagemagick ffmpeg \
libxml2 libxslt \
libidn \
openssl \
readline \
zlib \
libyaml \
gdbm \
ncurses \
libffi
# Initialize PostgreSQL
sudo -u postgres initdb -D /var/lib/postgres/data
sudo systemctl enable --now postgresql redis
# Configure PostgreSQL
sudo -u postgres createuser mastodon --createdb
sudo -u postgres psql -c "ALTER USER mastodon WITH ENCRYPTED PASSWORD 'secure_random_password_here';"
# Create mastodon user
sudo useradd -m -s /bin/bash mastodon
sudo usermod -a -G mastodon http
# Install Ruby using rbenv
sudo -u mastodon bash << 'EOF'
cd /home/mastodon
git clone https://github.com/rbenv/rbenv.git ~/.rbenv
echo 'export PATH="$HOME/.rbenv/bin:$PATH"' >> ~/.bashrc
echo 'eval "$(rbenv init -)"' >> ~/.bashrc
git clone https://github.com/rbenv/ruby-build.git ~/.rbenv/plugins/ruby-build
export PATH="$HOME/.rbenv/bin:$PATH"
eval "$(rbenv init -)"
rbenv install 3.2.2
rbenv global 3.2.2
gem install bundler
EOF
# Clone and setup Mastodon
sudo -u mastodon git clone https://github.com/mastodon/mastodon.git /home/mastodon/live
cd /home/mastodon/live
sudo -u mastodon git checkout $(git tag -l | grep -v 'rc' | sort -V | tail -n 1)
# Install dependencies
sudo -u mastodon bash << 'EOF'
cd /home/mastodon/live
export PATH="$HOME/.rbenv/bin:$PATH"
eval "$(rbenv init -)"
bundle config deployment 'true'
bundle config without 'development test'
bundle install
yarn install --pure-lockfile
EOF
Alpine Linux
# Update package index
apk update
# Install required packages
apk add --no-cache \
git curl wget gnupg \
build-base \
postgresql15 postgresql15-dev postgresql15-contrib \
redis \
nodejs npm yarn \
nginx \
imagemagick imagemagick-dev ffmpeg \
libxml2-dev libxslt-dev \
libidn-dev \
openssl-dev \
readline-dev \
zlib-dev \
yaml-dev \
gdbm-dev \
ncurses-dev \
libffi-dev
# Initialize and start services
rc-service postgresql setup
rc-service postgresql start
rc-service redis start
rc-update add postgresql
rc-update add redis
# Configure PostgreSQL
sudo -u postgres createuser mastodon --createdb
sudo -u postgres psql -c "ALTER USER mastodon WITH ENCRYPTED PASSWORD 'secure_random_password_here';"
# Create mastodon user
adduser -D -s /bin/ash mastodon
addgroup mastodon nginx
# Install Ruby using rbenv
sudo -u mastodon ash << 'EOF'
cd /home/mastodon
git clone https://github.com/rbenv/rbenv.git ~/.rbenv
echo 'export PATH="$HOME/.rbenv/bin:$PATH"' >> ~/.profile
echo 'eval "$(rbenv init -)"' >> ~/.profile
git clone https://github.com/rbenv/ruby-build.git ~/.rbenv/plugins/ruby-build
export PATH="$HOME/.rbenv/bin:$PATH"
eval "$(rbenv init -)"
rbenv install 3.2.2
rbenv global 3.2.2
gem install bundler
EOF
# Clone and setup Mastodon
sudo -u mastodon git clone https://github.com/mastodon/mastodon.git /home/mastodon/live
cd /home/mastodon/live
sudo -u mastodon git checkout $(git tag -l | grep -v 'rc' | sort -V | tail -n 1)
# Install dependencies
sudo -u mastodon ash << 'EOF'
cd /home/mastodon/live
export PATH="$HOME/.rbenv/bin:$PATH"
eval "$(rbenv init -)"
bundle config deployment 'true'
bundle config without 'development test'
bundle install
yarn install --pure-lockfile
EOF
3. Configuration
Production Environment Configuration
#### Complete Production Configuration
# Generate secrets first
sudo -u mastodon bash << 'EOF'
cd /home/mastodon/live
export PATH="$HOME/.rbenv/bin:$PATH"
eval "$(rbenv init -)"
# Generate secrets
SECRET_KEY_BASE=$(bundle exec rake secret)
OTP_SECRET=$(bundle exec rake secret)
# Generate VAPID keys
VAPID_KEYS=$(bundle exec rake mastodon:webpush:generate_vapid_key)
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)
# Save to temp file for reference
cat > /tmp/mastodon_secrets.txt << SECRETS
SECRET_KEY_BASE=$SECRET_KEY_BASE
OTP_SECRET=$OTP_SECRET
VAPID_PRIVATE_KEY=$VAPID_PRIVATE_KEY
VAPID_PUBLIC_KEY=$VAPID_PUBLIC_KEY
SECRETS
echo "Secrets generated and saved to /tmp/mastodon_secrets.txt"
EOF
# Create comprehensive production configuration
sudo -u mastodon tee /home/mastodon/live/.env.production << 'EOF'
# Mastodon Production Configuration
# Generated: $(date)
# ==============================================================================
# FEDERATION SETTINGS
# ==============================================================================
# Primary domain
LOCAL_DOMAIN=mastodon.example.com
# Web domain (if different from LOCAL_DOMAIN for CDN/proxy)
WEB_DOMAIN=mastodon.example.com
# Alternative domains (comma-separated)
# ALTERNATE_DOMAINS=alt1.example.com,alt2.example.com
# Use HTTPS (highly recommended)
LOCAL_HTTPS=true
# ==============================================================================
# DATABASE CONFIGURATION
# ==============================================================================
# PostgreSQL
DB_HOST=127.0.0.1
DB_USER=mastodon
DB_NAME=mastodon_production
DB_PASS=Mast0d0n_DB_P@ss_2024!
DB_PORT=5432
# Database pool settings
DB_POOL=25
PREPARED_STATEMENTS=false
# ==============================================================================
# REDIS CONFIGURATION
# ==============================================================================
# Redis for cache
REDIS_HOST=127.0.0.1
REDIS_PORT=6379
REDIS_NAMESPACE=mastodon
# Separate Redis databases for different functions
CACHE_REDIS_HOST=127.0.0.1
CACHE_REDIS_PORT=6379
CACHE_REDIS_DB=0
CACHE_REDIS_NAMESPACE=mastodon:cache
SIDEKIQ_REDIS_HOST=127.0.0.1
SIDEKIQ_REDIS_PORT=6379
SIDEKIQ_REDIS_DB=1
SIDEKIQ_REDIS_NAMESPACE=mastodon:sidekiq
# Redis password (if configured)
# REDIS_PASSWORD=redis_password_here
# ==============================================================================
# ELASTICSEARCH (Full-text search)
# ==============================================================================
ES_ENABLED=true
ES_HOST=localhost
ES_PORT=9200
ES_USER=elastic
ES_PASS=Elast1c_P@ss_2024!
ES_PREFIX=mastodon
# ==============================================================================
# SECRET KEYS (Replace with generated values)
# ==============================================================================
# Generate with: bundle exec rake secret
SECRET_KEY_BASE=<INSERT_SECRET_KEY_BASE_HERE>
OTP_SECRET=<INSERT_OTP_SECRET_HERE>
# Generate with: bundle exec rake mastodon:webpush:generate_vapid_key
VAPID_PRIVATE_KEY=<INSERT_VAPID_PRIVATE_KEY_HERE>
VAPID_PUBLIC_KEY=<INSERT_VAPID_PUBLIC_KEY_HERE>
# ==============================================================================
# EMAIL CONFIGURATION
# ==============================================================================
SMTP_SERVER=smtp.mailgun.org
SMTP_PORT=587
SMTP_LOGIN=postmaster@mg.example.com
SMTP_PASSWORD=SMTP_P@ssw0rd_2024!
SMTP_FROM_ADDRESS=Mastodon <notifications@mastodon.example.com>
SMTP_DOMAIN=mastodon.example.com
SMTP_DELIVERY_METHOD=smtp
SMTP_AUTH_METHOD=plain
SMTP_CA_FILE=/etc/ssl/certs/ca-certificates.crt
SMTP_OPENSSL_VERIFY_MODE=peer
SMTP_ENABLE_STARTTLS=auto
SMTP_TLS=false
# ==============================================================================
# FILE STORAGE
# ==============================================================================
# Local storage
PAPERCLIP_ROOT_PATH=/home/mastodon/live/public/system
PAPERCLIP_ROOT_URL=/system
# S3-compatible storage (uncomment to enable)
# S3_ENABLED=true
# S3_BUCKET=mastodon-media
# AWS_ACCESS_KEY_ID=your_access_key
# AWS_SECRET_ACCESS_KEY=your_secret_key
# S3_REGION=us-east-1
# S3_PROTOCOL=https
# S3_HOSTNAME=s3.amazonaws.com
# S3_ENDPOINT=https://s3.amazonaws.com
# S3_SIGNATURE_VERSION=v4
# S3_MULTIPART_THRESHOLD=15728640
# Cloudflare R2 (S3-compatible)
# S3_ENABLED=true
# S3_BUCKET=mastodon
# AWS_ACCESS_KEY_ID=your_r2_access_key
# AWS_SECRET_ACCESS_KEY=your_r2_secret_key
# S3_ENDPOINT=https://your-account-id.r2.cloudflarestorage.com
# S3_REGION=auto
# S3_HOSTNAME=your-bucket.your-account-id.r2.cloudflarestorage.com
# S3_PROTOCOL=https
# ==============================================================================
# CDN CONFIGURATION
# ==============================================================================
# CDN for assets
# CDN_HOST=https://cdn.example.com
# CDN for media files
# S3_ALIAS_HOST=media.example.com
# ==============================================================================
# STREAMING API
# ==============================================================================
STREAMING_API_BASE_URL=wss://mastodon.example.com
STREAMING_CLUSTER_NUM=1
# Node.js streaming
NODE_ENV=production
PORT=4000
BIND=127.0.0.1
# ==============================================================================
# SECURITY SETTINGS
# ==============================================================================
# Authorized fetch (require authentication for ActivityPub)
AUTHORIZED_FETCH=false
# Limited federation (allowlist mode)
LIMITED_FEDERATION_MODE=false
# IP retention period (days)
IP_RETENTION_PERIOD=365
# Session retention period (days)
SESSION_RETENTION_PERIOD=365
# Content retention (days, 0 = disabled)
CONTENT_CACHE_RETENTION_PERIOD=7
BACKUPS_RETENTION_PERIOD=7
# Security headers
FORCE_SSL=true
SEND_HSTS_PRELOAD=true
# CSRF protection
CSRF_PROTECTION=true
# ==============================================================================
# FEATURE FLAGS
# ==============================================================================
# Registration
SINGLE_USER_MODE=false
REGISTRATIONS_MODE=open # open, approved, closed
REQUIRE_INVITE_TEXT=false
# Captcha (hCaptcha)
# HCAPTCHA_SITE_KEY=your_site_key
# HCAPTCHA_SECRET_KEY=your_secret_key
# Trends
TRENDS=true
TRENDS_AS_LANDING_PAGE=true
# Translation
DEEPL_API_KEY=
DEEPL_PLAN=free # free or pro
# ==============================================================================
# LIMITS AND QUOTAS
# ==============================================================================
# Character limits
MAX_TOOT_CHARS=500
MAX_DISPLAY_NAME_CHARS=30
MAX_BIO_CHARS=500
MAX_PROFILE_FIELDS=4
# Poll limits
MAX_POLL_OPTIONS=4
MAX_POLL_OPTION_CHARS=100
# Media limits
MAX_IMAGE_SIZE=10485760 # 10MB
MAX_VIDEO_SIZE=41943040 # 40MB
MAX_EMOJI_SIZE=256000 # 250KB
# API rate limits
DEFAULT_THROTTLE_MULTIPLIER=1
API_RATE_LIMIT_MULTIPLIER=1
# ==============================================================================
# ADMIN SETTINGS
# ==============================================================================
# Admin email for reports
ADMIN_EMAIL=admin@example.com
# Instance information
INSTANCE_TITLE="Example Mastodon Instance"
INSTANCE_SHORT_DESCRIPTION="A federated social network instance"
INSTANCE_DESCRIPTION="Welcome to our Mastodon instance!"
INSTANCE_EXTENDED_DESCRIPTION="This is a longer description of the instance."
INSTANCE_CONTACT_EMAIL=contact@example.com
INSTANCE_CONTACT_USERNAME=admin
# Terms and Privacy Policy URLs
# TERMS_URL=https://example.com/terms
# PRIVACY_POLICY_URL=https://example.com/privacy
# ==============================================================================
# MONITORING AND LOGGING
# ==============================================================================
# Log level
LOG_LEVEL=info
# Lograge for structured logging
ENABLE_LOGRAGE=true
# StatsD metrics
# STATSD_ADDR=localhost:8125
# STATSD_NAMESPACE=mastodon
# OpenTelemetry
# OPENTELEMETRY_ENABLED=true
# OPENTELEMETRY_ENDPOINT=http://localhost:4318
# Sentry error tracking
# SENTRY_DSN=https://your-key@sentry.io/project-id
# SENTRY_ENVIRONMENT=production
# ==============================================================================
# DEVELOPMENT/DEBUG (Disable in production)
# ==============================================================================
RAILS_ENV=production
RAILS_SERVE_STATIC_FILES=false
RAILS_LOG_TO_STDOUT=false
TRUSTED_PROXY_IP=127.0.0.1
# ==============================================================================
# SCHEDULED MAINTENANCE
# ==============================================================================
# Maintenance mode message
# MAINTENANCE_MODE=true
# MAINTENANCE_MESSAGE="We are currently performing maintenance."
# ==============================================================================
# LDAP AUTHENTICATION (Optional)
# ==============================================================================
# LDAP_ENABLED=true
# LDAP_HOST=ldap.example.com
# LDAP_PORT=636
# LDAP_METHOD=simple_tls
# LDAP_BASE=dc=example,dc=com
# LDAP_BIND_DN=cn=admin,dc=example,dc=com
# LDAP_PASSWORD=ldap_password
# LDAP_UID=uid
# LDAP_SEARCH_FILTER=(|(uid=%u)(mail=%u))
# ==============================================================================
# SAML AUTHENTICATION (Optional)
# ==============================================================================
# SAML_ENABLED=true
# SAML_ACS_URL=https://mastodon.example.com/auth/saml/callback
# SAML_ISSUER=mastodon
# SAML_IDP_SSO_TARGET_URL=https://idp.example.com/sso
# SAML_IDP_CERT=base64_encoded_cert
# SAML_IDP_CERT_FINGERPRINT=fingerprint
# ==============================================================================
# OIDC AUTHENTICATION (Optional)
# ==============================================================================
# OIDC_ENABLED=true
# OIDC_DISPLAY_NAME=SSO
# OIDC_ISSUER=https://auth.example.com
# OIDC_DISCOVERY=true
# OIDC_CLIENT_ID=mastodon
# OIDC_CLIENT_SECRET=client_secret
# OIDC_REDIRECT_URI=https://mastodon.example.com/auth/openid_connect/callback
# OIDC_SCOPES=openid profile email
# ==============================================================================
# CAS AUTHENTICATION (Optional)
# ==============================================================================
# CAS_ENABLED=true
# CAS_URL=https://cas.example.com
# CAS_HOST=cas.example.com
# CAS_PORT=443
# CAS_SSL=true
# CAS_VALIDATE_URL=https://cas.example.com/validate
# CAS_CALLBACK_URL=https://mastodon.example.com/auth/cas/callback
EOF
# Set proper permissions
sudo chmod 600 /home/mastodon/live/.env.production
sudo chown mastodon:mastodon /home/mastodon/live/.env.production
#### Database Setup with Optimization
# Create and configure PostgreSQL database
sudo -u postgres psql << 'EOF'
-- Create user and database
CREATE USER mastodon WITH PASSWORD 'Mast0d0n_DB_P@ss_2024!' CREATEDB;
CREATE DATABASE mastodon_production OWNER mastodon;
-- Grant privileges
GRANT ALL PRIVILEGES ON DATABASE mastodon_production TO mastodon;
-- Connect to the database
\c mastodon_production
-- Enable required extensions
CREATE EXTENSION IF NOT EXISTS pg_stat_statements;
CREATE EXTENSION IF NOT EXISTS pg_trgm;
CREATE EXTENSION IF NOT EXISTS btree_gin;
CREATE EXTENSION IF NOT EXISTS btree_gist;
-- Set optimal parameters for Mastodon
ALTER DATABASE mastodon_production SET shared_preload_libraries = 'pg_stat_statements';
ALTER DATABASE mastodon_production SET pg_stat_statements.track = 'all';
ALTER DATABASE mastodon_production SET random_page_cost = 1.1;
ALTER DATABASE mastodon_production SET effective_cache_size = '4GB';
ALTER DATABASE mastodon_production SET shared_buffers = '1GB';
ALTER DATABASE mastodon_production SET work_mem = '16MB';
ALTER DATABASE mastodon_production SET maintenance_work_mem = '256MB';
ALTER DATABASE mastodon_production SET max_connections = 200;
EOF
# Initialize database schema
sudo -u mastodon bash << 'EOF'
cd /home/mastodon/live
export PATH="$HOME/.rbenv/bin:$PATH"
eval "$(rbenv init -)"
# Run database setup
RAILS_ENV=production bundle exec rails db:setup
# Run migrations
RAILS_ENV=production bundle exec rails db:migrate
# Precompile assets
RAILS_ENV=production bundle exec rails assets:precompile
# Create admin user
RAILS_ENV=production bundle exec tootctl accounts create admin --email admin@example.com --confirmed --role Admin
EOF
#### Redis Configuration
# Configure Redis for Mastodon
sudo tee /etc/redis/redis.conf << 'EOF'
# Redis Production Configuration for Mastodon
# Network
bind 127.0.0.1 ::1
protected-mode yes
port 6379
tcp-backlog 511
tcp-keepalive 300
timeout 0
# General
daemonize yes
supervised systemd
pidfile /var/run/redis/redis-server.pid
loglevel notice
logfile /var/log/redis/redis-server.log
syslog-enabled yes
databases 16
# Snapshotting
save 900 1
save 300 10
save 60 10000
stop-writes-on-bgsave-error yes
rdbcompression yes
rdbchecksum yes
dbfilename dump.rdb
dir /var/lib/redis
# Replication
replica-read-only yes
repl-diskless-sync no
repl-diskless-sync-delay 5
# Security
requirepass Redis_P@ss_2024!
masterauth Redis_P@ss_2024!
# Limits
maxclients 10000
maxmemory 2gb
maxmemory-policy allkeys-lru
# Append only mode
appendonly yes
appendfilename "appendonly.aof"
appendfsync everysec
no-appendfsync-on-rewrite no
auto-aof-rewrite-percentage 100
auto-aof-rewrite-min-size 64mb
# Lua scripting
lua-time-limit 5000
# Cluster
cluster-enabled no
# Slow log
slowlog-log-slower-than 10000
slowlog-max-len 128
# Latency monitor
latency-monitor-threshold 0
# Advanced config
hash-max-ziplist-entries 512
hash-max-ziplist-value 64
list-max-ziplist-size -2
list-compress-depth 0
set-max-intset-entries 512
zset-max-ziplist-entries 128
zset-max-ziplist-value 64
hll-sparse-max-bytes 3000
stream-node-max-bytes 4096
stream-node-max-entries 100
activerehashing yes
client-output-buffer-limit normal 0 0 0
client-output-buffer-limit replica 256mb 64mb 60
client-output-buffer-limit pubsub 32mb 8mb 60
hz 10
dynamic-hz yes
aof-rewrite-incremental-fsync yes
rdb-save-incremental-fsync yes
EOF
sudo systemctl restart redis
#### Nginx Configuration
# Create comprehensive Nginx configuration
sudo tee /etc/nginx/sites-available/mastodon << 'EOF'
upstream backend {
server 127.0.0.1:3000 fail_timeout=0;
}
upstream streaming {
server 127.0.0.1:4000 fail_timeout=0;
}
proxy_cache_path /var/cache/nginx/mastodon levels=1:2 keys_zone=CACHE:10m max_size=1g inactive=7d use_temp_path=off;
server {
listen 80;
listen [::]:80;
server_name mastodon.example.com;
root /home/mastodon/live/public;
location /.well-known/acme-challenge/ { allow all; }
location / { return 301 https://$host$request_uri; }
}
server {
listen 443 ssl http2;
listen [::]:443 ssl http2;
server_name mastodon.example.com;
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;
ssl_prefer_server_ciphers off;
ssl_certificate /etc/letsencrypt/live/mastodon.example.com/fullchain.pem;
ssl_certificate_key /etc/letsencrypt/live/mastodon.example.com/privkey.pem;
ssl_trusted_certificate /etc/letsencrypt/live/mastodon.example.com/chain.pem;
ssl_session_timeout 1d;
ssl_session_cache shared:SSL:10m;
ssl_session_tickets off;
ssl_stapling on;
ssl_stapling_verify on;
resolver 8.8.8.8 8.8.4.4 valid=300s;
resolver_timeout 5s;
# Security headers
add_header Strict-Transport-Security "max-age=63072000; includeSubDomains; preload" always;
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;
add_header Content-Security-Policy "default-src 'self'; script-src 'self' 'unsafe-inline' 'unsafe-eval'; style-src 'self' 'unsafe-inline'; img-src 'self' data: https:; font-src 'self' data:; connect-src 'self' wss://mastodon.example.com; media-src 'self' https:; frame-ancestors 'none'; base-uri 'self'; form-action 'self';" always;
keepalive_timeout 70;
sendfile on;
client_max_body_size 80m;
root /home/mastodon/live/public;
gzip on;
gzip_disable "msie6";
gzip_vary on;
gzip_proxied any;
gzip_comp_level 6;
gzip_types text/plain text/css text/xml text/javascript application/json application/javascript application/xml+rss application/rss+xml application/atom+xml image/svg+xml text/javascript application/vnd.ms-fontobject application/x-font-ttf font/opentype image/x-icon;
location / {
try_files $uri @proxy;
}
location ~ ^/(system|avatars|headers|emoji|packs|media_attachments) {
add_header Cache-Control "public, max-age=31536000, immutable";
add_header X-Content-Type-Options nosniff;
try_files $uri =404;
}
location /sw.js {
add_header Cache-Control "public, max-age=0";
try_files $uri =404;
}
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://backend;
proxy_buffering on;
proxy_cache CACHE;
proxy_cache_valid 200 7d;
proxy_cache_valid 410 24h;
proxy_cache_use_stale error timeout updating http_500 http_502 http_503 http_504;
add_header X-Cached $upstream_cache_status;
tcp_nodelay on;
}
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 Proxy "";
proxy_pass http://streaming;
proxy_buffering off;
proxy_redirect off;
proxy_http_version 1.1;
proxy_set_header Upgrade $http_upgrade;
proxy_set_header Connection $connection_upgrade;
tcp_nodelay on;
}
error_page 500 501 502 503 504 /500.html;
}
EOF
sudo ln -s /etc/nginx/sites-available/mastodon /etc/nginx/sites-enabled/
sudo nginx -t && sudo systemctl reload nginx
4. Service Management
Systemd Service Files (RHEL/Debian/Arch/SUSE)
Create the web service (/etc/systemd/system/mastodon-web.service):
[Unit]
Description=mastodon-web
After=network.target
[Service]
Type=simple
User=mastodon
WorkingDirectory=/home/mastodon/live
Environment="RAILS_ENV=production"
Environment="PORT=3000"
ExecStart=/home/mastodon/.rbenv/shims/bundle exec puma -C config/puma.rb
ExecReload=/bin/kill -SIGUSR1 $MAINPID
TimeoutSec=15
Restart=always
RestartSec=10
SyslogIdentifier=mastodon-web
[Install]
WantedBy=multi-user.target
Create the background jobs service (/etc/systemd/system/mastodon-sidekiq.service):
[Unit]
Description=mastodon-sidekiq
After=network.target
[Service]
Type=simple
User=mastodon
WorkingDirectory=/home/mastodon/live
Environment="RAILS_ENV=production"
Environment="DB_POOL=25"
ExecStart=/home/mastodon/.rbenv/shims/bundle exec sidekiq -c 25
TimeoutSec=15
Restart=always
RestartSec=10
SyslogIdentifier=mastodon-sidekiq
[Install]
WantedBy=multi-user.target
Create the streaming service (/etc/systemd/system/mastodon-streaming.service):
[Unit]
Description=mastodon-streaming
After=network.target
[Service]
Type=simple
User=mastodon
WorkingDirectory=/home/mastodon/live
Environment="NODE_ENV=production"
Environment="PORT=4000"
ExecStart=/usr/bin/node streaming
TimeoutSec=15
Restart=always
RestartSec=10
SyslogIdentifier=mastodon-streaming
[Install]
WantedBy=multi-user.target
Enable and Start Services
# Reload systemd and enable services
sudo systemctl daemon-reload
sudo systemctl enable mastodon-web mastodon-sidekiq mastodon-streaming
sudo systemctl start mastodon-web mastodon-sidekiq mastodon-streaming
# Check status
sudo systemctl status mastodon-web mastodon-sidekiq mastodon-streaming
OpenRC Services (Alpine Linux)
Create /etc/init.d/mastodon-web:
#!/sbin/openrc-run
name="mastodon-web"
description="Mastodon web service"
user="mastodon"
group="mastodon"
directory="/home/mastodon/live"
command="/home/mastodon/.rbenv/shims/bundle"
command_args="exec puma -C config/puma.rb"
command_background="yes"
pidfile="/run/${name}.pid"
output_log="/var/log/${name}.log"
error_log="/var/log/${name}.error.log"
depend() {
need net postgresql redis
}
start_pre() {
export RAILS_ENV=production
export PORT=3000
}
Create similar files for mastodon-sidekiq and mastodon-streaming, then:
# Make executable
sudo chmod +x /etc/init.d/mastodon-*
# Enable and start
sudo rc-update add mastodon-web
sudo rc-update add mastodon-sidekiq
sudo rc-update add mastodon-streaming
sudo rc-service mastodon-web start
sudo rc-service mastodon-sidekiq start
sudo rc-service mastodon-streaming start
5. Troubleshooting
Common Issues
Ruby/Bundle Issues:
# Ensure proper rbenv setup
sudo -u mastodon bash << 'EOF'
export PATH="$HOME/.rbenv/bin:$PATH"
eval "$(rbenv init -)"
which ruby
ruby --version
EOF
# Reinstall gems if needed
sudo -u mastodon bash << 'EOF'
cd /home/mastodon/live
export PATH="$HOME/.rbenv/bin:$PATH"
eval "$(rbenv init -)"
bundle install --deployment --without development test
EOF
Database Connection Issues:
# Test PostgreSQL connection
sudo -u mastodon psql -h localhost -U mastodon mastodon_production
# Check PostgreSQL logs
sudo journalctl -u postgresql-15 -f
# Verify database configuration
sudo -u mastodon bash << 'EOF'
cd /home/mastodon/live
export PATH="$HOME/.rbenv/bin:$PATH"
eval "$(rbenv init -)"
RAILS_ENV=production bundle exec rails console
# In console: ActiveRecord::Base.connection
EOF
Asset Compilation Issues:
# Clear and recompile assets
sudo -u mastodon bash << 'EOF'
cd /home/mastodon/live
export PATH="$HOME/.rbenv/bin:$PATH"
eval "$(rbenv init -)"
RAILS_ENV=production bundle exec rails assets:clobber
RAILS_ENV=production bundle exec rails assets:precompile
EOF
Service Startup Issues:
# Check service logs
sudo journalctl -u mastodon-web -f
sudo journalctl -u mastodon-sidekiq -f
sudo journalctl -u mastodon-streaming -f
# Check for port conflicts
sudo netstat -tlnp | grep :3000
sudo netstat -tlnp | grep :4000
# Verify file permissions
sudo ls -la /home/mastodon/live/
sudo -u mastodon test -r /home/mastodon/live/.env.production && echo "Can read env file"
Federation Issues
# Check federation status
sudo -u mastodon bash << 'EOF'
cd /home/mastodon/live
export PATH="$HOME/.rbenv/bin:$PATH"
eval "$(rbenv init -)"
RAILS_ENV=production bundle exec rails console
# In console: Account.find_remote('username', 'instance.domain')
EOF
# Test ActivityPub endpoint
curl -H "Accept: application/activity+json" https://your-domain.com/users/username
# Check delivery worker
sudo journalctl -u mastodon-sidekiq | grep DeliveryWorker
Performance Issues
# Monitor resource usage
htop
iostat -x 1
free -h
# Check database performance
sudo -u postgres psql mastodon_production -c "SELECT * FROM pg_stat_activity;"
# Redis monitoring
redis-cli monitor
redis-cli info memory
6. Security Considerations
Firewall Configuration
firewalld (RHEL/CentOS/Fedora):
# Open required ports
sudo firewall-cmd --permanent --add-service=http
sudo firewall-cmd --permanent --add-service=https
sudo firewall-cmd --permanent --add-port=3000/tcp
sudo firewall-cmd --permanent --add-port=4000/tcp
sudo firewall-cmd --reload
ufw (Ubuntu/Debian):
# Configure firewall
sudo ufw allow 80/tcp
sudo ufw allow 443/tcp
sudo ufw allow 3000/tcp
sudo ufw allow 4000/tcp
sudo ufw enable
iptables (Generic):
# Basic firewall rules
sudo iptables -A INPUT -p tcp --dport 80 -j ACCEPT
sudo iptables -A INPUT -p tcp --dport 443 -j ACCEPT
sudo iptables -A INPUT -p tcp --dport 3000 -j ACCEPT
sudo iptables -A INPUT -p tcp --dport 4000 -j ACCEPT
SSL/TLS Configuration with nginx
Create /etc/nginx/sites-available/mastodon (Debian/Ubuntu) or /etc/nginx/conf.d/mastodon.conf (RHEL):
map $http_upgrade $connection_upgrade {
default upgrade;
'' close;
}
upstream backend {
server 127.0.0.1:3000 fail_timeout=0;
}
upstream streaming {
server 127.0.0.1:4000 fail_timeout=0;
}
proxy_cache_path /var/cache/nginx/mastodon keys_zone=CACHE:10m levels=1:2 inactive=7d max_size=1g;
server {
listen 80;
listen [::]:80;
server_name mastodon.example.com;
root /home/mastodon/live/public;
location /.well-known/acme-challenge/ { allow all; }
location / { return 301 https://$host$request_uri; }
}
server {
listen 443 ssl http2;
listen [::]:443 ssl http2;
server_name mastodon.example.com;
ssl_protocols TLSv1.2 TLSv1.3;
ssl_ciphers ECDHE-RSA-AES128-GCM-SHA256:ECDHE-RSA-AES256-GCM-SHA384:ECDHE-RSA-CHACHA20-POLY1305:DHE-RSA-AES128-GCM-SHA256:DHE-RSA-AES256-GCM-SHA384;
ssl_prefer_server_ciphers off;
ssl_session_cache shared:SSL:10m;
ssl_session_tickets off;
ssl_certificate /etc/letsencrypt/live/mastodon.example.com/fullchain.pem;
ssl_certificate_key /etc/letsencrypt/live/mastodon.example.com/privkey.pem;
keepalive_timeout 70;
sendfile on;
client_max_body_size 80m;
root /home/mastodon/live/public;
gzip on;
gzip_disable "msie6";
gzip_vary on;
gzip_proxied any;
gzip_comp_level 6;
gzip_types
text/plain
text/css
text/xml
text/javascript
application/json
application/javascript
application/xml+rss
application/atom+xml
image/svg+xml;
add_header Strict-Transport-Security "max-age=31536000" always;
location / {
try_files $uri @proxy;
}
location ~ ^/(emoji|packs|system/accounts/avatars|system/media_attachments/files) {
add_header Cache-Control "public, max-age=31536000, immutable";
add_header Strict-Transport-Security "max-age=31536000" always;
try_files $uri @proxy;
}
location /sw.js {
add_header Cache-Control "public, max-age=604800, must-revalidate";
add_header Strict-Transport-Security "max-age=31536000" always;
try_files $uri @proxy;
}
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://backend;
proxy_buffering on;
proxy_redirect off;
proxy_http_version 1.1;
proxy_set_header Upgrade $http_upgrade;
proxy_set_header Connection $connection_upgrade;
proxy_cache CACHE;
proxy_cache_valid 200 7d;
proxy_cache_valid 410 24h;
proxy_cache_use_stale error timeout updating http_500 http_502 http_503 http_504;
proxy_cache_lock on;
tcp_nodelay on;
}
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 Proxy "";
proxy_pass http://streaming;
proxy_buffering off;
proxy_redirect off;
proxy_http_version 1.1;
proxy_set_header Upgrade $http_upgrade;
proxy_set_header Connection $connection_upgrade;
tcp_nodelay on;
}
error_page 500 501 502 503 504 /500.html;
}
SSL Certificate Setup
# Install Certbot
# RHEL/CentOS/Fedora
sudo dnf install -y certbot python3-certbot-nginx
# Debian/Ubuntu
sudo apt install -y certbot python3-certbot-nginx
# Arch Linux
sudo pacman -S certbot certbot-nginx
# Obtain certificate
sudo certbot --nginx -d mastodon.example.com
# Auto-renewal
echo "0 12 * * * /usr/bin/certbot renew --quiet" | sudo crontab -
SELinux Configuration (RHEL-based systems)
# Allow nginx to connect to Mastodon services
sudo setsebool -P httpd_can_network_connect 1
# Set SELinux contexts for Mastodon files
sudo semanage fcontext -a -t httpd_exec_t "/home/mastodon/live/public(/.*)?"
sudo restorecon -R /home/mastodon/live/public/
# Allow Mastodon to bind to required ports
sudo semanage port -a -t http_port_t -p tcp 3000
sudo semanage port -a -t http_port_t -p tcp 4000
Security Hardening
# Disable password authentication for mastodon user
sudo passwd -l mastodon
# Set proper file permissions
sudo chmod 750 /home/mastodon
sudo chmod 755 /home/mastodon/live
sudo chmod 600 /home/mastodon/live/.env.production
# Configure fail2ban for nginx
sudo tee /etc/fail2ban/jail.d/nginx.conf << 'EOF'
[nginx-http-auth]
enabled = true
port = http,https
logpath = /var/log/nginx/error.log
[nginx-limit-req]
enabled = true
port = http,https
logpath = /var/log/nginx/error.log
EOF
sudo systemctl restart fail2ban
7. Performance Tuning
PostgreSQL Optimization
Edit /var/lib/pgsql/15/data/postgresql.conf:
# Memory settings (adjust based on available RAM)
shared_buffers = 256MB # 1/4 of RAM
effective_cache_size = 1GB # 3/4 of RAM
work_mem = 4MB # RAM/max_connections/4
maintenance_work_mem = 64MB # RAM/16
# Connection settings
max_connections = 100
superuser_reserved_connections = 3
# WAL settings for better performance
wal_buffers = 16MB
checkpoint_completion_target = 0.7
checkpoint_timeout = 5min
max_wal_size = 1GB
min_wal_size = 80MB
# Query optimization
random_page_cost = 1.1 # For SSD storage
effective_io_concurrency = 200 # For SSD storage
Redis Optimization
Edit /etc/redis/redis.conf or /etc/redis.conf:
# Memory optimization
maxmemory 256mb
maxmemory-policy allkeys-lru
# Persistence (adjust based on needs)
save 900 1
save 300 10
save 60 10000
# Network optimization
tcp-keepalive 300
timeout 0
Ruby/Rails Optimization
Edit /home/mastodon/live/.env.production:
# Database connection pooling
DB_POOL=25
# Sidekiq concurrency
SIDEKIQ_CONCURRENCY=25
# Puma workers (number of CPU cores)
WEB_CONCURRENCY=4
MAX_THREADS=5
# Streaming cluster
STREAMING_CLUSTER_NUM=4
System-level Optimization
# Increase file descriptors limit
echo "mastodon soft nofile 65536" | sudo tee -a /etc/security/limits.conf
echo "mastodon hard nofile 65536" | sudo tee -a /etc/security/limits.conf
# Optimize kernel parameters
sudo tee -a /etc/sysctl.conf << 'EOF'
# Network optimization
net.core.somaxconn = 1024
net.core.netdev_max_backlog = 5000
net.ipv4.tcp_max_syn_backlog = 1024
net.ipv4.tcp_keepalive_time = 120
net.ipv4.tcp_keepalive_intvl = 30
net.ipv4.tcp_keepalive_probes = 3
# Memory optimization
vm.swappiness = 10
vm.dirty_ratio = 15
vm.dirty_background_ratio = 5
EOF
sudo sysctl -p
Elasticsearch Integration (Optional)
# Install Elasticsearch (RHEL/CentOS)
sudo rpm --import https://artifacts.elastic.co/GPG-KEY-elasticsearch
sudo tee /etc/yum.repos.d/elasticsearch.repo << 'EOF'
[elasticsearch]
name=Elasticsearch repository for 8.x packages
baseurl=https://artifacts.elastic.co/packages/8.x/yum
gpgcheck=1
gpgkey=https://artifacts.elastic.co/GPG-KEY-elasticsearch
enabled=1
autorefresh=1
type=rpm-md
EOF
sudo dnf install -y elasticsearch
sudo systemctl enable --now elasticsearch
# Configure for Mastodon
sudo tee -a /home/mastodon/live/.env.production << 'EOF'
ES_ENABLED=true
ES_HOST=localhost
ES_PORT=9200
EOF
# Create search index
sudo -u mastodon bash << 'EOF'
cd /home/mastodon/live
export PATH="$HOME/.rbenv/bin:$PATH"
eval "$(rbenv init -)"
RAILS_ENV=production bundle exec rake chewy:upgrade
EOF
8. Backup and Restore
Database Backup
# Create backup script
sudo tee /usr/local/bin/mastodon-backup.sh << 'EOF'
#!/bin/bash
BACKUP_DIR="/backup/mastodon"
DATE=$(date +%Y%m%d_%H%M%S)
DB_NAME="mastodon_production"
MEDIA_DIR="/home/mastodon/live/public/system"
# Create backup directory
mkdir -p "$BACKUP_DIR"
# Database backup
sudo -u postgres pg_dump "$DB_NAME" | gzip > "$BACKUP_DIR/db_${DATE}.sql.gz"
# Media backup (if stored locally)
if [ -d "$MEDIA_DIR" ]; then
tar -czf "$BACKUP_DIR/media_${DATE}.tar.gz" -C "$(dirname "$MEDIA_DIR")" "$(basename "$MEDIA_DIR")"
fi
# Configuration backup
tar -czf "$BACKUP_DIR/config_${DATE}.tar.gz" /home/mastodon/live/.env.production
# Remove backups older than 30 days
find "$BACKUP_DIR" -name "*.gz" -mtime +30 -delete
echo "Backup completed: $DATE"
EOF
sudo chmod +x /usr/local/bin/mastodon-backup.sh
Automated Backups
# Add to crontab
echo "0 2 * * * /usr/local/bin/mastodon-backup.sh" | sudo crontab -
# Verify crontab
sudo crontab -l
S3 Backup Integration
# Install AWS CLI
sudo dnf install -y awscli # RHEL/CentOS
sudo apt install -y awscli # Debian/Ubuntu
# Configure AWS credentials
aws configure
# Enhanced backup script with S3
sudo tee /usr/local/bin/mastodon-s3-backup.sh << 'EOF'
#!/bin/bash
BACKUP_DIR="/tmp/mastodon-backup"
DATE=$(date +%Y%m%d_%H%M%S)
S3_BUCKET="your-backup-bucket"
DB_NAME="mastodon_production"
mkdir -p "$BACKUP_DIR"
# Database backup
sudo -u postgres pg_dump "$DB_NAME" | gzip > "$BACKUP_DIR/db_${DATE}.sql.gz"
# Upload to S3
aws s3 cp "$BACKUP_DIR/db_${DATE}.sql.gz" "s3://$S3_BUCKET/mastodon/db/"
# Clean up local backup
rm -f "$BACKUP_DIR/db_${DATE}.sql.gz"
echo "S3 backup completed: $DATE"
EOF
sudo chmod +x /usr/local/bin/mastodon-s3-backup.sh
Restore Procedure
# Stop Mastodon services
sudo systemctl stop mastodon-web mastodon-sidekiq mastodon-streaming
# Restore database
sudo -u postgres dropdb mastodon_production
sudo -u postgres createdb mastodon_production
sudo -u postgres psql -c "GRANT ALL PRIVILEGES ON DATABASE mastodon_production TO mastodon;"
gunzip -c /backup/mastodon/db_YYYYMMDD_HHMMSS.sql.gz | sudo -u postgres psql mastodon_production
# Restore media files (if needed)
sudo rm -rf /home/mastodon/live/public/system
sudo tar -xzf /backup/mastodon/media_YYYYMMDD_HHMMSS.tar.gz -C /home/mastodon/live/public/
sudo chown -R mastodon:mastodon /home/mastodon/live/public/system
# Restore configuration
sudo tar -xzf /backup/mastodon/config_YYYYMMDD_HHMMSS.tar.gz -C /
# Start services
sudo systemctl start mastodon-web mastodon-sidekiq mastodon-streaming
Database Migration and Upgrades
# Before upgrading Mastodon
sudo systemctl stop mastodon-web mastodon-sidekiq mastodon-streaming
# Backup current version
sudo -u postgres pg_dump mastodon_production | gzip > /backup/pre-upgrade-$(date +%Y%m%d).sql.gz
# Update Mastodon code
sudo -u mastodon bash << 'EOF'
cd /home/mastodon/live
git fetch
git checkout $(git tag -l | grep -v 'rc' | sort -V | tail -n 1)
export PATH="$HOME/.rbenv/bin:$PATH"
eval "$(rbenv init -)"
bundle install --deployment --without development test
yarn install --pure-lockfile
RAILS_ENV=production bundle exec rails db:migrate
RAILS_ENV=production bundle exec rails assets:precompile
EOF
# Start services
sudo systemctl start mastodon-web mastodon-sidekiq mastodon-streaming
9. System Requirements
Minimum Requirements
Recommended Production Requirements
Scaling Considerations
Storage Requirements
Network Requirements
10. Support
Official Resources
Community Support
Commercial Support
Troubleshooting Resources
11. Contributing
How to Contribute
1. Report Bugs: Submit issues to GitHub
2. Translate: Help with internationalization at Crowdin
3. Code Contributions: Submit pull requests for bug fixes and features
4. Documentation: Improve documentation and guides
5. Testing: Test release candidates and provide feedback
Development Setup
# Clone repository
git clone https://github.com/mastodon/mastodon.git
cd mastodon
# Install dependencies
bundle install
yarn install
# Setup development database
rails db:setup
# Run development server
foreman start
Contribution Guidelines
Code of Conduct
Pull Request Process
1. Fork the repository
2. Create a feature branch
3. Make your changes with tests
4. Update documentation
5. Submit pull request with clear description
12. License
Mastodon is licensed under the GNU Affero General Public License v3.0 (AGPL-3.0).
Key License Points
License Compliance
Commercial Use
Full license text: https://github.com/mastodon/mastodon/blob/main/LICENSE
13. Acknowledgments
Core Team
Technologies Used
Community
Special Thanks
14. Version History
Major Releases
Current Stable Version
Upgrade Path
Release Cycle
Breaking Changes
15. Appendices
Appendix A: Port Reference
| Port | Service | Protocol | Description |
|------|---------|----------|-------------|
| 80 | HTTP | TCP | Web traffic (redirects to HTTPS) |
| 443 | HTTPS | TCP | Secure web traffic |
| 3000 | Mastodon Web | TCP | Internal web service |
| 4000 | Mastodon Streaming | TCP | Real-time updates |
| 5432 | PostgreSQL | TCP | Database (internal) |
| 6379 | Redis | TCP | Cache and queues (internal) |
| 25/587 | SMTP | TCP | Email delivery (outbound) |
Appendix B: File Locations
| Path | Description |
|------|-------------|
| /home/mastodon/live/ | Mastodon application directory |
| /home/mastodon/live/.env.production | Main configuration file |
| /home/mastodon/live/public/system/ | Media files storage |
| /etc/systemd/system/mastodon-*.service | Service definitions |
| /etc/nginx/sites-available/mastodon | nginx configuration |
| /var/lib/postgresql/*/data/ | PostgreSQL data directory |
| /etc/redis/redis.conf | Redis configuration |
| /var/log/nginx/ | nginx logs |
Appendix C: Common Commands
# Service management
sudo systemctl status mastodon-web mastodon-sidekiq mastodon-streaming
sudo systemctl restart mastodon-web mastodon-sidekiq mastodon-streaming
# Mastodon maintenance
sudo -u mastodon bash -c 'cd /home/mastodon/live && RAILS_ENV=production bundle exec rails mastodon:maintenance'
# Database management
sudo -u postgres psql mastodon_production
# Log monitoring
sudo journalctl -u mastodon-web -f
sudo tail -f /var/log/nginx/access.log
Appendix D: Environment Variables Reference
| Variable | Description | Required |
|----------|-------------|----------|
| LOCAL_DOMAIN | Your Mastodon domain | Yes |
| SECRET_KEY_BASE | Rails secret key | Yes |
| OTP_SECRET | Two-factor authentication secret | Yes |
| VAPID_PRIVATE_KEY | Push notification private key | Yes |
| VAPID_PUBLIC_KEY | Push notification public key | Yes |
| DB_HOST | Database hostname | Yes |
| DB_USER | Database username | Yes |
| DB_PASS | Database password | Yes |
| DB_NAME | Database name | Yes |
| REDIS_HOST | Redis hostname | Yes |
| SMTP_SERVER | SMTP server hostname | Recommended |
| SMTP_FROM_ADDRESS | From email address | Recommended |
| S3_ENABLED | Enable S3 storage | No |
| CDN_HOST | CDN hostname for assets | No |
Appendix E: Federation Debugging
# Test federation endpoints
curl -H "Accept: application/activity+json" https://your-domain.com/.well-known/webfinger?resource=acct:username@your-domain.com
curl -H "Accept: application/activity+json" https://your-domain.com/users/username
# Check instance reachability
sudo -u mastodon bash << 'EOF'
cd /home/mastodon/live
export PATH="$HOME/.rbenv/bin:$PATH"
eval "$(rbenv init -)"
RAILS_ENV=production bundle exec rails console
# Instance.find_by(domain: 'example.com')&.ping
EOF
# Monitor federation jobs
sudo journalctl -u mastodon-sidekiq | grep -E "(DeliveryWorker|ActivityPub)"
Appendix F: Monitoring and Alerting
# Basic monitoring script
sudo tee /usr/local/bin/mastodon-monitor.sh << 'EOF'
#!/bin/bash
# Check services
systemctl is-active --quiet mastodon-web || echo "ALERT: Mastodon web service down"
systemctl is-active --quiet mastodon-sidekiq || echo "ALERT: Mastodon sidekiq down"
systemctl is-active --quiet mastodon-streaming || echo "ALERT: Mastodon streaming down"
# Check disk space
DISK_USAGE=$(df /home/mastodon | tail -1 | awk '{print $5}' | sed 's/%//')
if [ "$DISK_USAGE" -gt 80 ]; then
echo "ALERT: Disk usage high: ${DISK_USAGE}%"
fi
# Check database connections
DB_CONNECTIONS=$(sudo -u postgres psql -t -c "SELECT count(*) FROM pg_stat_activity WHERE datname='mastodon_production';" | tr -d ' ')
if [ "$DB_CONNECTIONS" -gt 80 ]; then
echo "ALERT: High database connections: $DB_CONNECTIONS"
fi
EOF
sudo chmod +x /usr/local/bin/mastodon-monitor.sh
---
Note: This guide is part of the HowToMgr collection. Always refer to official Mastodon documentation for the most up-to-date information.