Motivation

I have some datasets on a VPS that I want to back up regularly to my homelab.

  • Both servers run ZFS
  • I don’t want to expose extra ports

Approach

Use zrepl1 to transport backups through a Wireguard2 tunnel. While I still have to open up the Wireguard port, having an encrypted tunnel between my VPS and my home server is a useful thing to have in general.

Details

Setting up Wireguard

Most of this guide is taken from here.

Install packages.

pkg install wireguard-tools zrepl

Generate public and private keys for both the backup server and the VPS. Keep track of this.

wg genkey | \
    tee /usr/local/etc/wireguard/privatekey | \
    wg pubkey > /usr/local/etc/wireguard/publickey

Create a new configuration for the interface in /usr/local/etc/wireguard/wg0.conf.

  • The backup server acts as the server, listening for requests
  • Address is some private address. Subnet mask represents the address space that your peers live in.
[Interface]
PrivateKey = <backup server private key>
Address = 10.1.0.1/24 # As an example
ListenPort = 51820

[Peer]
PublicKey = <vps public key>
AllowedIPs = <the ips that your vps uses>

And on the client:

[Interface]
PrivateKey= <vps private key>
Address = <same as AllowedIPs in previous file>

[Peer]
AllowedIPs=10.1.0.1/32 # backup server IP
Endpoint=<public ip address>:51820
PersistentKeepalive=25
PublicKey= <backup server public key>

Enable and start Wireguard.

service wireguard enable
service wireguard start

You can also bring it up with

wg-quick wg0 up

Enable forwarding packets between interfaces.

sysctl net.inet.ip.forwarding=1
echo 'net.inet.ip.forwarding=1' >> /etc/sysctl.conf

And set up /etc/pf.conf. This enables packets to be received on the wg0 interface.

ext_if = "vtnet0"
wg_net = "10.1.0.0/24"
nat on $ext_if from $wg_net to any -> ($ext_if)

Enable and start pf.

service pf enable
service pf start

service pflog enable
service pflog start

Setting up zrepl

VPS config:

jobs:
  - name: minecraft_server
    type: push
    connect:
      type: tcp
      address: "10.1.0.2:8888"
    filesystems:
      zroot/minecraft: true
    snapshotting:
      type: periodic
      prefix: zrepl_
      interval: 10m
    pruning:
      keep_sender:
        - type: grid
          grid: "12x10m(keep=all) | 24x1h | 14x1d"
          regex: "^zrepl_"
      keep_receiver:
        - type: grid
          grid: "1x1m(keep=all) | 24x1h | 30x1d | 12x30d"
          regex: "^zrepl_"

This sets up snapshotting every 10 minutes. On the VPS, all snapshots are stored in the past two hours, a snapshot every hour is stored in the past day, and a snapshot every day is stored in the past two weeks. On the backup server, hourly snapshots are kept in the last day, daily snapshots in the last month, and monthly snapshots in the past year.

The 1x1m(keep=all) rule is to avoid this issue.

Server config:

jobs:
  - type: sink
    name: "backup-name"
    root_fs: "zroot/backups" # This is the fs where you want all your backups to live in
    serve:
      type: tcp
      listen: ":8888"
      clients: {
        "10.1.0.3": "vps_server"
      }
    recv:
      placeholder:
        encryption: off # Must be added

The name of the filesystem will be zroot/backups/vps_server/zroot/minecraft in this case.

Monitoring

You can monitor if data has been sent through the tunnel via sudo wg show, and the status of the backup jobs with sudo zrepl status.

Improvements

  • Containerize zrepl and WireGuard
    • Set up mounting ZFS in a container3
    • Set up Wireguard in a container
  • Figure out DNS to find the server name
  • Figure out restore

References

https://emar10.dev/posts/rootless-podman-wireguard

https://www.server-world.info/en/note?os=FreeBSD_14&p=wireguard&f=1