The Step-by-Step Handholding Absolute Retard's Guide to Securely Setting up Misskey on Ubuntu v1.1

Since more than one person didn't know how to securely setup a VPS.

This guide is targeted at people who don't know what a Linux is. Basic computer literacy is a requirement.

This document represents my knowledge of securing a VPS to the best of my ability. I have done this a lot but I am an amateur.

This document is licensed under the CC-0 license (a lot of it copy pasted anyways). Don't sue me if you get hacked.

I tested this on Linux, some of the Windows instructions might be wrong.

If you don't want to install Misskey just stop at the "Setup Misskey" step and install Mastodong or Pleroma or whatever instead

If you think Ubuntu sucks then you probably don't need this guide.

Yell at me at @hiro@mstdn.starnix.network or hirohito [at] cock.li for comments, complaints, or questions.

How to Read This Guide

This guide will tell you all the commands you need to type in to set up shit. COPY PASTE ONE LINE AT A TIME! Some commands are interactive and you have to fill in information, if you try to copy paste an entire block of commands into the terminal you will fuck things up.

That doesn't apply if you are pasting stuff into a text editor like nano.

Step 0: Learn Basic Linux Commands

You interact with your VPS through ssh, which is a program that allows you to run programs on other computers over an encrypted connection. You can type text into the terminal, use the arrow keys and backspace to edit text. After you have your line, press "ENTER" to send it. You can press the Up key and the Down key to scroll through the commands you have already sent.

Pasting into a terminal is complicated. NEVER CTRL+C or CTRL+V! These commands do different things in a terminal! Copying and pasting is discussed in another step when you need to do it.

There are multiple users on a Linux system. The administrator is called "root". Root can fuck shit up so the first thing you do with a VPS is restrict root access.

Step 1: Buy a VPS

Go to lowendbox and pick one. Low spec VPS are OK for hosting instances with only a few users.

If your VPS asks you what OS to install, or asks you configuration options about your OS like paswords, then don't order it until Step 5.

meme.moe hosted Misskey on spectra-ip on "VPS X2" which is 4 euro (like $4.50) a month, so if you can't decide buy from them. You can even pay with bitcoin if you're paranoid.

Step 2: Buy a Domain Name

Come up with a domain and buy one. You might be able to buy one from your VPS provider, but you can also buy one from dynadot or dreamhost.

Step 3: Point Domain Name to VPS

Find your IP address in the VPS. The shorter IP (looks like 127.0.0.1) is an IPv4 address, the longer one (has : instead of .) is an IPv6 address. You might only have one or the other.

Go to the control panel to your domain and find a tab that says "Records" or "DNS". For your IPv4 address, make an "A" record with your IPv4 address. For your IPv6 address, make an "AAAA" record with your IPv6 address.

If you want to host your Misskey instance on a subdomain of your domain, make a "CNAME" record for your subdomain. Have it point to your domain. Make sure your hostname has a . at the end of it or else it won't work.

Step 4: Setting up SSH Keys

If you are on Mac, Linux, or BSD, you already have an SSH client.

Windows

If you are on Windows, download PuTTY. Run PuTTYgen. Do not generate a DSA or SSH-1 key. RSA 4096 or ED25519 are better key algorithms. Then click "Generate" and follow the on-screen instructions until you get a public key. You can optionally add a key passphrase to encrypt the private key. Make sure to save the private and public key.

To copy text in PuTTY, just highlight it with your mouse. To paste text in PuTTY, Press SHIFT+Insert.

Mac, Linux, BSD

Open a terminal and run ssh-keygen -t ed25519. If you don't already have SSH keys then use the default path. Otherwise pick another path. Follow the rest of the on-screen instructions.

If you are using Terminal on Mac OSX, then Command+C and Command+V will work. If you are using a terminal on Linux or BSD, then to copy highlight the text and press CTRL+SHIFT+C. To paste, use either CTRL+SHIFT+V or middle click.

Step 5: Setup Ubuntu and Secure SSH

Basically every VPS will have an Ubuntu install. This guide is written for 24.04 LTS (which will have security updates until 2034). 22.04 LTS will also work.

Your VPS might ask for you to fill in info like passwords, default users, etc. Fill all of that in with whatever information you want. Some systems will email you the username and password for access to the machine. The root password should be very long and randomly generated, you will not use it for very long.

The VPS might ask for SSH public keys. This will automatically take care of some security settings but can make logging in for the first time more difficult. Add your public key to it, but if the following steps give you trouble reinstall Ubuntu without the public keys.

Step 5: Setup SSH Security

Mac, Linux, BSD

In your terminal run nano ~/.ssh/config. Type in

Host my.domain.com
    User username
    IdentityFile ~/.ssh/id_ed25519

