How to install Mastodon from source on Ubuntu 24.04 or Debian 13
Install a Mastodon server from source as the official docs describe: Ruby, Node.js, PostgreSQL, Redis, Nginx with Certbot, systemd units, backups and upgrades.
- Advanced
- 90 min read
- Updated
Tested on: Ubuntu 24.04 LTS, Debian 13
This guide is not available in your language yet, so it is shown in English.
On this page
- Prerequisites
- Step 1 — Update the server and open the firewall
- Step 2 — Add the Node.js and PostgreSQL repositories
- Step 3 — Install the system packages and create the mastodon user
- Step 4 — Create the PostgreSQL user
- Step 5 — Get the code and install Ruby
- Step 6 — Install the Ruby and JavaScript dependencies
- Step 7 — Run the setup wizard
- Step 8 — Get a certificate and configure Nginx
- Step 9 — Start the Mastodon services
- Step 10 — Finish the admin setup and schedule media cleanup
- Back up and restore
- Update Mastodon
- Troubleshooting
- The browser shows the error page with the elephant
- Pages load without styles or images
- Confirmation emails never arrive
- The disk fills up
- bundle install fails after an upgrade with a Ruby version error
- Next steps
Mastodon is a decentralised social network: each server runs its own community and federates with thousands of others over ActivityPub. This guide installs Mastodon from source, the way the official documentation describes it, on Ubuntu 24.04 LTS or Debian 13: Node.js and PostgreSQL from their upstream repositories, Ruby through rbenv, Redis, Nginx with a Let's Encrypt certificate from Certbot, and the three systemd services that run the web app, the background workers and the streaming API. You then create the owner account, schedule media cleanup, and set up backups and upgrades.
Prerequisites
- A fresh server running Ubuntu 24.04 LTS or Debian 13. These are the two releases the official guide targets; Ubuntu 26.04 and Debian 12 are not covered.
- A non-root user with
sudorights and SSH key login: see Secure a new Linux server and Set up SSH keys. - A domain such as
social.example.comwith an A record (and AAAA record if you use IPv6) pointing to the server. - SMTP credentials from an email provider: host, port, user name, password and a sender address.
- Optional: an S3-compatible bucket for media, for example from Self-hosted S3 storage.
The Mastodon project does not publish minimum requirements. The figures below are a conservative starting point for a small instance, not official numbers:
| Resource | Minimum (official) | Suggested starting point |
|---|---|---|
| CPU | Not published | 2 vCPU |
| RAM | Not published | 4 GB, because asset compilation and Sidekiq need memory |
| Disk | Not published | 40 GB SSD plus media, or object storage for media |
| Software | Ruby 3.3+, PostgreSQL 14+, Redis 7.0+, Node.js 22+ (Mastodon 4.7) | The versions the steps below install |
Step 1 — Update the server and open the firewall
Open a root shell, install updates and allow only SSH and web traffic. The official prerequisites use iptables rules for the same ports; ufw gives the same result with less typing:
sudo -i
apt update && apt upgrade -y
apt install -y ufw
ufw allow OpenSSH
ufw allow 80/tcp
ufw allow 443/tcp
ufw enable
ufw status verboseStep 2 — Add the Node.js and PostgreSQL repositories
Mastodon's guide installs Node.js 24 from NodeSource and PostgreSQL from the PostgreSQL project's own apt repository:
apt install -y curl wget gnupg lsb-release ca-certificates
curl -fsSL https://deb.nodesource.com/gpgkey/nodesource-repo.gpg.key | gpg --dearmor -o /etc/apt/keyrings/nodesource.gpg
echo "deb [signed-by=/etc/apt/keyrings/nodesource.gpg] https://deb.nodesource.com/node_24.x nodistro main" | tee /etc/apt/sources.list.d/nodesource.list
wget -O /usr/share/keyrings/postgresql.asc https://www.postgresql.org/media/keys/ACCC4CF8.asc
echo "deb [signed-by=/usr/share/keyrings/postgresql.asc] http://apt.postgresql.org/pub/repos/apt $(lsb_release -cs)-pgdg main" > /etc/apt/sources.list.d/postgresql.listStep 3 — Install the system packages and create the mastodon user
Install the build tools, media libraries, Nginx, Node.js, Redis, PostgreSQL and Certbot in one command, enable corepack (which provides Yarn), and create the unprivileged mastodon user that will own the code:
apt update
apt install -y imagemagick ffmpeg libvips-tools libpq-dev libxslt1-dev file git \
protobuf-compiler pkg-config autoconf bison build-essential \
libssl-dev libyaml-dev libreadline-dev zlib1g-dev libffi-dev libgdbm-dev \
nginx nodejs redis-server postgresql certbot python3-certbot-nginx \
libidn-dev libicu-dev libjemalloc-dev
corepack enable
adduser --disabled-password mastodon
node -vnode -v should print a v24 version. adduser asks for optional user details; press Enter to skip them.
Step 4 — Create the PostgreSQL user
Mastodon connects to PostgreSQL over the local socket as the Linux user of the same name, so the database user needs no password:
sudo -u postgres psqlCREATE USER mastodon CREATEDB;
\qFor larger instances the guide suggests tuning PostgreSQL with values from pgTune in /etc/postgresql/18/main/postgresql.conf and restarting PostgreSQL. A small server runs fine with the defaults.
Step 5 — Get the code and install Ruby
Switch to the mastodon user, clone the repository, check out the latest stable release tag, and install the Ruby version the release asks for with rbenv:
su - mastodon
git clone https://github.com/mastodon/mastodon.git live && cd live
git checkout $(git tag -l | grep '^v[0-9.]*$' | sort -V | tail -n 1)
git clone https://github.com/rbenv/rbenv.git ~/.rbenv
echo 'export PATH="$HOME/.rbenv/bin:$PATH"' >> ~/.bashrc
echo 'eval "$(rbenv init -)"' >> ~/.bashrc
source ~/.bashrc
git clone https://github.com/rbenv/ruby-build.git "$(rbenv root)"/plugins/ruby-build
RUBY_CONFIGURE_OPTS=--with-jemalloc rbenv install
ruby -vrbenv install reads the version from the .ruby-version file in the repository and compiles Ruby with jemalloc, which takes several minutes. ruby -v should print the same version.
Step 6 — Install the Ruby and JavaScript dependencies
Still as the mastodon user, in ~/live:
bundle config deployment 'true'
bundle config without 'development test'
bundle install
yarn installThe two bundle config lines are only needed on the first install. Both installs take a while; errors here usually mean a system package from Step 3 is missing.
Step 7 — Run the setup wizard
The interactive wizard writes .env.production, generates all secrets and encryption keys, creates the database schema, compiles the assets and can create the first admin account:
RAILS_ENV=production bin/rails mastodon:setupAnswer the questions like this:
| Question | Answer for this guide |
|---|---|
| Domain name | social.example.com; this becomes LOCAL_DOMAIN |
| Single user mode | No, unless the server is only for you |
| Are you using Docker | No |
| PostgreSQL host, port, database, user, password | Accept the defaults: /var/run/postgresql, 5432, mastodon_production, mastodon, empty password |
| Redis host, port, password | Accept the defaults: localhost, 6379, empty password |
| Store uploaded files on the cloud | No for local storage; Yes to enter your S3-compatible bucket |
| Send e-mails from localhost | No; then enter your SMTP server, for example smtp.example.com, port 587, user name, password and sender address |
| Send a test e-mail | Yes, to check SMTP now |
| Save configuration, prepare the database, compile assets | Yes |
| Create an admin user | Yes; choose a user name and email, then copy the password the wizard prints |
When the wizard has finished, return to the root shell:
exitStep 8 — Get a certificate and configure Nginx
Request a certificate with Certbot's Nginx plugin, then install Mastodon's Nginx configuration from the repository:
certbot certonly --nginx -d social.example.com
cp /home/mastodon/live/dist/nginx.conf /etc/nginx/sites-available/mastodon
ln -s /etc/nginx/sites-available/mastodon /etc/nginx/sites-enabled/mastodon
rm /etc/nginx/sites-enabled/default
sed -i 's/example\.com/social.example.com/g' /etc/nginx/sites-available/mastodon
nano /etc/nginx/sites-available/mastodonThe sed command replaces the placeholder domain in the template with yours; use your real domain in place of social.example.com. In the editor, find the two commented certificate lines and remove the leading # so they read:
ssl_certificate /etc/letsencrypt/live/social.example.com/fullchain.pem;
ssl_certificate_key /etc/letsencrypt/live/social.example.com/privkey.pem;Let Nginx read the asset files in the mastodon home directory, test the configuration and restart Nginx:
chmod o+x /home/mastodon
nginx -t
systemctl restart nginxOpening https://social.example.com now shows Mastodon's error page with the elephant, because the Mastodon processes are not running yet. Certbot's package renews the certificate automatically; certbot renew --dry-run tests the renewal.
Step 9 — Start the Mastodon services
The repository ships systemd units for the web app (Puma), the background workers (Sidekiq) and the streaming API. They assume the mastodon user and /home/mastodon/live, which this guide uses:
cp /home/mastodon/live/dist/mastodon-*.service /etc/systemd/system/
systemctl daemon-reload
systemctl enable --now mastodon-web mastodon-sidekiq mastodon-streaming
systemctl status mastodon-web mastodon-sidekiq mastodon-streaming --no-pagerAll three should be active (running). Open https://social.example.com and log in with the admin account from the wizard, then change its password under Preferences > Account.
Step 10 — Finish the admin setup and schedule media cleanup
If you skipped the admin account in the wizard, create the owner account with tootctl. Mastodon's setup guide notes that browser registration is disabled by default, so this is the way to create the first account:
su - mastodon
cd live
RAILS_ENV=production bin/tootctl accounts create alice --email [email protected] --confirmed --role Owner
RAILS_ENV=production bin/tootctl accounts modify alice --approve
exitThe first command prints a random password. Then go to Preferences > Administration > Server Settings and fill in the contact user name, a business email, the server description and the rules or code of conduct. Review the registration mode in the same area before you open sign-ups; requiring approval lets you check each new account.
Mastodon caches media and link previews from other servers, and the cache grows every day. tootctl media remove deletes cached remote attachments older than a number of days (7 by default), and tootctl preview_cards remove deletes link preview thumbnails (180 days by default). Schedule both as the mastodon user:
su - mastodon
crontab -ePATH=/home/mastodon/.rbenv/shims:/home/mastodon/.rbenv/bin:/usr/local/bin:/usr/bin:/bin
RAILS_ENV=production
15 3 * * * cd /home/mastodon/live && bin/tootctl media remove --days 7
45 3 * * 0 cd /home/mastodon/live && bin/tootctl preview_cards remove --days 180Save, leave the editor and run exit. The PATH line lets cron find the Ruby version installed with rbenv. RAILS_ENV=production bin/tootctl media usage shows how much space media uses.
Back up and restore
Mastodon's backup guide lists four things, in order of importance: the PostgreSQL database (losing it means losing the whole server), the .env.production file with the secrets (losing it logs everyone out and breaks two-factor authentication), user-uploaded files in public/system when you use local storage, and the Redis database, which is the least critical. Run these commands in the root shell:
mkdir -p /opt/backups
chmod 700 /opt/backups
sudo -u mastodon pg_dump -Fc mastodon_production > /opt/backups/mastodon-db-$(date +%F).dump
cp /home/mastodon/live/.env.production /opt/backups/mastodon-env-$(date +%F)
redis-cli SAVE
cp /var/lib/redis/dump.rdb /opt/backups/mastodon-redis-$(date +%F).rdb
tar czf /opt/backups/mastodon-system-$(date +%F).tar.gz --exclude=system/cache -C /home/mastodon/live public/system
ls -lh /opt/backupsThe --exclude=system/cache option skips media cached from other servers, which Mastodon can fetch again. With object storage, back up the bucket with your provider's tools instead of public/system. The best backups are off-site, so copy these files to another machine.
To restore on a new server, follow Steps 1 to 6 with the same version tag, then Step 8 and the cp and daemon-reload lines of Step 9 without starting the services. Do not run the setup wizard. Then load the backups as root:
systemctl stop 'mastodon-*.service'
sudo -u mastodon createdb -T template0 mastodon_production
sudo -u mastodon pg_restore -Fc -U mastodon -n public --no-owner --role=mastodon -d mastodon_production < /opt/backups/mastodon-db-2026-10-09.dump
cp /opt/backups/mastodon-env-2026-10-09 /home/mastodon/live/.env.production
chown mastodon:mastodon /home/mastodon/live/.env.production
tar xzf /opt/backups/mastodon-system-2026-10-09.tar.gz -C /home/mastodon/live
chown -R mastodon:mastodon /home/mastodon/live/public/system
systemctl stop redis-server
cp /opt/backups/mastodon-redis-2026-10-09.rdb /var/lib/redis/dump.rdb
chown redis:redis /var/lib/redis/dump.rdb
systemctl start redis-serverFinally rebuild the assets and home feeds, start the services and restart Nginx:
su - mastodon
cd live
RAILS_ENV=production bundle exec rails assets:precompile
RAILS_ENV=production ./bin/tootctl feeds build
exit
systemctl enable --now mastodon-web mastodon-sidekiq mastodon-streaming
systemctl restart nginxUpdate Mastodon
Every Mastodon release on GitHub has its own upgrade instructions, and the order of steps matters. Mastodon's upgrade guide says you may skip patch releases, but you should deploy at least one release from each minor series and run every instruction at least once. Read the notes for each version between yours and the target, and take a backup first.
The steps below follow the non-Docker instructions of the 4.7 releases. Replace v4.7.3 with the release you are upgrading to:
su - mastodon
cd live
git fetch --tags
git checkout v4.7.3
bundle install
yarn install --immutable
RAILS_ENV=production bundle exec rails assets:precompile
SKIP_POST_DEPLOYMENT_MIGRATIONS=true RAILS_ENV=production bundle exec rails db:migrate
exitRestart the processes, then run the remaining post-deployment migrations:
systemctl restart mastodon-sidekiq
systemctl reload mastodon-web
systemctl restart mastodon-streaming
su - mastodon
cd live
RAILS_ENV=production bundle exec rails db:migrate
exitsystemctl reload mastodon-web performs a phased restart without downtime; restarting the streaming service disconnects clients briefly. When the release notes ask for a new Ruby version, update ruby-build with git -C ~/.rbenv/plugins/ruby-build pull and run RUBY_CONFIGURE_OPTS=--with-jemalloc rbenv install in ~/live before bundle install.
Troubleshooting
The browser shows the error page with the elephant
Nginx works, but Puma does not answer. Check systemctl status mastodon-web and journalctl -u mastodon-web -n 50 --no-pager. A wrong path or Ruby version in the unit file, or a failed asset compilation, are the usual causes.
Pages load without styles or images
Nginx cannot read the compiled assets. Run chmod o+x /home/mastodon again, and if the files are missing, rebuild them as the mastodon user with RAILS_ENV=production bundle exec rails assets:precompile.
Confirmation emails never arrive
Mail is sent by Sidekiq. Read journalctl -u mastodon-sidekiq -n 100 --no-pager for SMTP errors, check the SMTP_ values in .env.production, and restart all three Mastodon services after every change to that file. SMTP_PORT should be 587, the relay port from Step 7.
The disk fills up
Run RAILS_ENV=production bin/tootctl media usage as the mastodon user to see where the space goes. Make sure the cron jobs from Step 10 run (grep CRON /var/log/syslog on Ubuntu, journalctl -u cron on Debian), lower --days if needed, or move media to object storage.
bundle install fails after an upgrade with a Ruby version error
The release needs a Ruby version that is not installed yet. Update ruby-build, run RUBY_CONFIGURE_OPTS=--with-jemalloc rbenv install in ~/live, then repeat bundle install and the remaining upgrade steps.
Next steps
- Put object storage behind your media with Self-hosted S3 storage.
- Learn more about the web server used here in Nginx with Certbot.
- Add a community forum next to your instance with Discourse, or a private chat with Matrix Synapse.
- Read the official Mastodon admin documentation for Elasticsearch full-text search, scaling and moderation.
- Compare servers for community platforms on the social network hosting page.
Frequently asked questions
Can I change my Mastodon domain later?
No. Mastodon's configuration docs say LOCAL_DOMAIN and WEB_DOMAIN cannot be safely changed once set: remote servers would treat your accounts as new ones and communication with them can break. Even a reinstall does not fix it, so decide before you run the setup wizard.
Do I need an email server?
Yes. Mastodon sends confirmation links, password resets and notifications through the SMTP server you configure. Outbound port 25 is closed by default on HyperDC VPS. For services bought for a term of 3 months or longer, it is opened on request through a support ticket. Until then, send mail through an SMTP relay on port 587.
Why does this guide use Nginx instead of Caddy?
The Mastodon repository ships an Nginx configuration for its web, streaming and static file routes, and the official installation guide uses it with Certbot. Staying with it keeps your server close to the documentation and to the release notes.
Should I store media in object storage?
It is optional. Local storage keeps uploads and cached remote media in public/system on the server. S3-compatible object storage moves them off the machine; the setup wizard can configure it, and the bucket must support ACLs according to Mastodon's configuration docs.
Can I run Mastodon with Docker instead?
The official installation guide documents the from-source install on Ubuntu 24.04 or Debian 13 and does not describe a Docker deployment, so this guide follows the source install. Its systemd services, tootctl commands and release notes all assume this layout.
Which HyperDC servers can run Mastodon?
A HyperDC Linux VPS, VDS or dedicated server with root access running Ubuntu 24.04 LTS or Debian 13. Plan disk space for media, because files cached from other servers keep growing unless you clean them up.