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.
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
.
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.
ls
: List files in a directorysu
: Allows you to log in as other users, if you know that user's password.sudo
: Allows you to execute commands as other users, or log in as other
users, using your own password. You don't need the other user's password
to execute commands with this, which makes it preferrable to su
.nano
: A text editor. The only thing you need to know is that
n
y
, and hitting ENTERexit
: Logs out of the current user. If you are on the user you logged in
as, this will close the session.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.
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.
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.
If you are on Mac, Linux, or BSD, you already have an SSH client.
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.
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.
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.
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".
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".
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.
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.
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.
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...)
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".
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.
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
.
You now have Misskey running. Invite your friends, follow people, do whatever.
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.
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
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
Go back to the user account. Run
sudo true
sudo -u postgres pg_dump misskey | gzip > misskey.sql.gz
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.
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.
Stop misskey.
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.
Use Filezilla.
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 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.
sudo -iu misskey
nvm install N
nvm use N
corepack enable
replace N
with the version number required for the new misskey version.
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.
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.
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.)