How to Install Mastodon on Debian and Ubuntu (Step-by-Step Guide)
Step-by-step guide to installing Mastodon on Debian and Ubuntu. Start your own decentralized social network today!

I contributed to the official Mastodon installation guide back in 2022, but it has since become outdated. In this updated guide, I’ll walk you through the latest step-by-step process to install and configure a Mastodon instance on your own server.
1.What is Mastodon?
Mastodon is a free, open-source, decentralized social networking platform that offers a Twitter-like experience without being controlled by a single company.
Instead of everyone joining one central site, users can create or join independently operated servers (called "instances") that communicate with each other across the wider "Fediverse" (federated universe).
Running your own Mastodon instance gives you full control over your community, data, and content policies.

Mastodon Official Website
2.Prerequisites
You will need to prepare the following:
-
A public domain name (either free or paid) with DNS A/AAAA records pointed to your server's IP address. In this tutorial, we will use
example.com
as an example. -
A Linux server with Debian Stable or Ubuntu LTS installed and root access.
-
An email delivery service such as Mailgun or Amazon Simple Email Service.
First, log in to your server as the root user, or switch to root using sudo -i
or su root
and update system:
apt update
apt upgrade -y
apt full-upgrade -y
Upgrade system
Then, install the required software and dependencies:
apt install curl vim wget gnupg dpkg apt-transport-https lsb-release ca-certificates -y
Install system packages
3.Add repositories
Nginx
curl -sS https://n.wtf/public.key | gpg --dearmor > /usr/share/keyrings/n.wtf.gpg
echo "deb [arch=$(dpkg --print-architecture) signed-by=/usr/share/keyrings/n.wtf.gpg] https://mirror-cdn.xtom.com/sb/nginx/ $(lsb_release -sc) main" > /etc/apt/sources.list.d/n.wtf.list
Nginx by N.WTF
Node.js
curl -fsSL https://deb.nodesource.com/gpgkey/nodesource-repo.gpg.key | sudo gpg --dearmor -o /usr/share/keyrings/nodesource.gpg
echo "deb [signed-by=//usr/share/keyrings/nodesource.gpg] https://deb.nodesource.com/node_22.x nodistro main" | sudo tee /etc/apt/sources.list.d/nodesource.list
Node.js 22.x
PostgreSQL
curl -sSL https://www.postgresql.org/media/keys/ACCC4CF8.asc | gpg --dearmor > /usr/share/keyrings/postgresql.gpg
echo "deb [arch=$(dpkg --print-architecture) signed-by=/usr/share/keyrings/postgresql.gpg] http://apt.postgresql.org/pub/repos/apt $(lsb_release -cs)-pgdg main" > /etc/apt/sources.list.d/postgresql.list
Latest PostgreSQL
Redis
curl -fsSL https://packages.redis.io/gpg | sudo gpg --dearmor -o /usr/share/keyrings/redis-archive-keyring.gpg
echo "deb [signed-by=/usr/share/keyrings/redis-archive-keyring.gpg] https://packages.redis.io/deb $(lsb_release -cs) main" | sudo tee /etc/apt/sources.list.d/redis.list
Latest Redis
Elasticsearch 7
curl -sS https://artifacts.elastic.co/GPG-KEY-elasticsearch | gpg --dearmor > /usr/share/keyrings/elasticsearch.gpg
echo "deb [arch=$(dpkg --print-architecture) signed-by=/usr/share/keyrings/elasticsearch.gpg] https://artifacts.elastic.co/packages/7.x/apt stable main" > /etc/apt/sources.list.d/elasticsearch.list
Elasticsearch 7 (optional)
Then update system
apt update
Update system
And enable Yarn
corepack enable
Enable Yarn
4.Install System Packages
Now, let's install the system packages required for Mastodon:
apt install -y \
imagemagick ffmpeg libpq-dev libxml2-dev libxslt1-dev file git-core \
g++ libprotobuf-dev protobuf-compiler pkg-config nodejs gcc autoconf \
bison build-essential libssl-dev libyaml-dev libreadline6-dev \
zlib1g-dev libncurses5-dev libffi-dev libgdbm-dev \
redis-server redis-tools postgresql postgresql-contrib \
libidn11-dev libicu-dev libjemalloc-dev libvips nginx-extras
Install system packages
Then, enable the redis-server
service:
systemctl enable --now redis-server
Enable Redis
5.Create Masoton User
We should create a user called mastodon:
sudo adduser --disabled-password --gecos "" --home /home/mastodon mastodon
Add Mastodon user
Then, grant the appropriate permissions for the /home/mastodon
directory:
chmod 0755 /home/mastodon
Grant permission
6.Setting Up PostgreSQL
Mastodon uses PostgreSQL as its database. Let's create a user named mastodon
and grant it permission to create a database for Mastodon:
sudo -u postgres psql
CREATE USER mastodon CREATEDB;
\q
Create PostgreSQL user
You can use PGTune to optimize your PostgreSQL configuration and performance settings.
PGTune automatically generates recommended PostgreSQL configuration settings based on your server's resources, such as memory and CPU, to improve database performance.
7.Install Mastodon
Now, let's use the mastodon
user to install Mastodon. First, switch to the mastodon
user and navigate to the /home/mastodon
directory:
su - mastodon
cd ~
Switch to Mastodon user
Next, install rbenv, a version management tool for the Ruby programming language:
git clone https://github.com/rbenv/rbenv.git ~/.rbenv
cd ~/.rbenv && src/configure && make -C src
echo 'export PATH="$HOME/.rbenv/bin:$PATH"' >> ~/.bashrc
echo 'eval "$(rbenv init -)"' >> ~/.bashrc
sourcec ~/.bashrc
git clone https://github.com/rbenv/ruby-build.git ~/.rbenv/plugins/ruby-build
Install rbenv
Then, clone the Mastodon Git repository and switch to the latest released version:
cd ~
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)
Fetch Mastodon source code
Once this is done, we can install the correct version of Ruby:
RUBY_CONFIGURE_OPTS=--with-jemalloc rbenv install
Install Ruby
Now, let's install the Ruby and JavaScript dependencies:
bundle config deployment 'true'
bundle config without 'development test'
bundle install -j$(getconf _NPROCESSORS_ONLN)
yarn install
Install Ruby and NodeJS dependencies
Wait until the installation is complete. Next, we will proceed with the Mastodon configuration.
6.Generate the Mastodon configuration file
Run the interactive setup wizard:
RAILS_ENV=production bin/rails mastodon:setup
Setup Mastodon
This process will:
- Create a configuration file
- Run asset precompilation
- Create the database schema
The configuration file is saved as .env.production
. You can review and edit it as needed. For detailed options, refer to the official documentation.
You’re done working with the mastodon
user for now, so switch back to the root
user:
exit
Exit to root
7.Configuring Nginx and SSL Certicates
We use acme.sh to obtain free SSL certificates from Let's Encrypt:
cd ~
git clone https://github.com/acmesh-official/acme.sh.git
cd ./acme.sh
./acme.sh --install -m [email protected]
source ~/.bashrc
acme.sh --upgrade --auto-upgrade
acme.sh --set-default-ca --server letsencrypt
Install acme.sh
Make sure to replace [email protected]
with your actual email address; otherwise, you may encounter an error like this:
[Mon Apr 28 06:28:13 PM UTC 2025] Registering account: https://acme-v02.api.letsencrypt.org/directory
[Mon Apr 28 06:28:14 PM UTC 2025] Account registration error: {
"type": "urn:ietf:params:acme:error:invalidContact",
"detail": "Error creating new account :: contact email has forbidden domain \"example.com\"",
"status": 400
}
Error if you use @example.com
Now, let's create a directory for Let's Encrypt verification using the HTTP-01 challenge, along with a directory to store the SSL certificates:
mkdir -p /var/www/letsencrypt/.well-known/{acme-challenge,pki-validation}
chown www-data:www-data /var/www/letsencrypt -R
mkdir -p /etc/nginx/ssl
Create a directory for HTTP-01 challenge
Then, modify the default Nginx configuration file.
sudo bash -c 'cat > /etc/nginx/sites-available/default << "EOF"
server {
listen 80 default_server;
listen [::]:80 default_server;
server_name _;
location ~ ^/.well-known/(acme-challenge|pki-validation)/ {
root /var/www/letsencrypt;
}
location / {
return 301 https://$host$request_uri;
}
}
EOF'
Modify default Nginx configuration
This configuration will redirect all requests on HTTP port 80 to HTTPS port 443, except for requests to /.well-known/acme-challenge/
and /.well-known/acme-challenge/pki-validation/
, which will be served from the /var/www/letsencrypt
directory.
These exceptions are necessary for the Let's Encrypt HTTP-01 challenge to work properly.
Reload Nginx:
nginx -t
nginx -s reload
Reload Nginx
Now we can issue SSL certificates for your domain name:
acme.sh --issue -d example.com -w /var/www/letsencrypt
Issue SSL certificates
example.com
to your own domain name.Then, install the certificates to /etc/nginx/ssl
:
acme.sh --install-cert -d example.com \
--key-file /etc/nginx/ssl/example.com.key \
--fullchain-file /etc/nginx/ssl/example.com.crt \
--ca-file /etc/nginx/ssl/example.com.ca.crt \
--reloadcmd "systemctl restart nginx"
Install SSL certificates
Copy the Nginx configuration template from the Mastodon directory:
cp /home/mastodon/live/dist/nginx.conf /etc/nginx/sites-available/mastodon
ln -s /etc/nginx/sites-available/mastodon /etc/nginx/sites-enabled/mastodon
Copy Mastodon Nginx configuration
Modify /etc/nginx/sites-available/mastodon
:
- Remove the server block that starts with
listen 80
.
server {
listen 80;
listen [::]:80;
server_name example.com;
root /home/mastodon/live/public;
location /.well-known/acme-challenge/ { allow all; }
location / { return 301 https://$host$request_uri; }
}
Remove this server block
- In the server block that starts with
listens 443 ssl
:
server {
listen 443 ssl http2;
listen [::]:443 ssl http2;
server_name example.com;
ssl_protocols TLSv1.2 TLSv1.3;
# You can use https://ssl-config.mozilla.org/ to generate your cipher set.
# We recommend their "Intermediate" level.
ssl_ciphers ECDHE-ECDSA-AES128-GCM-SHA256:ECDHE-RSA-AES128-GCM-SHA256:ECDHE-ECDSA-AES256-GCM-SHA384:ECDHE-RSA-AES256-GCM-SHA384:ECDHE-ECDSA-CHACHA20-POLY1305:ECDHE-RSA-CHACHA20-POLY1305:DHE-RSA-AES128-GCM-SHA256:DHE-RSA-AES256-GCM-SHA384:DHE-RSA-CHACHA20-POLY1305;
ssl_prefer_server_ciphers on;
ssl_session_cache shared:SSL:10m;
ssl_session_tickets off;
# Uncomment these lines once you acquire a certificate:
# ssl_certificate /etc/letsencrypt/live/example.com/fullchain.pem;
# ssl_certificate_key /etc/letsencrypt/live/example.com/privkey.pem;
Modify this server block
server_name example.com;
to your own domain name.Add the following under ssl_session_tickets off;
line:
ssl_certificate /etc/nginx/ssl/example.com.crt;
ssl_certificate_key /etc/nginx/ssl/example.com.key;
Use your SSL certificates
We can use Perl and sed to delete or replace them directly from the terminal:
sudo perl -i -0pe 's/server \{\n listen 80;\n listen \[::\]:80;\n server_name example\.com;\n root \/home\/mastodon\/live\/public;\n location \/\.well-known\/acme-challenge\/ \{ allow all; \}\n location \/ \{ return 301 https:\/\/\$host\$request_uri; \}\n\}//g' /etc/nginx/sites-available/mastodon
sudo sed -i '
s|# ssl_certificate /etc/letsencrypt/live/example.com/fullchain.pem;|ssl_certificate /etc/nginx/ssl/example.com.crt;|;
s|# ssl_certificate_key /etc/letsencrypt/live/example.com/privkey.pem;|ssl_certificate_key /etc/nginx/ssl/example.com.key;|
' /etc/nginx/sites-available/mastodon
Replace server block by two commands
Then, create a cache folder for Nginx and reload it:
mkdir -p /var/cache/nginx
chown www-data:www-data /var/cache/nginx -R
nginx -t
nginx -s reload
Create cache folder and reload Nginx
8.Setting Up Systemd Services
First, copy the systemd service templates from the Mastodon directory.
cp /home/mastodon/live/dist/mastodon-*.service /etc/systemd/system/
Copy systemd templates
Then, start and enable the new systemd services:
systemctl daemon-reload
systemctl enable --now mastodon-web mastodon-sidekiq mastodon-streaming
Enable and start Mastodon services
Now, you can visit https://example.com/ from your browser and enjoy your own Mastodon instance!