So you want to deploy pleroma, or already run it, and you either expect to be attacked or are already under attack. Your one VPS isn’t holding up to the strain, and you want to keep your space open for those that need it.

The purpose of this post is to guide you through setting up a more secure instance that won’t get knocked offline so quickly.

You will need:

  • 3 ideally network-seperated hosts
    • 1*highish-spec server to run pleroma itself. size based on expected # of users
    • 2*very low spec, you can go for AWS t3 micro or similar
  • an ssh terminal
  • some patience

The architecture we’re aiming for is this:


I will assume you’re using ubuntu throughout. If you use another distribution, you can follow the gist, but just alter commands when needed.

Setting up the bastion

First thing’s first, you’ll want a bastion. Spin up a small host somewhere, don’t point any DNS at it or anything.

First, we want to shut off all ports except 22.

sudo apt-get install ufw
sudo ufw default deny incoming
sudo ufw allow 22
sudo ufw enable

It’ll shout at you about disrupting connections, but we only care about 22 so it’s ok.

Now we want to turn off password authentication - otherwise you’ll have people everywhere trying to brute-force you. Make yourself an ssh key if you don’t already have one with ssh-keygen on your local host, and paste the contents of ~/.ssh/ into your bastion host’s /home/myuser/.ssh/authorized_keys file.

Try logging into your bastion with the ssh key - it shouldn’t ask you for a password.

Now we’ve got that sorted, we can stop anyone ever logging in with a passwor again.

sudo nano /etc/ssh/sshd_config

make sure PubkeyAuthentication is set to yes, and PasswordAuthentication to no, then run sudo systemctl restart sshd

You now have a host that only accepts SSH keys. Test it out by trying ssh nouser@BASTION_IP - it should reject you instantly.

Note your bastion’s IP address, you’ll need it later.

Setting up the outbound proxy

Spin up another small host somewhere, and in the same way add your SSH key to the authorized_keys file and disable password login

Once you’ve done that, we can set up the firewall

sudo apt-get install ufw
sudo ufw default deny incoming
sudo ufw allow from BASTION_IP to any port 22
sudo ufw enable

Log out, you should get denied trying to SSH in from any host but your bastion.

Now log back in via your bastion (just ssh into the bastion then ssh onto the proxy host)

Let’s install the proxy software!

First you’ll need the IP of your proxy server, ip addr | grep inet to get this, take the non-127 address.

sudo apt-get install dante-server
sudo nano /etc/danted.conf

you’ll want to set the following in your config:

logoutput: stderr /var/log/dante.log
internal: PROXY_HOST_IP port = 1080
external: PROXY_HOST_IP
socksmethod: none
clientmethod: none

client pass {
        from: to:
        log: connect disconnect error
socks pass {
        from: to:
        log: connect disconnect error

and then run sudo systemctl enable danted && sudo systemctl start danted to start it up.

Setting up pleroma

Follow the standard instructions from the documentation to install pleroma from OTP. Once you’ve done that and are able to access your instance, turn it off. We’ve got more to do.

Pleroma config

Set the relevant sections in config/prod.secret.exs to:

config :pleroma, :activitypub,
  outgoing_blocks: false

config :pleroma, :http,
  send_user_agent: true,
  user_agent: "Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/51.0.2704.103 Safari/537.36",
  proxy_url: {:socks5, {A,B,C,D}, 1080}

config :pleroma, :rate_limit,
  account_confirmation_resend: {8_640_000, 2}

Replace the A,B,C,D with the parts of your proxy host’s IP - i.e {192,168,1,1}. Feel free to swap the user-agent for whatever you want.

Nginx config

(credit to hexadix)

We also want to set up nginx to survive in the event of any high-volume attack.

Open up /etc/nginx/nginx.conf and set the following

worker_rlimit_nofile 102400;

events {
  worker_connections 100000;

Now open /etc/sysctl.conf and set fs.file-max = 500000, close it and run sudo sysctl -p to reload your config

Over to /etc/security/limits.conf - add the following lines

www-data         soft    nofile          102400
www-data         hard    nofile          409600

Now reload nginx with sudo systemctl reload nginx and you should be good

Setting up cloudflare

Go sign up at and follow their instructions to get your domain onto cloudflare. Once they’re happy and your DNS is set up, we can move on. You may want to install their origin SSL certificate.

Whilst you’re here, jump on over to firewall -> tools, and if you’re willing to set up on a paid plan, set some rate limit rules. Some basic ones may include 5 requests per 10 seconds to /, block for 1 hour, or 600 requests per 1 minute to *, block for 1 hour. Set them as you feel appropriate. I’d advise picking as low a number as you think you can get away with.

Setting the firewall

sudo apt-get install ufw
sudo ufw default deny incoming
sudo ufw allow from BASTION_IP to any port 22
while read line; do
  sudo ufw allow from $line to any port 443
done < ips-v4
sudo ufw enable

This will only allow access to your bastion for SSH, and from cloudflare to your origin.

Adding pleroma’s IP to your proxy

SSH back into your proxy and run a simple

sudo ufw allow from PLEROMA_IP to any port 1080

Now you can start up your pleroma instance, and theoretically access it as normal.

So now we’ve got ourselves a system by which the origin’s IP is never exposed, our connection is anonymised, inbound connections are rate-limited and nobody can ssh in without first going through the bastion which only lets in our certificate.

If you want to go even further, you could install a HIDS like ossec on your hosts, or install a monitoring solution to keep track of how everything is going.