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
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.
$ sudo apt update
$ sudo apt install --assume-yes curl wget gnupg lsb-release ca-certificates
$ curl -fsSL https://deb.nodesource.com/gpgkey/nodesource-repo.gpg.key | sudo 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" | 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.
$ sudo wget -O /usr/share/keyrings/postgresql.asc https://www.postgresql.org/media/keys/ACCC4CF8.asc ##### snipped #####
$ 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.
$ sudo apt update
$ 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.
$ sudo corepack enable
$ sudo adduser --disabled-password 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.
$ sudo -iu mastodon
$ 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.
$ cd ~/live
$ git describe --tags --exact-match v4.6.2
$ 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 ##### 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.
$ ruby -e 'puts RUBY_VERSION' 4.0.5
$ bundle config set --local deployment 'true'
$ bundle config set --local without 'development test'
$ bundle install
$ 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.
$ 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
$ exit
$ 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.
$ sudo cp /home/mastodon/live/dist/nginx.conf /etc/nginx/sites-available/mastodon
$ sudo ln -s /etc/nginx/sites-available/mastodon /etc/nginx/sites-enabled/mastodon
$ sudo rm /etc/nginx/sites-enabled/default
$ sudoedit /etc/nginx/sites-available/mastodon
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.
$ sudo chmod o+x /home/mastodon
$ sudo nginx -t nginx: the configuration file /etc/nginx/nginx.conf syntax is ok nginx: configuration file /etc/nginx/nginx.conf test is successful
$ sudo systemctl restart nginx
$ sudo cp /home/mastodon/live/dist/mastodon-*.service /etc/systemd/system/
$ sudo systemctl daemon-reload
$ sudo systemctl enable --now mastodon-web mastodon-sidekiq mastodon-streaming
$ sudo systemctl is-active mastodon-web mastodon-sidekiq mastodon-streaming active active active
$ 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