Where "username" is the username you want to use for the user account (not "root") and id_ed25519 is the filename you put in in ssh-keygen. For instance, if you input /Users/Name/.ssh/misskey_key into ssh-keygen, put ~/.ssh/misskey_key into the config file.

Save and exit.

Now log in to your VPS. If you set up a user account, run ssh my.domain.com. If that doesn't work, or if your VPS setup did not set up a user account, try ssh root@my.domain.com.

You will get a prompt about an unknown fingerprint. Type yes. In rare circumstances you might get a fingerprint mismatch error. Only for this setup, run the command the error messages gives to remove the fingerprint. Continue on to the header labeled "For Everyone".

Windows

Open PuTTY. Type in your hostname. Go to "Connection->SSH->Auth" and enter the filepath for your private key. Go to "Connection->Data" and put in the username you want to use for your user account (not "root"). Go back to "Session" and type a name into the "Saved Sessions" box and hit save.

Click "open". You will be prompted for a username and password. If you set up a user previously, use that as your username and put in the user's password. If you didn't, use root as your username.

You might get a pop-up about a fingerprint. This is normal when connecting to a VPS for the first time. Click "yes" to trust the host.

If you set up SSH keys and can't log in, go back to "Connection->Data" and use "root" as your auto-login. Make sure to switch this back to the user you set up later.

Continue on to the header labeled "For Everyone".

For Everyone

This will only allow logins to people with our key. This step should be run as quickly as possible after your VPS has set up Ubuntu.

If you logged in with a user that was not "root", then run sudo true. Input your user password if necessary. If that doesn't work, then run su and type in the root password. If you are logged in with root continue on.

Run sudo nano /etc/ssh/sshd_config.

Find a line that says PermitRootLogin. Delete that line and write PermitRootLogin no. Make sure there isn't a # in front of the line, or it will be ignored!

Find a line that says PasswordAuthentication. Delete that line and write PasswordAuthentication no.

Go to the end of the file and write AllowUsers username, where username is the username< you want to use.

Save and exit the file. Run systemctl restart ssh.

ONLY if you logged in with a user that was not root, and you ran su, then run

usermod -aG sudo username
exit

If you ran exit as the user you logged in as, you would close the connection and you will not be able to log in!

If you logged in with root, type adduser username. Enter the password you want to use. The fields like "Full Name", "Room Number" etc. can be left blank. Run usermod -aG sudo username where username is the same username. Now run su username.

Now run the following commands:

cd
mkdir ~/.ssh
nano ~/.ssh/authorized_keys

Paste in your SSH public key into nano. Save and exit. Run chmod 644 ~/.ssh/authorized_keys.

Now open another Terminal/PuTTY (do not log out). If you changed the auto-login username on PuTTY, change that to the user account username and save the settings. Now try and log in. It should only prompt you for the private key and not for the user account itself. Close the first session and continue in the second session.

Step 6: Install Web Server

Run sudo apt-get update and sudo apt-get upgrade to make sure your system is up to date. Now run sudo apt-get install nginx and hit yes to all prompts.

Step 7: Setup Firewall

Run the following:

sudo ufw default deny incoming
sudo ufw default allow outgoing
sudo ufw allow OpenSSH
sudo ufw allow "Nginx Full"
sudo ufw enable

Respond y to all prompts. This will only allow connections to the web server and to SSH.

Step 8: Setup Fail2ban

Fail2ban is a program that will block IPs that fail too many login attempts. You can lock yourself out of your VPS (temporarily), so be careful.

NOTE: Fail2ban is currently broken on Ubuntu 24.04 LTS.

