Files
homelab-optimized/docs/services/mastodon/install-baremetal.sh
Gitea Mirror Bot fb00a325d1
Some checks failed
Documentation / Build Docusaurus (push) Failing after 5m14s
Documentation / Deploy to GitHub Pages (push) Has been skipped
Sanitized mirror from private repository - 2026-04-18 11:19:59 UTC
2026-04-18 11:19:59 +00:00

341 lines
10 KiB
Bash
Executable File

#!/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 ""