Skip to content
On this page

Security

In this chapter will cover many security tips and servers best-practice routines. You can use the chapter as a reference for most common cases.

The content in this chapter doesn't have to be related to each other, and you can just read the parts you think relevant for your use-case.

Up to date

It may sound obvious, but it's important to make sure all your software and tools are up-to-date.

Latest version

Here you can read an example of why chasing the latest version at all costs is so vital.

In short, the reason is that upgrading regularly from one version to the following one is much easier than run a huge upgrade every few versions.

Staying up-to-date is necessary to avoid any exploits or zero-day vulnerabilities. When you're in charge of a deployed Appwrite server, make sure you're regularly checking for updates:

  • Operating system
  • Security patches
  • Docker
  • Appwrite
  • Databases

In case you're using Linux - Ubuntu - for example, make sure to use the LTS - Long Term Support - version.

Blocking Console access?

You can block direct access to your Appwrite console by blocking the https://appwrite/console/* endpoint.

But you need to know that it won't actually block the endpoint access as the console uses the Realtime & CLI endpoints via the https://appwrite/v1/* endpoint, which can't be blocked as it's vital for all other endpoints.

For brute force attacks, Appwrite is taking care of it, and it won't matter if the request is done from the web console or via any REST/HTTP client.

Limit API keys ability and expiration.

There's a famous principle when it comes to security, it's called the least privilege principle. Meaning giving the fewest privileges possible while maintaining functionality.

When generating API keys within Appwrite, or any fine-grained tokens for that matter, grant the fewest permissions possible. If the API token is needed to execute a function, it doesn't make sense to grant it the Database one as well.

It is also best practice to set expiration dates for tokens, as some time you may have left active token that is not in use anymore.

Best practice

DDoS protection (Cloudflare)

You can use a DNS service like Cloudflare to proxy your request to your servers. Doing so will

  • Hide your server IP from the world, make it harder to get attacked.
  • Blocks all common ports as 22 for SSH
  • Give you the ability to block specific countries.

You're also getting a fast TTL for changing your DNS records.

And all of this is completely free.

Firewall

In any case, even if you're using Cloudflare, you would want to utilize a Firewall in front of your server.

There are two types of Firewalls

  • Software - Using common Linux tools like ufw, csf and iptables to block access to specific ports.
  • Hardware - Using your IaaS - Infrastructure as a Service - provider. This Firewall type is supported by all major providers including Linode, DigitalOcean, Hetzner and the Big-3.

When possible, I'm rooting toward the Hardware based solution. It's also recommended to block the ssh port to your specific IPs only.

SSH Settings

When accessing your Appwrite server, it's best to make sure you're blocking the SSH password authentication method, and changing the default 22 SSH port.

You can do so by editing the /etc/ssh/sshd_config file and changing these values

# Changing the port to 2221 (for example)
#Port 22 
Port 2221 

# Disable password login
PasswordAuthentication yes 
PasswordAuthentication no  

Then restart the SSH service

shell
systemctl restart ssh

It might be overwhelmed, but you can use Cloudflare zero-trust tunnels to use SSH in your browser. The advantage of using a Cloudflare tunnel is that you can use many SSO providers as AzureAD, Google, Google Workspace and more to access you server terminal.

Cloudflare zero-trust is completely free up to 50 users, this will sufficient the most use case.

Check the fail2ban utility used for block machines that cause multiple authentication errors.

You can also avoid using the root account altogether by implementing sudo in the following paragraph 👇.

Sudo

I was asked what is the use of sudo if you can use it to run any commands? that's a good question.

Most of the time the sudo settings are way too open, this is due to many reasons, some time is part of your IaaS Image for your Linux distro.

When you'll sudo more strictly, you'll get the following advantages:

Requiring password
When running command with sudo the user will be prompt to enter is password, after that the user can run as many sudo command as needed without having the need to renter the password.

So even if someone gained access to your private key and was able to log in to your server, they are still going to know your password for running any sudo command.

After entering the password you can keep run sudo command with a 15-minutes default timeout, meaning after 15 minutes of idle mode, you'll need to renter the password.

Limiting commands When setting the sudo command for a given user it's best to use the least privilege principle, giving the user sudo access only to the needed commands nothing more.