Run sudo apt-get install fail2ban. It should automatically set up everything. (I can't test it right now...)

Step 9: Setup HTTPS

HTTPS is required for Fedi, and it encrypts your connection to your server.

Run sudo apt-get install acmetool. Run sudo acmetool quickstart, select "Live v2", "WEBROOT", /var/www/.well-known/acme-challenge, put in your email if you want to, and agree to the LetsEncrypt TOS.

Type clear. Write sudo nano /etc/nginx/sites-available/my.domain.com.conf (replace with your domain name).

server {
    listen 80;
    listen [::]:80;
    server_name my.domain.com;

    # For SSL domain validation
    root /var/www;
    location /.well-known/acme-challenge/ { allow all; }
    location /.well-known/pki-validation/ { allow all; }
    location / { return 301 https://$server_name$request_uri; }
}

server {
    listen 443;
    listen [::]:443;
    server_name my.domain.com;
    root /var/www;
}

Remember to replace the domain names with the domain name you want misskey to run under. Save and exit. Now run sudo nano /var/www/index.html and write

<h1>Hello HTTPS</h1>

save and exit. Now run

sudo rm /etc/nginx/sites-available/default
sudo rm /etc/nginx/sites-enabled/default
sudo ln -s /etc/nginx/sites-available/my.domain.com.conf /etc/nginx/sites-enabled/my.domain.com.conf
sudo systemctl restart nginx
sudo openssl dhparam -out /etc/nginx/dhparams.pem 4096
sudo acmetool want my.domain.com

There should be no errors. Now run sudo nano /etc/nginx/sites-available/my.domain.com.conf and replace the second block (the one with 443) with

server {
        listen 443 ssl http2;
        listen [::]:443 ssl http2;
        server_name my.domain.com;
        root /var/www;

        ssl_certificate /var/lib/acme/live/my.domain.com/fullchain;
        ssl_certificate_key /var/lib/acme/live/my.domain.com/privkey;
        ssl_session_timeout 1d;
        ssl_session_cache shared:MozSSL:10m;  # about 40000 sessions
        ssl_session_tickets off;

        # curl https://ssl-config.mozilla.org/ffdhe2048.txt > /path/to/dhparam
        ssl_dhparam /etc/nginx/dhparams.pem;

        # intermediate configuration
        ssl_protocols TLSv1.2 TLSv1.3;
        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 off;

        # HSTS (ngx_http_headers_module is required) (63072000 seconds)
        add_header Strict-Transport-Security "max-age=63072000" always;
}

There's a really long line in this, you might have to scroll your browser horizontally. If pasting from here doesn't work, you can get that long line by going to https://ssl-config.mozilla.org and generating a config for "nginx", "intermediate" with "HTTP Strict Transport Security" checked and "OCSP Stapling" unchecked.

Remember to put your domain instead of "my.domain.com". Save, exit, and run systemctl restart nginx.

Go to a web browser and type in https://my.domain.com (use your domain, make sure it's https). You should securely connect to the server (no self signed cert errors or other errors) and it should say "Hello HTTPS".

Step 10: Setup PostgreSQL

Run

sudo apt-get install postgresql
sudo systemctl start postgresql
sudo -iu postgres
psql

The prompt should now change to postgres=#. Type

create user misskey with password 'dbpassword';
create database misskey with owner = misskey;
\q

Replace dbpassword with any password you want. You can just make it random, you don't have to memorize it.

Run exit to leave the postgres user.

Step 11: Setup Misskey

NodeJS is terrible but a lot of webshit uses it. The standard way to install the NodeJS version manager is haphazard. Never run a command of the form curl -o- https://url | bash, which is what most guides will tell you.

Run

sudo apt-get install redis ffmpeg tmux
sudo systemctl start redis
sudo adduser --disabled-password --home /var/misskey misskey
sudo -iu misskey
wget https://raw.githubusercontent.com/nvm-sh/nvm/v0.39.7/install.sh
bash install.sh
rm install.sh
exit
sudo -iu misskey
nvm install 20
corepack enable
git clone --recursive https://github.com/misskey-dev/misskey.git
cd misskey
git checkout master
git submodule update --init
NODE_ENV=production pnpm install --frozen-lockfile
cp .config/example.yml .config/default.yml

Edit the config by running nano .config/default.yml. Write in your domain name. In the "db" section, replace "default-postgres-user" with "misskey" and "default-postgres-pass" with the password you put into postgreSQL. Save and exit.

Run NODE_ENV=production pnpm run build. If this fails with an error like "javascript heap out of memory", run

NODE_OPTIONS=--max_old_space_size=4096 NODE_ENV=production pnpm run build

If that doesn't work, then you don't have enough RAM. Exit the misskey account (run exit), then run

sudo fallocate -l 4G /swapfile
sudo chmod 600 /swapfile
sudo mkswap /swapfile
sudo swapon /swapfile
sudo -iu misskey
cd misskey
NODE_OPTIONS=--max_old_space_size=4096 NODE_ENV=production pnpm run build

After that has run successfully, run pnpm run init.

Run exit to exit the misskey user. Run sudo nano /etc/nginx/sites-enabled/my.domain.com.conf.

At the very top of the file add

# For WebSocket
map $http_upgrade $connection_upgrade {
    default upgrade;
    ''      close;
}

proxy_cache_path /tmp/nginx_cache levels=1:2 keys_zone=cache1:16m
    max_size=1g inactive=720m use_temp_path=off;

and inside of the second server block (the one with 443 ssl http2), add this to the bottom before the final }:

client_max_body_size 80m;

# Proxy to Node
location / {
    proxy_pass http://127.0.0.1:3000;
    proxy_set_header Host $host;
    proxy_http_version 1.1;
    proxy_redirect off;

    # If it's behind another reverse proxy or CDN, remove the following.
    proxy_set_header X-Real-IP $remote_addr;
    proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
    proxy_set_header X-Forwarded-Proto https;

    # For WebSocket
    proxy_set_header Upgrade $http_upgrade;
    proxy_set_header Connection $connection_upgrade;

    # Cache settings
    proxy_cache cache1;
    proxy_cache_lock on;
    proxy_cache_use_stale updating;
    add_header X-Cache $upstream_cache_status;
}

Run sudo systemctl restart nginx, there should be no errors. Now run

sudo -iu misskey
cd misskey
tmux
NODE_ENV=production pnpm run start

Go to your web browser and open your website. It should prompt you to make an admin user. Put in your preferred username and password.

You should now be at misskey's main page. Try sending a message to an account you own on another instance. After a little bit (Misskey will spend some time loading data about the other instance) your message should show up on the other end. Press CTRL+b and write detach press enter, and type exit.

If you want to look at the Misskey log, run sudo -iu misskey followed by tmux attach -t 0. If you want to close tmux without closing Misskey, press CTRL+B and write detach. If you want to close misskey, open the log and press CTRL+c.

Congraulations!

You now have Misskey running. Invite your friends, follow people, do whatever.

Maintenance Tasks

Clean Up Remote Files

Misskey will save remote files to your server. It doesn't clean them up automatically and they will take up a lot of space. You need to delete them periodically.

Go to the Misskey Control Panel, click "files", and click the trash can button. This will delete the remote files.

Backup

CAUTION!: I haven't tested this yet, this is an outline of what I would do. follow at your own risk!

Backup can be done while Misskey is running.

If you have enough space on your VPS, you can just compress all the files on the server and then download them. If you lack the space on your VPS, things get trickier.

TODO: compressed encrypted backup scripts with borg-backup. I know how to do this but I've been writing this for like 4 hours and I just want to finish this

Compress Files

Backing up your files is easy. Run

sudo -iu misskey
cd misskey
ls

This will show you all the folders. One of them is called "files" and this is where all uploaded and cached files are. Run tar -czvf files.tgz files to zip up all files. Make sure to clean up remote files or else you will get a bunch of crap that isn't a part of your instance.

The file is currently in the misskey directory. To move it to the user's directory:

exit
sudo cp /var/misskey/misskey/files.tgz ~/files.tgz
sudo chown $(whoami):$(whoami) ~/files.tgz

Compress Database

Go back to the user account. Run

sudo true
sudo -u postgres pg_dump misskey | gzip > misskey.sql.gz

Copying Files

Linux, MacOS

Open another terminal.

Run sftp my.domain.com. Then type get misskey.sql.gz and get files.tgz. Then run rm misskey.sql.gz and rm files.tgz to delete them.

Windows

I don't know, I haven't used windows in years

Filezilla is the simplest SFTP client. This will allow you to transfer files between your computer and your VPS.

Download Filezilla. Open the "Site Manager", use "SFTP" protocol, with your domain as the host, port 22. Select your private key as the keyfile.

Restoring Backups

Stop misskey.

Linux, MacOS

Open another terminal. Change your directory to where your file and database backup is.

Run sftp my.domain.com. Then type put misskey.sql.gz and put files.tgz, or whatever the filenames are.

Windows

Use Filezilla.

For Everyone

To restore files, just move the files.tgz to the misskey directory and run tar -xvf files.tgz. This will decompress the files and put them back.

NOTE: This will destroy the current database! If you do not want to destroy the current database, BACK IT UP! To restore the database, run

sudo -iu postgres
psql
DROP DATABASE misskey;
CREATE DATABSE misskey with owner = misskey;
\q
gunzip misskey.sql.gz | sudo -u postgres psql -d dbname

Upgrading Misskey

Upgrading misskey is usually a simple task. However misskey versions may bump the minimum NodeJS or Postgres version, which can be a pain. check the changelogs to see if there are changes.

Before doing any of these tasks, stop Misskey.

Upgrading NodeJS

sudo -iu misskey
nvm install N
nvm use N
corepack enable

replace N with the version number required for the new misskey version.

Upgrading PostgreSQL

NOTE: I haven't tested this section, this is an outline of what I would do. Follow at your own risk!

Backup your PostgreSQL database. Run sudo systemctl stop postgres. Uninstall the current version of postgres and install the explicit postgres version number (i.e. postgres-16).

Then follow the instructions to restore the backup of the database.

Upgrading Misskey

sudo -iu misskey
cd misskey
git checkout master
git pull
git submodule update --init
NODE_ENV=production pnpm install --frozen-lockfile
NODE_ENV=production pnpm run build
pnpm run migrate

If everything went well then you can restart Misskey.

Other Non-Fedi Things

You now have VPS. The world is your oyster. You can set up IRC servers, mumble, XMPP, Wiki, other websites, other domain names, Matrix, game servers, self-hosted git repos, email servers (God help you), etc. The world is your oyster. (Just make sure to open the firewall ports.)