Infra
I’m renting a VPS and a Storage Box from Hetzner.
Architecture
The VPS runs NixOS and mounts the Storage Box with SSHFS, and there is encfs on top of that for transparently encrypting all data stored on the box. It hosts the nextcloud data directory and automated daily backups.
Note: I don’t have a lot of experience with FUSE mounts but it seems like all the files will be owned by the user and group ID specified, regardless of who creates the files or any chown attempts. For this reason you must set the same user and group ID as what is used by FUSE mounts (maybe systemd?) for all users that run services which manipulate files on the mount. This solved all permission issues.
Container
Nextcloud runs in a NixOS container with Postgresql, Redis, and nginx. There is an nginx reverse proxy on the host OS. The NixOS container is ephemeral and uses bind mounts for persistent data. Although the host is not ephemeral, all the non-reproducible data is stored on /persistent or /mnt/box-plain with the exception of the SSH key used to mount the box. There is my user data in /home but there is nothing important there.
The container service is configured to run after the encfs mount service is done which runs after the sshfs mounting is done. This should ensure that after a reboot all the data is present for the container when it starts. The timeout for the container startup is also overwritten to 30 minutes.
Office
For online office support, Collabora CODE runs in an OCI container. The Nextcloud container is behind NAT, so I can have more services running parallelly in containers without unwanted interaction with the database, redis, or other services. The persistent data is separated for the container, so I can theoretically easily move a service to another VPS.
Maintenance
By default NixOS containers get a 1 minute timeout to start up with all their services which is not enough when there is an update to Nextcloud. I was unable to update nextcloud at all because of this and it took me forever to figure it out. With the custom timeout configuration updates are completely painless and there is basically no manual maintenance other than the occasional flake update and switching to the new nextcloud package when there is a new major release. I completely migrated all my data from OneDrive half a year ago and since then this setup has been a reliable and full-featured alternative.
Securing the Data
The storage box can only be accessed by SSH with an ssh key (no password) and cannot be accessed outside of the Hetzner network at all. To prevent the data at rest getting stolen everything on the external storage box is encrypted with a secure encfs configuration and in transit also by the sshfs layer under that. On top of that Nextcloud server side encryption is enabled, but after setting up encfs that seems a bit unnecessary. It makes it harder for attackers who gain access to the server to read the files but with a bit of skill and time they can be manually decrypted, given that the key can also be found on the server. There is also the zero trust Nextcloud E2EE feature which should be used for highly sensitive information.
To prevent the data getting lost there is a daily backup of the database and config - which are stored on the VPS block storage - to the external storage box. It’s a systemd service running every morning which creates a compressed archive with a timestamp. The storage box uses ZFS and has automated zero-cost backups as a built-in feature. This runs every morning after the other backup. Right now backing up the config, the database, and the data directory should cover everything that is necessary to recreate the setup if anything happens, except if the storage box loses the data, but for that I rely on the provider’s highly reliable setup.
Full disk encryption for the VPS block storage is still missing, which means that attackers with access to the physical disks or the hypervisors could steal and decrypt all the user data that is not secured with E2EE. I consider this a low risk scenario. The same can happen if they steal my log-in password, which - I hope - is unlikely.
Practical Info
It is a flake based config, here are all the modules for my VPS starting with conf.nix
. The latest up to date state is not on main right now.
There are some things I would like to improve on:
- encrypt the root partition and enable remote unlock over SSH in initrd
- make the host ephemeral and put all the persistent files in
/persistent
including SSH keys - add more Nextcloud apps
This was my first time deploying any service on a VPS and I’m pretty happy with how it turned out. Suggestions, questions and critique are welcome!