A source-based Mastodon install runs the Rails web app, streaming server, Sidekiq workers, PostgreSQL, Redis, and Nginx directly on the host. Use it when the server should follow upstream source releases without Docker Compose and the operator is ready to maintain Ruby, Node.js, systemd units, and reverse-proxy files.
The upstream source path uses NodeSource for Node.js, PGDG for PostgreSQL, Corepack for Yarn, and rbenv for the Ruby version declared by the checked-out Mastodon release. Keeping the default Mastodon user, application directory, and service template layout avoids manual path edits when the Nginx and systemd templates are installed.
Finish DNS, SMTP, and certificate planning before running the setup wizard because LOCAL_DOMAIN and mail settings are written into Mastodon's production environment file and affect federation and account recovery. A completed install has the three Mastodon systemd services active, Nginx proxying HTTPS traffic, and the public instance API returning 200 OK.
Related: How to install Mastodon with Docker
Related: How to configure a Mastodon domain
Related: How to configure Mastodon SMTP email
Related: How to create a Mastodon admin user
Steps to install Mastodon from source:
- Open a terminal with sudo privileges.
The upstream source install currently targets Ubuntu 24.04 or Debian 13. Use a host in that family for the repository commands in this flow.
- Refresh the package index.
$ sudo apt update
- Install the repository helper packages.
$ sudo apt install --assume-yes curl wget gnupg lsb-release ca-certificates
- Add the NodeSource signing key.
$ curl -fsSL https://deb.nodesource.com/gpgkey/nodesource-repo.gpg.key | sudo gpg --dearmor -o /etc/apt/keyrings/nodesource.gpg
- Add the Node.js 24 package repository.
$ echo "deb [signed-by=/etc/apt/keyrings/nodesource.gpg] https://deb.nodesource.com/node_24.x nodistro main" | sudo tee /etc/apt/sources.list.d/nodesource.list deb [signed-by=/etc/apt/keyrings/nodesource.gpg] https://deb.nodesource.com/node_24.x nodistro main
Mastodon 4.6 requires Node.js 22 or newer. The upstream install path currently uses the NodeSource 24.x repository so Corepack can install the Yarn version pinned in package.json.
- Add the PostgreSQL repository signing key.
$ sudo wget -O /usr/share/keyrings/postgresql.asc https://www.postgresql.org/media/keys/ACCC4CF8.asc ##### snipped #####
- Add the PGDG PostgreSQL package repository.
$ echo "deb [signed-by=/usr/share/keyrings/postgresql.asc] http://apt.postgresql.org/pub/repos/apt $(lsb_release -cs)-pgdg main" | sudo tee /etc/apt/sources.list.d/postgresql.list deb [signed-by=/usr/share/keyrings/postgresql.asc] http://apt.postgresql.org/pub/repos/apt noble-pgdg main
The shown output is for Ubuntu 24.04. On Debian 13, lsb_release -cs should expand to the Debian codename.
- Refresh the package index with the new repositories.
$ sudo apt update
- Install Mastodon runtime and build packages.
$ sudo apt install --assume-yes \ 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
Mastodon 4.6 uses libvips for media processing. Older ImageMagick-only install notes do not match the current release line.
- Enable Corepack for the pinned Yarn version.
$ sudo corepack enable
- Create the Mastodon system user.
$ sudo adduser --disabled-password mastodon
- Create the PostgreSQL role used by Mastodon.
$ sudo -u postgres createuser --createdb mastodon
The default source install uses peer authentication, so the Linux user mastodon can connect as the PostgreSQL role mastodon without storing a database password in /home/mastodon/live/.env.production.
- Open a login shell as the Mastodon user.
$ sudo -iu mastodon
- Clone the current Mastodon source release.
$ git clone --branch v4.6.2 --depth 1 https://github.com/mastodon/mastodon.git live
Use the current stable release tag for a real production install. Avoid deploying the default branch unless the instance is deliberately tracking unreleased code.
- Change into the Mastodon application directory.
$ cd ~/live
- Verify the checked-out release tag.
$ git describe --tags --exact-match v4.6.2
- Install rbenv for the Mastodon user.
$ git clone https://github.com/rbenv/rbenv.git ~/.rbenv
- Add rbenv to the Mastodon shell path.
$ echo 'export PATH="$HOME/.rbenv/bin:$PATH"' >> ~/.bashrc
- Enable rbenv shell initialization.
$ echo 'eval "$(rbenv init -)"' >> ~/.bashrc
- Load the updated shell configuration.
$ source ~/.bashrc
- Install ruby-build for rbenv.
$ git clone https://github.com/rbenv/ruby-build.git "$(rbenv root)"/plugins/ruby-build
- Install the Ruby version declared by the release.
$ RUBY_CONFIGURE_OPTS=--with-jemalloc rbenv install ##### snipped ##### Installed ruby-4.0.5 to /home/mastodon/.rbenv/versions/4.0.5
rbenv install reads /home/mastodon/live/.ruby-version from the current directory. The --with-jemalloc build option matches the upstream service templates that preload libjemalloc.
- Verify the Ruby version.
$ ruby -e 'puts RUBY_VERSION' 4.0.5
- Enable Bundler deployment mode.
$ bundle config set --local deployment 'true'
- Exclude development and test gems.
$ bundle config set --local without 'development test'
- Install the Ruby dependencies.
$ bundle install
- Install the JavaScript dependencies.
$ yarn install --immutable
Corepack reads the Yarn version from package.json. Current Mastodon source declares Yarn 4.x, so do not replace this with the old global npm install -g yarn path.
- Run the Mastodon setup wizard.
$ RAILS_ENV=production bin/rails mastodon:setup
Use social.example.com as LOCAL_DOMAIN unless a split-domain plan is already chosen, enter the SMTP service details, keep PostgreSQL and Redis on localhost for this single-host layout, and let the wizard create /home/mastodon/live/.env.production, precompile assets, and initialize the database.
Related: How to configure a Mastodon domain
Related: How to configure Mastodon SMTP email - Return to the sudo user shell.
$ exit
- Request the HTTPS certificate.
$ sudo certbot certonly --nginx -d social.example.com Successfully received certificate. Certificate is saved at: /etc/letsencrypt/live/social.example.com/fullchain.pem Key is saved at: /etc/letsencrypt/live/social.example.com/privkey.pem
Port 80 must reach this host from the public internet for the Nginx challenge to pass. Use the real Mastodon web hostname, not the account-only domain from a split-domain setup.
- Copy the upstream Nginx virtual host template.
$ sudo cp /home/mastodon/live/dist/nginx.conf /etc/nginx/sites-available/mastodon
- Enable the Nginx virtual host.
$ sudo ln -s /etc/nginx/sites-available/mastodon /etc/nginx/sites-enabled/mastodon
- Disable the default Nginx site.
$ sudo rm /etc/nginx/sites-enabled/default
- Edit the Mastodon Nginx virtual host.
$ sudoedit /etc/nginx/sites-available/mastodon
- Set the server name and certificate paths.
server_name social.example.com; ssl_certificate /etc/letsencrypt/live/social.example.com/fullchain.pem; ssl_certificate_key /etc/letsencrypt/live/social.example.com/privkey.pem;
Keep the upstream backend and streaming upstreams on 127.0.0.1:3000 and 127.0.0.1:4000 unless the systemd service ports were changed.
- Allow Nginx to read the public assets under the Mastodon home directory.
$ sudo chmod o+x /home/mastodon
- Test the Nginx configuration.
$ sudo nginx -t nginx: the configuration file /etc/nginx/nginx.conf syntax is ok nginx: configuration file /etc/nginx/nginx.conf test is successful
- Restart Nginx with the Mastodon virtual host.
$ sudo systemctl restart nginx
- Install the Mastodon systemd unit files.
$ sudo cp /home/mastodon/live/dist/mastodon-*.service /etc/systemd/system/
- Reload systemd.
$ sudo systemctl daemon-reload
- Enable and start the Mastodon services.
$ sudo systemctl enable --now mastodon-web mastodon-sidekiq mastodon-streaming
- Confirm the Mastodon services are active.
$ sudo systemctl is-active mastodon-web mastodon-sidekiq mastodon-streaming active active active
- Verify the public instance endpoint over HTTPS.
$ curl --head https://social.example.com/api/v2/instance HTTP/2 200 server: nginx content-type: application/json; charset=utf-8 ##### snipped #####
Create the first Owner account after the service and HTTPS checks pass.
Related: How to create a Mastodon admin user
Mohd Shakir Zakaria is a cloud architect with deep roots in software development and open-source advocacy. Certified in AWS, Red Hat, VMware, ITIL, and Linux, he specializes in designing and managing robust cloud and on-premises infrastructures.