Setting sudo

Run this command to create user named dockeruser

shell
adduser dockeruser

Set a strong password for the user and complete the process.

Now, while running as the root user, run one of these two commands to edit the sudoers file.

shell
# This command will open the sudoers using the system default editor.
visudo

# Or
nano /etc/sudoers

Add these two lines at the end of the file to give the user a sudo access for docker binary with 5-minutes timeout.

dockeruser ALL=(ALL) /usr/bin/docker
Defaults:dockeruser timestamp_timeout=5

Now you can set an SSH connection for that user and use it for most management actions.

When needed, you can add more binary files for the user to run as root by separating them with a comma , like so:

dockeruser ALL=(ALL) /usr/bin/docker, /usr/bin/which

If you don't like the sudo way you can check how to run Docker in Rootless mode, which doesn't supports Docker-Swarm Overlay networks.

VPC

When your Appwrite infrastructure is composed of more than one server, it's best to use VPC - Virtual Private Cloud.

You can place multiple servers into one VPC which gives you the ability to share the same network subnet between all the servers, letting you access servers by using their local IP.

This way you can have a Database server that can be connected only from servers within the VPC with no outside connection allowed.

You can do that by adding a rule to the IaaS provider Firewall letting this server be contacted only from servers from the same VPC.

Uptime

You can use on of the plenty available uptime services out there. I'm recommending you check Uptime Kuma, a fancy self-hosted monitoring tool.

One of the advantages of Uptime Kuma is the multiple checking methods, including one for checking Docker containers.

Monitor login attempts

You can check the login attempts in the auth.log file by running

shell
grep "Failed" /var/log/auth.log

You don't need to do it so often, but it will give you a quick overview of the server SSH attacking measures.

Antivirus

You can install the clamav antivirus in your server in general by running.

shell
apt install clamav clamav-daemon -y

The only way viruses can enter your self-hosted Appwrite is from user-uploaded files in the storage module. That's why it's vital to make sure you're protected against any viruses.

Both Docker volumes and the decentralized approach for your volumes will save the file in plain sight.

To mitigate this issue, it will be best to use an external storage service like S3 for the user-uploaded media.

Mini tip

Hide server information

Appwrite is already hiding your server details, but just in case it might be good to change the hostname of your server for something that don't identify your server OS type & version.

In case your hostname something like this eu-4gb-ubuntu-2024 you might want to change it by running:

shell
hostname NEW_HOST_NAME

Worst case

It's very unpleasant to find out your Appwrite is not working, try to be prepared for the worst case,

It's best to have a plan, and practice it, for what you do when:

CrisisPossible solutions
Server is not respondingChecking the server in your IaaS provider
Booting another server using recent snapshot and the daily DB backup.
HackedRestore another server using recent snapshot and the daily DB backup
Check to see if you can spot the server weakness.
Corrupted DatabaseRestore the DB from the latest DB backup
Missing files (volumes)Restore from backup
Use external provider.

In the end, vulnerabilities can't be planned, and your job as a Appwrite DevOps is to try to mitigate the worst cases as fast as possible.

Backups

Backup is an important aspect of your self-hosted Appwrite.

When approaching backups, it's best to make as automatically as possible, so you can set & forget.

In this example of backup.sh script you can see that I'm running backup on the database and saving the file in my Google Drive using rclone

shell
#!/bin/sh

cd /root/appwrite/

# Database backup
/usr/local/bin/docker-compose -f /root/appwrite/docker-compose.yml exec mariadb sh -c 'exec mysqldump --all-databases --add-drop-database -u"$MYSQL_USER" -p"$MYSQL_PASSWORD"' >dump.sql

# Setting variables
name=$(date +"%m_%d_%Y_%H_%M")
fullname="backup_${name}.zip"

# Creating zip file
zip -r $fullname dump.sql docker-compose.yml .env

# Coping the file to Google Drive
rclone copy $fullname "gdr:/"

# Deleting the backup and dump.sql file
rm $fullname dump.sql

Then I've added the backup to Linux cronjob by running

shell
crontab -e

Then, I've added this line

0 8 * * *  /root/appwrite/backup.sh

Which sets the task to run any morning at 08:00AM at server time.