#!/bin/bash # Mastodon v4.5.4 Bare-Metal Install Script for Rocky Linux 10 # Usage: curl -sSL https://git.vish.gg/Vish/pihole-baremetal/raw/branch/main/mastodon/install-mastodon.sh | bash # Run as root on a fresh Rocky Linux 10 VM set -e # Configuration - Edit these before running DOMAIN="${DOMAIN:-mastodon.example.com}" ADMIN_USER="${ADMIN_USER:-admin}" ADMIN_EMAIL="${ADMIN_EMAIL:-admin@example.com}" SMTP_SERVER="${SMTP_SERVER:-smtp.gmail.com}" SMTP_PORT="${SMTP_PORT:-587}" SMTP_USER="${SMTP_USER:-}" SMTP_PASS="REDACTED_PASSWORD" SMTP_FROM="${SMTP_FROM:-notifications@example.com}" echo "==========================================" echo "Mastodon v4.5.4 Installation Script" echo "Target Domain: $DOMAIN" echo "==========================================" # Check if running as root if [[ $EUID -ne 0 ]]; then echo "This script must be run as root" exit 1 fi # Install system dependencies echo "[1/12] Installing system dependencies..." dnf install -y epel-release dnf install -y git curl wget gcc make autoconf bison openssl-devel \ libyaml-devel libffi-devel readline-devel zlib-devel gdbm-devel ncurses-devel \ libxml2-devel libxslt-devel libicu-devel libidn-devel jemalloc-devel \ ImageMagick ImageMagick-devel nginx postgresql-server postgresql-contrib \ valkey certbot python3-certbot-nginx meson ninja-build \ libpng-devel libjpeg-turbo-devel libwebp-devel libtiff-devel \ expat-devel gobject-introspection-devel glib2-devel # Install Node.js 20 echo "[2/12] Installing Node.js 20..." curl -fsSL https://rpm.nodesource.com/setup_20.x | bash - dnf install -y nodejs # Enable corepack for Yarn corepack enable # Build libvips from source (not in Rocky 10 repos) echo "[3/12] Building libvips from source..." cd /tmp wget https://github.com/libvips/libvips/releases/download/v8.16.1/vips-8.16.1.tar.xz tar xf vips-8.16.1.tar.xz cd vips-8.16.1 meson setup build --prefix=/usr --buildtype=release cd build && ninja && ninja install ldconfig cd /tmp && rm -rf vips-8.16.1* # Initialize PostgreSQL echo "[4/12] Setting up PostgreSQL..." postgresql-setup --initdb systemctl enable --now postgresql # Create mastodon database user and database sudo -u postgres psql -c "CREATE USER mastodon CREATEDB;" sudo -u postgres psql -c "CREATE DATABASE mastodon_production OWNER mastodon;" # Start Valkey (Redis) echo "[5/12] Starting Valkey..." systemctl enable --now valkey # Create mastodon user echo "[6/12] Creating mastodon user..." useradd -m -s /bin/bash mastodon || true # Install Ruby via rbenv echo "[7/12] Installing Ruby 3.4.7..." sudo -u mastodon bash << 'RUBY_INSTALL' cd ~ git clone https://github.com/rbenv/rbenv.git ~/.rbenv echo 'export PATH="$HOME/.rbenv/bin:$PATH"' >> ~/.bashrc echo 'eval "$(rbenv init -)"' >> ~/.bashrc export PATH="$HOME/.rbenv/bin:$PATH" eval "$(rbenv init -)" git clone https://github.com/rbenv/ruby-build.git ~/.rbenv/plugins/ruby-build RUBY_CONFIGURE_OPTS="--with-jemalloc" rbenv install 3.4.7 rbenv global 3.4.7 gem install bundler RUBY_INSTALL # Clone Mastodon echo "[8/12] Cloning Mastodon v4.5.4..." sudo -u mastodon bash << 'CLONE' cd ~ git clone https://github.com/mastodon/mastodon.git live cd live git checkout v4.5.4 CLONE # Install dependencies echo "[9/12] Installing Ruby and Node dependencies..." sudo -u mastodon bash << 'DEPS' export PATH="$HOME/.rbenv/bin:$PATH" eval "$(rbenv init -)" cd ~/live bundle config deployment 'true' bundle config without 'development test' bundle install -j$(nproc) yarn install --immutable DEPS # Generate secrets and create .env.production echo "[10/12] Generating secrets and configuration..." SECRET_KEY=$(openssl rand -hex 64) OTP_SECRET=$(openssl rand -hex 64) VAPID_KEYS=$(sudo -u mastodon bash -c 'cd ~/live && export PATH="$HOME/.rbenv/bin:$PATH" && eval "$(rbenv init -)" && RAILS_ENV=production bundle exec rake mastodon:webpush:generate_vapid_key 2>/dev/null') VAPID_PRIVATE=$(echo "$VAPID_KEYS" | grep VAPID_PRIVATE_KEY | cut -d= -f2) VAPID_PUBLIC=$(echo "$VAPID_KEYS" | grep VAPID_PUBLIC_KEY | cut -d= -f2) AR_KEY=$(openssl rand -hex 32) AR_DETERMINISTIC=$(openssl rand -hex 32) AR_SALT=$(openssl rand -hex 32) cat > /home/mastodon/live/.env.production << ENVFILE LOCAL_DOMAIN=$DOMAIN SINGLE_USER_MODE=false SECRET_KEY_BASE=$SECRET_KEY OTP_SECRET=$OTP_SECRET VAPID_PRIVATE_KEY=$VAPID_PRIVATE VAPID_PUBLIC_KEY=$VAPID_PUBLIC DB_HOST=/var/run/postgresql DB_USER=mastodon DB_NAME=mastodon_production DB_PASS= "REDACTED_PASSWORD" REDIS_HOST=127.0.0.1 REDIS_PORT=6379 SMTP_SERVER=$SMTP_SERVER SMTP_PORT=$SMTP_PORT SMTP_LOGIN=$SMTP_USER SMTP_PASSWORD="REDACTED_PASSWORD" SMTP_FROM_ADDRESS=$SMTP_FROM SMTP_AUTH_METHOD=plain SMTP_OPENSSL_VERIFY_MODE=none SMTP_ENABLE_STARTTLS=auto ACTIVE_RECORD_ENCRYPTION_PRIMARY_KEY=$AR_KEY ACTIVE_RECORD_ENCRYPTION_DETERMINISTIC_KEY=$AR_DETERMINISTIC ACTIVE_RECORD_ENCRYPTION_KEY_DERIVATION_SALT=$AR_SALT TRUSTED_PROXY_IP=127.0.0.1,::1,192.168.0.0/16 ENVFILE chown mastodon:mastodon /home/mastodon/live/.env.production chmod 600 /home/mastodon/live/.env.production # Run migrations and seed echo "[11/12] Running database migrations..." sudo -u mastodon bash << 'MIGRATE' export PATH="$HOME/.rbenv/bin:$PATH" eval "$(rbenv init -)" cd ~/live RAILS_ENV=production bundle exec rails db:migrate RAILS_ENV=production bundle exec rails db:seed RAILS_ENV=production bundle exec rails assets:precompile MIGRATE # Create systemd services echo "[12/12] Creating systemd services..." cat > /etc/systemd/system/mastodon-web.service << 'SERVICE' [Unit] Description=mastodon-web After=network.target [Service] Type=simple User=mastodon WorkingDirectory=/home/mastodon/live Environment="RAILS_ENV=production" Environment="PORT=3001" ExecStart=/bin/bash -lc 'cd /home/mastodon/live && exec bundle exec puma -C config/puma.rb' Restart=on-failure RestartSec=5 [Install] WantedBy=multi-user.target SERVICE cat > /etc/systemd/system/mastodon-sidekiq.service << 'SERVICE' [Unit] Description=mastodon-sidekiq After=network.target [Service] Type=simple User=mastodon WorkingDirectory=/home/mastodon/live Environment="RAILS_ENV=production" Environment="MALLOC_ARENA_MAX=2" ExecStart=/bin/bash -lc 'cd /home/mastodon/live && exec bundle exec sidekiq -c 25' Restart=on-failure RestartSec=5 [Install] WantedBy=multi-user.target SERVICE cat > /etc/systemd/system/mastodon-streaming.service << 'SERVICE' [Unit] Description=mastodon-streaming After=network.target [Service] Type=simple User=mastodon WorkingDirectory=/home/mastodon/live Environment="NODE_ENV=production" Environment="PORT=4000" Environment="STREAMING_CLUSTER_NUM=1" ExecStart=/usr/bin/node ./streaming Restart=on-failure RestartSec=5 [Install] WantedBy=multi-user.target SERVICE # Nginx config cat > /etc/nginx/conf.d/mastodon.conf << 'NGINX' map $http_upgrade $connection_upgrade { default upgrade; '' close; } upstream backend { server 127.0.0.1:3001 fail_timeout=0; } upstream streaming { server 127.0.0.1:4000 fail_timeout=0; } server { listen 3000; listen [::]:3000; server_name _; keepalive_timeout 70; sendfile on; client_max_body_size 99m; root /home/mastodon/live/public; gzip on; gzip_vary on; gzip_proxied any; gzip_comp_level 6; gzip_types text/plain text/css application/json application/javascript text/xml application/xml image/svg+xml; location / { try_files $uri @proxy; } location ~ ^/(assets|avatars|emoji|headers|packs|shortcuts|sounds|system)/ { add_header Cache-Control "public, max-age=2419200, must-revalidate"; try_files $uri =404; } location ^~ /api/v1/streaming { proxy_set_header Host $host; proxy_set_header X-Real-IP $remote_addr; proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; proxy_set_header X-Forwarded-Proto https; 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; } 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 https; 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; tcp_nodelay on; } error_page 404 500 501 502 503 504 /500.html; } NGINX # SELinux and firewall setsebool -P httpd_can_network_connect 1 setsebool -P httpd_read_user_content 1 chcon -R -t httpd_sys_content_t /home/mastodon/live/public chmod 755 /home/mastodon /home/mastodon/live /home/mastodon/live/public firewall-cmd --permanent --add-port=3000/tcp firewall-cmd --reload # Add localhost to Rails hosts echo 'Rails.application.config.hosts << "localhost"' >> /home/mastodon/live/config/environments/production.rb echo 'Rails.application.config.hosts << "127.0.0.1"' >> /home/mastodon/live/config/environments/production.rb chown mastodon:mastodon /home/mastodon/live/config/environments/production.rb # Enable and start services systemctl daemon-reload systemctl enable --now mastodon-web mastodon-sidekiq mastodon-streaming nginx # Create admin user echo "" echo "Creating admin user..." ADMIN_PASS="REDACTED_PASSWORD" -u mastodon bash -c "cd ~/live && export PATH=\"\$HOME/.rbenv/bin:\$PATH\" && eval \"\$(rbenv init -)\" && RAILS_ENV=production bin/tootctl accounts create $ADMIN_USER --email=$ADMIN_EMAIL --confirmed 2>&1 | grep 'New password' | awk '{print \$3}'") sudo -u mastodon bash -c "cd ~/live && export PATH=\"\$HOME/.rbenv/bin:\$PATH\" && eval \"\$(rbenv init -)\" && RAILS_ENV=production bin/tootctl accounts modify $ADMIN_USER --role Owner" sudo -u mastodon bash -c "cd ~/live && export PATH=\"\$HOME/.rbenv/bin:\$PATH\" && eval \"\$(rbenv init -)\" && RAILS_ENV=production bin/tootctl accounts approve $ADMIN_USER" echo "" echo "==========================================" echo "✅ Mastodon Installation Complete!" echo "==========================================" echo "" echo "Domain: $DOMAIN" echo "Admin User: $ADMIN_USER" echo "Admin Email: $ADMIN_EMAIL" echo "Admin Password: "REDACTED_PASSWORD" echo "" echo "Listening on port 3000 (HTTP)" echo "" echo "Next steps:" echo "1. Configure your reverse proxy to forward HTTPS to port 3000" echo "2. Login and change your password" echo "3. Configure instance settings in Administration panel" echo ""