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.comas 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 -yUpgrade system
Then, install the required software and dependencies:
apt install curl vim wget gnupg dpkg apt-transport-https lsb-release ca-certificates -yInstall 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.listNginx 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.listNode.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.listLatest 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.listLatest 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.listElasticsearch 7 (optional)
Then update system
apt updateUpdate system
And enable Yarn
corepack enableEnable 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-extrasInstall system packages
Then, enable the redis-server service:
systemctl enable --now redis-serverEnable Redis
5.Create Masoton User
We should create a user called mastodon:
sudo adduser --disabled-password --gecos "" --home /home/mastodon mastodonAdd Mastodon user
Then, grant the appropriate permissions for the /home/mastodon directory:
chmod 0755 /home/mastodonGrant 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;
\qCreate 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
source ~/.bashrc
git clone https://github.com/rbenv/ruby-build.git ~/.rbenv/plugins/ruby-buildInstall 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 installInstall 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 --immutableInstall 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:setupSetup 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:
exitExit 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 me@example.com
source ~/.bashrc
acme.sh --upgrade --auto-upgrade
acme.sh --set-default-ca --server letsencryptInstall acme.sh
Make sure to replace me@example.com 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/sslCreate 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 reloadReload Nginx
Now we can issue SSL certificates for your domain name:
acme.sh --issue -d example.com -w /var/www/letsencryptIssue 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/mastodonCopy 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/mastodonReplace 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 reloadCreate 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-streamingEnable and start Mastodon services
Now, you can visit https://example.com/ from your browser and enjoy your own Mastodon instance!
9.Enable Elasticsearch (Optional)
Mastodon supports full-text search when Elasticsearch is available. It is highly recommended to enable this feature if your server has sufficient resources.
We have already added the official Elasticsearch 7.x repository, so you can simply install it using apt:
apt install elasticsearchNext, create a jvm.options file to configure Elasticsearch memory usage:
sudo bash -c 'echo "-Xms4g
-Xmx4g" > /etc/elasticsearch/jvm.options.d/jvm.options'Then enable and start the systemd service:
systemctl daemon-reload
systemctl enable --now elasticsearchEdit the /home/mastodon/live/.env.production file and add the following lines:
ES_ENABLED=true
ES_HOST=localhost
ES_PORT=9200Then, restart the Mastodon services for the changes to take effect:
systemctl restart mastodon-{web,sidekiq}Finally, run the following command to create the Elasticsearch indices and populate them with searchable data:
su - mastodon
RAILS_ENV=production /home/mastodon/live/bin/tootctl search deploy

