Skip to content

Mastodon 4.6.0 Upgrade — MajorToot

Readiness assessment and manual upgrade runbook for toot.majorshouse.com (Hetzner CPX31, Ubuntu 24.04). Upgrading from 4.5.11 → 4.6.0 — a clean single-step upgrade. Verified live on majortoot-hetzner 2026-06-17.

Status

This 4.5.11 → 4.6.0 upgrade was completed on 2026-06-17. The instance has since tracked the 4.6.x patch line and currently runs 4.6.2 — treat the version floors below as the baseline this runbook was written against; the pre/post-deploy migration ordering applies to any 4.5 → 4.6 major bump.

Access

Management is via the ansible user with ~/.ssh/id_ansible (ssh ansible@<tailscale-ip>, or ssh majortoot once the alias is corrected — see server doc 2026-06-17 entry). The ansible user has sudo. Run all tootctl/rails commands as the mastodon user.

Readiness — all dependency floors met (live-verified 2026-06-17)

Requirement (4.6.0) On majortoot Status
Ruby ≥ 3.3 3.4.7 (rbenv, +PRISM) ✅
Node ≥ 22 v24.11.0 (nvm) ✅
FFmpeg ≥ 5.1 6.1.1 (apt) ✅
libvips ≥ 8.13 (ImageMagick removed) libvips42t64 8.15.1 ✅ already in use
PostgreSQL ≥ 14 17.x (PGDG) ✅
Redis ≥ 7.0 7.0.15 ✅
Disk free for pg_dump 124 GB free (15% used) ✅

Key point on libvips: 4.6.0 removes ImageMagick entirely and ignores MASTODON_USE_LIBVIPS=false. MajorToot already runs on libvips 8.15.1 (MASTODON_USE_LIBVIPS unset), so this is a non-event. The original Hetzner provisioning dep list (cutover plan / migration brief) named imagemagick and omitted libvips, but libvips came in transitively and is what Mastodon actually uses.

What does NOT affect MajorToot

  • Custom mute system (mastodon_mute.py / claude_mute_token) — uses the account-mute REST endpoints, unchanged in 4.6.0. The only mute-adjacent change is an expire_at→expires_at rename in muted-words/filter CSV exports, which this tooling doesn't parse.
  • S3 fix from 2026-06-01 — S3_PERMISSION= empty and S3_ACL commented (BucketOwnerEnforced). 4.6.0 doesn't change S3 behavior; the fix holds. Confirmed still set.
  • Invite permission split (invite_bypass_approval) — solo instance, irrelevant.
  • Email subscriptions — admin-gated + per-user opt-in; can hard-disable with DISABLE_EMAIL_SUBSCRIPTIONS=true if outbound mail volume ever matters.
  • ENABLE_SIDEKIQ_UNIQUE_JOBS_UI (removed in 4.6.0) — not set. ✅

Watch items (non-blocking)

  • nginx vhost: MajorToot hand-maintains /etc/nginx/sites-available/mastodon (bad-bot UA stanza, fail2ban). 4.6.0 changes the default sample (location blocks #19644/#37866, proxy_read_timeout → 120s). An upgrade won't touch the custom vhost — but diff it against the new dist/nginx.conf for the changed location blocks. The UA block stays.
  • Custom themes would break (theming rebuilt) — MajorToot runs no custom theme.

Manual upgrade runbook

Run as the mastodon user unless noted. Solo instance, so no maintenance window needed; old DO droplet remains as a fallback.

# 1. Back up the DB FIRST (as postgres or via sudo)
pg_dump -Fc mastodon_production > ~/mastodon-pre-4.6.0.dump

# 2. Get the code (as mastodon user)
cd /home/mastodon/live
git fetch && git checkout v4.6.0

# 3. Install deps
bundle install -j$(getconf _NPROCESSORS_ONLN)
yarn install --immutable
#   If charlock_holmes fails to build on recent gcc:
#   BUNDLE_BUILD__CHARLOCK_HOLMES="--with-cxxflags=-std=c++17" bundle install

# 4. Precompile assets
RAILS_ENV=production bundle exec rails assets:precompile

# 5. PRE-deployment migrations
SKIP_POST_DEPLOYMENT_MIGRATIONS=true RAILS_ENV=production bundle exec rails db:migrate

# 6. Restart services (confirm Sidekiq drained first)
sudo systemctl restart mastodon-web mastodon-sidekiq mastodon-streaming

# 7. POST-deployment migrations
RAILS_ENV=production bundle exec rails db:migrate

Migration ordering matters

Pre-deploy migrations (step 5) → restart (step 6) → post-deploy migrations (step 7). Do not collapse them.

Post-upgrade verification

# Site loads
curl -I https://toot.majorshouse.com
# Version reports 4.6.0
curl -s https://toot.majorshouse.com/api/v2/instance | python3 -c "import sys,json;print(json.load(sys.stdin)['version'])"
# Sidekiq clean
journalctl -u mastodon-sidekiq -f | grep -i "error\|fail\|retry"
Then send a test toot and confirm it federates. Confirm avatars/media still serve (S3 write path).

Rollback

Restore the pre-upgrade dump and git checkout v4.5.11, or fall back to the still-running old DO droplet by reverting DNS if it hasn't been destroyed.