Gabriel Cachadiña

Private and Low-Power Backup Server

· Gabriel Cachadiña

Setting up a backup for our files that is secure, has large storage capacity, and consumes little power should be a priority for anyone. That is why in this post I show how to configure a server using NixOS that is capable of synchronizing with your devices to perform periodic backups, shutting itself down afterward to increase system security.

Why NixOS?

NixOS is a UNIX operating system that allows you to define all its programs, utilities, and services in a declarative, idempotent, and centralized configuration file. Thanks to this, we can create a single configuration file that we can keep in case we move our operations to any other computer.

That said, everything explained here could be transferred to any other UNIX server, either manually or using a declarative configuration tool such as Ansible.

WOL (Wake On LAN)

If our system is running 24/7 we will have to deal with the following:

For this reason, I decided that this synchronization machine will only run while one of my other machines is active. With this in mind, I configured the system to work using Wake On LAN (WOL), which allows the motherboard to listen on one of its RJ45 ports for a “magic packet.” If this magic packet is received, the machine will power on.

This can be configured in the Nix configuration as follows:

 1networking = {
 2  interfaces = {
 3    ens3 = {
 4      wakeOnLan.enable = true;
 5    };
 6  };
 7  firewall = {
 8    allowedUDPPorts = [ 9 ];
 9  };
10};

With this configuration, our system can be powered on remotely if any machine on the local network sends:

1wakeonlan -i 192.168.0.xxx { link/ether }

We can add this instruction to a cronjob on all machines that should be synchronized, so that if any of them is online and on the local network, it will synchronize its data with our server.

Automated shutdowns

Now we can turn our server on automatically, but how do we turn it off? To achieve this, we could create a small cronjob that shuts it down — but what if we are using it?

That is why I created a cronjob that runs the following BASH script:

 1{ config, pkgs, ... }:
 2
 3let
 4  turnOffNoSessions = pkgs.writeShellScript "turn-off-no-sessions" ''
 5    # Exit if any user is logged in
 6    if who | grep .; then
 7      exit 0
 8    fi
 9
10    # Otherwise shut down
11    shutdown -h now
12  '';
13in
14{
15	...
16}

This script checks whether there is any active SSH session and, if not, shuts down the server. This cronjob can be enabled in NixOS as follows:

1  services.cron = {
2    enable = true;
3    systemCronJobs = [
4      "*/5 * * * *	root	${turnOffNoSessions}"
5    ];
6  };

Synchronization

There are several possible options for synchronization. First, the same script that powers on the synchronization machine via WOL could, after a small delay, make a copy to the remote disk using scp or rsync. This solution would be very effective in the case of having a single machine, but since I have many machines with very different uptimes, I prefer to use syncthing.

Syncthing is a peer-to-peer file synchronization program available on all platforms, including Android. In addition, it allows configuring file versioning, making it possible to roll back documents.

To run the Syncthing service, the following lines should be added to the configuration.nix configuration file1:

1  services.syncthing = {
2    enable = true;
3    openDefaultPorts = true; # Open ports in the firewall for Syncthing. (NOTE: this will not open syncthing gui port)
4    user = "gabriel";
5    dataDir = "/home/gabriel";
6    configDir = "/home/gabriel/.config/syncthing";
7  };

WOL_Esquema

Verification

Finally, a verification method should be implemented to ensure that files are being saved and synchronized correctly. A very simple way to do this is to create an additional cronjob that runs every minute and writes a text file into the synchronized folder(s) showing the date of the last update. This way, from any other machine, there is a record of the last synchronization performed by the remote system.

 1{ config, pkgs, ... }:
 2
 3let
 4  logSync = pkgs.writeShellScript "log-sync" ''
 5    set -euo pipefail
 6    date '+%Y-%m-%d %H:%M:%S' > /home/gabriel/Sync/last_sync.txt
 7  '';
 8in
 9{
10  services.cron = {
11    enable = true;
12    systemCronJobs = [
13      "* * * * *	gabriel	${logSync}"
14    ];
15  };
16}

  1. Port 8483 must be opened in order to edit the Syncthing configuration. ↩︎

#self-hosted

Reply to this post by email ↪