
How to Install Mastodon on Ubuntu 24
Mastodon is an open-source, decentralized social networking platform that’s part of the ActivityPub-based “fediverse.” Unlike traditional centralized social media platforms, Mastodon allows you to run your own instance while still connecting with users across thousands of other servers. Setting up your own Mastodon instance on Ubuntu 24 gives you complete control over your community’s data, moderation policies, and feature set. This guide will walk you through the entire installation process, from system preparation to your first toot, including common troubleshooting scenarios that’ll save you hours of debugging.
How Mastodon Works Under the Hood
Mastodon is built on Ruby on Rails with a React-based frontend, using PostgreSQL for data storage and Redis for caching and real-time features. The architecture consists of several components working together:
- Web process: Handles HTTP requests and serves the web interface
- Streaming API: Manages real-time updates via WebSockets
- Sidekiq workers: Process background jobs like federation, email sending, and media processing
- PostgreSQL: Stores all application data including posts, users, and relationships
- Redis: Handles caching, session storage, and job queues
- Elasticsearch: Powers full-text search capabilities (optional but recommended)
The federation aspect works through ActivityPub, allowing your instance to communicate with other Mastodon servers and compatible platforms like PeerTube, Pixelfed, and Pleroma. When a user on your instance follows someone on another server, your instance subscribes to updates from that remote server.
System Requirements and Prerequisites
Before diving into the installation, let’s cover what you’ll need. Mastodon isn’t particularly lightweight, especially if you’re planning to federate with many instances.
Component | Minimum | Recommended | Notes |
---|---|---|---|
RAM | 2GB | 4GB+ | More needed for larger instances |
CPU | 1 core | 2+ cores | Background jobs benefit from multiple cores |
Storage | 10GB | 50GB+ | Media files grow quickly |
Bandwidth | Unmetered | Unmetered | Federation generates significant traffic |
You’ll also need a domain name pointed to your server and preferably an email service for user notifications. Let’s get started with a fresh Ubuntu 24.04 server.
Step-by-Step Installation Guide
Step 1: System Updates and Basic Dependencies
First, update your system and install essential packages:
sudo apt update && sudo apt upgrade -y
sudo apt install -y curl wget gnupg apt-transport-https lsb-release ca-certificates
Step 2: Install Node.js and Yarn
Mastodon requires Node.js 18+ and Yarn for frontend asset compilation:
# Install Node.js 20.x
curl -fsSL https://deb.nodesource.com/setup_20.x | sudo -E bash -
sudo apt install -y nodejs
# Install Yarn
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
sudo apt update && sudo apt install -y yarn
Step 3: Install and Configure PostgreSQL
PostgreSQL will store all your Mastodon data:
sudo apt install -y postgresql postgresql-contrib libpq-dev
sudo -u postgres createuser --createdb mastodon
sudo -u postgres psql
In the PostgreSQL prompt, set up the database:
\password mastodon
CREATE DATABASE mastodon_production OWNER mastodon;
\q
Step 4: Install Redis
Redis handles caching and background job queues:
sudo apt install -y redis-server
sudo systemctl enable redis-server
sudo systemctl start redis-server
Step 5: Install Ruby via rbenv
Mastodon requires a specific Ruby version. Using rbenv gives you better version control:
# Create mastodon user
sudo adduser --disabled-login mastodon
# Switch to mastodon user
sudo su - mastodon
# Install rbenv
git clone https://github.com/rbenv/rbenv.git ~/.rbenv
echo 'export PATH="$HOME/.rbenv/bin:$PATH"' >> ~/.bashrc
echo 'eval "$(rbenv init -)"' >> ~/.bashrc
exec bash
# Install ruby-build
git clone https://github.com/rbenv/ruby-build.git ~/.rbenv/plugins/ruby-build
# Install Ruby 3.2.2 (check Mastodon docs for current version)
rbenv install 3.2.2
rbenv global 3.2.2
# Install bundler
gem install bundler --no-document
Step 6: Download and Configure Mastodon
Still as the mastodon user:
cd ~
git clone https://github.com/mastodon/mastodon.git live
cd live
git checkout $(git tag -l | grep -v 'rc[0-9]*$' | sort -V | tail -n 1)
Install Ruby and Node.js dependencies:
bundle config deployment 'true'
bundle config without 'development test'
bundle install -j$(getconf _NPROCESSORS_ONLN)
yarn install --pure-lockfile
Step 7: Configure Mastodon
Generate the configuration file:
RAILS_ENV=production bundle exec rake mastodon:setup
This interactive setup will ask for:
- Your domain name
- Database connection details
- Redis connection details
- Email configuration (SMTP settings)
- File storage preferences (local vs. cloud)
The setup will create a .env.production
file with your configuration. Here’s what a typical configuration looks like:
# Domain and URLs
LOCAL_DOMAIN=your-domain.com
WEB_DOMAIN=your-domain.com
# Database
DB_HOST=localhost
DB_PORT=5432
DB_NAME=mastodon_production
DB_USER=mastodon
DB_PASS=your_password
# Redis
REDIS_HOST=localhost
REDIS_PORT=6379
# SMTP
SMTP_SERVER=smtp.your-provider.com
SMTP_PORT=587
SMTP_LOGIN=your@email.com
SMTP_PASSWORD=your_smtp_password
SMTP_FROM_ADDRESS=notifications@your-domain.com
# Security keys (generated by setup)
SECRET_KEY_BASE=very_long_random_string
OTP_SECRET=another_long_random_string
Step 8: Set Up Systemd Services
Exit back to your regular user account and create systemd service files. First, the web service:
sudo nano /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
[Install]
WantedBy=multi-user.target
Create the streaming service:
sudo nano /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
[Install]
WantedBy=multi-user.target
Create the Sidekiq service for background jobs:
sudo nano /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
[Install]
WantedBy=multi-user.target
Step 9: Configure Nginx
Install Nginx and set up the reverse proxy:
sudo apt install -y nginx
sudo nano /etc/nginx/sites-available/mastodon
Here’s a production-ready Nginx configuration:
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 clean_time=30d levels=2 keys_zone=mastodon:10m max_size=1g;
server {
listen 80;
listen [::]:80;
server_name your-domain.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 your-domain.com;
ssl_protocols TLSv1.2 TLSv1.3;
ssl_ciphers HIGH:!MEDIUM:!LOW:!aNULL:!NULL:!SHA;
ssl_prefer_server_ciphers on;
ssl_session_cache shared:SSL:10m;
ssl_session_tickets off;
# SSL certificates (configure after getting Let's Encrypt certs)
ssl_certificate /etc/letsencrypt/live/your-domain.com/fullchain.pem;
ssl_certificate_key /etc/letsencrypt/live/your-domain.com/privkey.pem;
keepalive_timeout 70;
sendfile on;
client_max_body_size 99m;
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;
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 mastodon;
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;
}
Enable the site and create the cache directory:
sudo ln -s /etc/nginx/sites-available/mastodon /etc/nginx/sites-enabled/
sudo mkdir -p /var/cache/nginx/mastodon
sudo chown www-data:www-data /var/cache/nginx/mastodon
sudo nginx -t
Step 10: SSL Certificate Setup
Install Certbot for Let’s Encrypt SSL certificates:
sudo apt install -y certbot python3-certbot-nginx
sudo certbot --nginx -d your-domain.com
Set up automatic renewal:
sudo crontab -e
# Add this line:
0 12 * * * /usr/bin/certbot renew --quiet
Step 11: Start All Services
Enable and start all Mastodon services:
sudo systemctl daemon-reload
sudo systemctl enable mastodon-web mastodon-streaming mastodon-sidekiq nginx
sudo systemctl start mastodon-web mastodon-streaming mastodon-sidekiq nginx
Check that everything is running:
sudo systemctl status mastodon-web mastodon-streaming mastodon-sidekiq nginx
Creating Your First Admin User
Before you can use your instance, create an admin account:
sudo su - mastodon
cd live
RAILS_ENV=production bin/tootctl accounts create yourusername --email your@email.com --confirmed --role Owner
This will output a temporary password. You can reset it through the web interface once you log in.
Common Issues and Troubleshooting
Memory Issues
If you’re running on a smaller VPS, you might encounter memory issues. Here are some optimization strategies:
# Reduce Sidekiq concurrency in .env.production
DB_POOL=5
# And update the systemd service to match
You can also add swap space:
sudo fallocate -l 2G /swapfile
sudo chmod 600 /swapfile
sudo mkswap /swapfile
sudo swapon /swapfile
echo '/swapfile none swap sw 0 0' | sudo tee -a /etc/fstab
Federation Issues
If federation isn’t working, check these common issues:
- Ensure your domain’s SSL certificate is valid and accessible
- Check that the streaming service is running and accessible
- Verify firewall settings allow outbound HTTPS connections
- Review Sidekiq logs for federation job failures
# Check federation status
sudo su - mastodon
cd live
RAILS_ENV=production bin/tootctl self-destruct --dry-run
Performance Monitoring
Monitor your instance performance with these commands:
# Check service status
systemctl status mastodon-*
# Monitor resource usage
htop
# Check database performance
sudo -u postgres psql mastodon_production -c "SELECT schemaname,tablename,attname,n_distinct,correlation FROM pg_stats WHERE tablename = 'statuses';"
# Monitor Sidekiq queues
sudo su - mastodon
cd live
RAILS_ENV=production bin/tootctl statuses count
Comparison with Alternative Platforms
Here’s how Mastodon stacks up against other fediverse platforms:
Platform | Language | Resource Usage | Features | Best For |
---|---|---|---|---|
Mastodon | Ruby/Rails | High | Full-featured, Twitter-like | Large communities |
Pleroma | Elixir | Low | Lightweight, customizable | Small instances |
Misskey | Node.js | Medium | Unique UI, reactions | Creative communities |
GoToSocial | Go | Very Low | API-focused | Personal instances |
Best Practices and Security Considerations
Security Hardening
Implement these security measures for production:
# Enable firewall
sudo ufw allow ssh
sudo ufw allow http
sudo ufw allow https
sudo ufw enable
# Set up fail2ban
sudo apt install -y fail2ban
sudo systemctl enable fail2ban
Create a fail2ban configuration for Mastodon:
sudo nano /etc/fail2ban/jail.local
[mastodon]
enabled = true
port = http,https
filter = mastodon
logpath = /home/mastodon/live/log/production.log
maxretry = 5
bantime = 3600
Backup Strategy
Implement regular backups:
#!/bin/bash
# backup-mastodon.sh
DATE=$(date +%Y%m%d_%H%M%S)
BACKUP_DIR="/home/backups/mastodon"
# Database backup
sudo -u postgres pg_dump mastodon_production > $BACKUP_DIR/db_$DATE.sql
# Media files backup
tar -czf $BACKUP_DIR/media_$DATE.tar.gz /home/mastodon/live/public/system
# Configuration backup
cp /home/mastodon/live/.env.production $BACKUP_DIR/env_$DATE.backup
# Clean old backups (keep 7 days)
find $BACKUP_DIR -name "*.sql" -mtime +7 -delete
find $BACKUP_DIR -name "*.tar.gz" -mtime +7 -delete
Monitoring and Maintenance
Set up log rotation and monitoring:
# Add to /etc/logrotate.d/mastodon
/home/mastodon/live/log/*.log {
daily
missingok
rotate 30
compress
delaycompress
notifempty
copytruncate
su mastodon mastodon
}
Real-World Use Cases and Performance Data
Based on real deployments, here’s what you can expect:
- Small instance (10-50 users): 2GB RAM, 1 CPU core, ~500MB daily federation traffic
- Medium instance (100-500 users): 4GB RAM, 2 CPU cores, ~2GB daily federation traffic
- Large instance (1000+ users): 8GB+ RAM, 4+ CPU cores, ~10GB+ daily federation traffic
A typical small instance federating with major instances like mastodon.social will see:
- ~50,000 federation jobs per day
- Database growing by ~100MB per month
- Media storage growing by ~1GB per month (with media retention policies)
Performance optimization becomes crucial as you grow. Consider using external media storage like S3-compatible services and implementing CDN caching for static assets.
For official documentation and updates, check the Mastodon documentation and the GitHub repository. The community also maintains helpful resources at Fediverse.info for broader ecosystem information.
Your Mastodon instance should now be running and ready to federate with the broader fediverse. Remember that running a social media instance is an ongoing commitment that requires regular updates, moderation, and community management. Start small, learn the ropes, and scale up as your community grows.

This article incorporates information and material from various online sources. We acknowledge and appreciate the work of all original authors, publishers, and websites. While every effort has been made to appropriately credit the source material, any unintentional oversight or omission does not constitute a copyright infringement. All trademarks, logos, and images mentioned are the property of their respective owners. If you believe that any content used in this article infringes upon your copyright, please contact us immediately for review and prompt action.
This article is intended for informational and educational purposes only and does not infringe on the rights of the copyright owners. If any copyrighted material has been used without proper credit or in violation of copyright laws, it is unintentional and we will rectify it promptly upon notification. Please note that the republishing, redistribution, or reproduction of part or all of the contents in any form is prohibited without express written permission from the author and website owner. For permissions or further inquiries, please contact us.