Skip to content

OpenMediaVault — Proxmox VM

OpenMediaVault (OMV) is the home NAS, running as a VM on Proxmox. It exposes ZFS pool storage as SMB shares on the LAN and is the primary source for rsnapshot pull backups to the Bazzite desktop.

VM specs: 2 cores, 2 GB RAM (balloon to 8 GB), 8 GB OS disk, installed from ISO.

Never update OMV via the web UI

Always update via the console:

apt update && apt upgrade -y
Updating through the OMV web UI can break the installation.


Part 1: OMV VM Setup

1.1 Initial Web UI Configuration

After installing from ISO, browse to the OMV IP (192.168.0.149) and log in with the default credentials (admin / openmediavault).

  1. Update all packages and reboot
  2. Click the user icon (top right) → change the admin password → log out and back in
  3. Users → Users: add users, assign usernames and passwords

1.2 Pass ZFS Storage to the OMV VM

OMV uses two ZFS pools from the Proxmox host: deep_pool (for local_share) and sync_pool (for Nextcloud data). Storage is passed to the VM as virtual block devices (zvols).

Create and attach a Zvol (deep_pool/local_share)

On the Proxmox host console:

# Create a 4TB sparse zvol (only consumes physical space as data is written)
zfs create -s -V 4000G deep_pool/local_share

# Find the zvol device path
ls /dev/zvol/deep_pool/
# returns: local_share

# Attach the zvol to the OMV VM (replace 102 with your VMID)
qm set 102 --virtio0 /dev/zvol/deep_pool/local_share

The VirtIO disk should now appear in the OMV VM's hardware table in Proxmox.

Pass a physical disk directly

For physical drives not in a ZFS pool:

# List disks by stable ID
ls -l /dev/disk/by-id/
# Note the disk name, e.g. ata-ST3500641NS_3PM1GVH3

# Add it to the OMV VM config
nano /etc/pve/qemu-server/102.conf

Add at the bottom:

scsi1: /dev/disk/by-id/ata-ST3500641NS_3PM1GVH3

Restart the OMV VM.


1.3 Initialize Disks and Shares in the OMV Web UI

For each new disk, follow this sequence in the OMV web UI:

  1. Storage → Disks: find the disk, select it, click Wipe
  2. Edit the disk → set spindown options: spin down, 10 minutes, enable write-cache
  3. Storage → File Systems: create ext4, select disk, save, then mount it
  4. Storage → Shared Folders: create the shared folder (e.g. OMVstorage), set permissions
  5. Services → SMB/CIFS → Shares: add the shared folder to make it available on the LAN
  6. Users → Users: assign user permissions to each shared folder

SMB/CIFS service

Enable the SMB/CIFS service before adding shares (Services → SMB/CIFS → enable).


1.4 USB Backup Drive Setup

To add a USB backup drive:

In Proxmox: Hardware → Add USB Device → select the USB port → reboot the OMV VM.

In OMV web UI:

  1. Storage → Disks → wipe the drive
  2. Storage → File Systems → create ext4 — do not mount after creating (the USB backup plugin handles mounting)
  3. Services → USB Backup → Add:
  4. Select the USB backup drive as volume
  5. Select local_share as source
  6. Enable auto-run

Part 2: rsnapshot Pull Backups (on Bazzite)

rsnapshot runs on the Bazzite desktop (/mnt/backups), not on OMV. It SSHes into OMV (and the Proxmox host directly for Nextcloud data) and pulls snapshots to a dedicated local HDD. OMV is the data source; Bazzite is where the backup job runs and where snapshots are stored.


2.1 Prepare the Backup HDD (Bazzite)

  1. Open KDE Partition Manager, delete all existing partitions on the secondary HDD, create one new ext4 partition labeled Backups
  2. Create the mount point:
    sudo mkdir -p /mnt/backups
    
  3. In Partition Manager, right-click the partition → Edit Mount Point → identify by UUID, set path to /mnt/backups
  4. Set permissions (root-only access):
    sudo chown root:root /mnt/backups
    sudo chmod 700 /mnt/backups
    

2.2 Install rsnapshot (Bazzite)

sudo rpm-ostree install rsnapshot

Reboot after install.


2.3 SSH Key Setup

rsnapshot pulls over SSH as root. Two SSH targets need passwordless access:

OMV VM (192.168.0.149) — for deep_pool/local_share:

ssh-keygen -t ed25519
ssh-copy-id root@192.168.0.149
# Verify
ssh root@192.168.0.149

Proxmox host (192.168.0.250) — for sync_pool/nextcloud_data (this pool is on the Proxmox host directly, not passed through to any LXC):

sudo ssh-copy-id -i /root/.ssh/id_ed25519.pub root@192.168.0.250
# Verify
sudo ssh -i /root/.ssh/id_ed25519 root@192.168.0.250 "hostname"
sudo ssh root@192.168.0.250 "ls /sync_pool/nextcloud_data/"

Why SSH to Proxmox directly?

The Nextcloud data lives on sync_pool on the Proxmox host itself — it's mounted into the Nextcloud LXC via pct set, but the actual ZFS dataset is at the host level. rsnapshot reaches it by SSHing to the Proxmox host rather than through the LXC.


2.4 rsnapshot Configuration

rsnapshot.conf
#################################################
# rsnapshot.conf - rsnapshot configuration file #
#################################################
#                                               #
# PLEASE BE AWARE OF THE FOLLOWING RULE:        #
#                                               #
# This file requires tabs between elements      #
#                                               #
#################################################

#######################
# CONFIG FILE VERSION #
#######################

config_version  1.2

###########################
# SNAPSHOT ROOT DIRECTORY #
###########################

# All snapshots will be stored under this root directory.
#
snapshot_root   /mnt/backups/

# If no_create_root is enabled, rsnapshot will not automatically create the
# snapshot_root directory. This is particularly useful if you are backing
# up to removable media, such as a FireWire or USB drive.
#
no_create_root  1

#################################
# EXTERNAL PROGRAM DEPENDENCIES #
#################################

# LINUX USERS:   Be sure to uncomment "cmd_cp". This gives you extra features.
# EVERYONE ELSE: Leave "cmd_cp" commented out for compatibility.
#
# See the README file or the man page for more details.
#
cmd_cp      /usr/bin/cp

# uncomment this to use the rm program instead of the built-in perl routine.
#
cmd_rm      /usr/bin/rm

# rsync must be enabled for anything to work. This is the only command that
# must be enabled.
#
cmd_rsync   /usr/bin/rsync

# Uncomment this to enable remote ssh backups over rsync.
#
cmd_ssh /usr/bin/ssh

# Comment this out to disable syslog support.
#
cmd_logger  /usr/bin/logger

# Uncomment this to specify the path to "du" for disk usage checks.
# If you have an older version of "du", you may also want to check the
# "du_args" parameter below.
#
#cmd_du     /usr/bin/du

# Uncomment this to specify the path to rsnapshot-diff.
#
#cmd_rsnapshot_diff /usr/bin/rsnapshot-diff

# Specify the path to a script (and any optional arguments) to run right
# before rsnapshot syncs files
#
#cmd_preexec    /path/to/preexec/script

# Specify the path to a script (and any optional arguments) to run right
# after rsnapshot syncs files
#
#cmd_postexec   /path/to/postexec/script

# Paths to lvcreate, lvremove, mount and umount commands, for use with
# Linux LVMs.
#
#linux_lvm_cmd_lvcreate /usr/bin/lvcreate
#linux_lvm_cmd_lvremove /usr/bin/lvremove
#linux_lvm_cmd_mount    /usr/bin/mount
#linux_lvm_cmd_umount   /usr/bin/umount

#########################################
#     BACKUP LEVELS / INTERVALS         #
# Must be unique and in ascending order #
# e.g. alpha, beta, gamma, etc.         #
#########################################

retain  daily   6
retain  weekly  4
retain  monthly 12
#retain delta   3

############################################
#              GLOBAL OPTIONS              #
# All are optional, with sensible defaults #
############################################

# Verbose level, 1 through 5.
# 1     Quiet           Print fatal errors only
# 2     Default         Print errors and warnings only
# 3     Verbose         Show equivalent shell commands being executed
# 4     Extra Verbose   Show extra verbose information
# 5     Debug mode      Everything
#
verbose     2

# Same as "verbose" above, but controls the amount of data sent to the
# logfile, if one is being used. The default is 3.
#
loglevel    3

# If you enable this, data will be written to the file you specify. The
# amount of data written is controlled by the "loglevel" parameter.
#
logfile /var/log/rsnapshot

# If enabled, rsnapshot will write a lockfile to prevent two instances
# from running simultaneously (and messing up the snapshot_root).
# If you enable this, make sure the lockfile directory is not world
# writable. Otherwise anyone can prevent the program from running.
#
lockfile    /var/run/rsnapshot.pid

# By default, rsnapshot check lockfile, check if PID is running
# and if not, consider lockfile as stale, then start
# Enabling this stop rsnapshot if PID in lockfile is not running
#
#stop_on_stale_lockfile     0

# Default rsync args. All rsync commands have at least these options set.
#
rsync_short_args    -a
rsync_long_args --delete --numeric-ids --relative --delete-excluded --bwlimit=25600
#
# Note: there is an exception to every rule:
#   There is a <tab> after `rsync_long_args` and before `--delete`.
#   And a <space> after all of the other paramters.  Similar to:
#   rsync_long_args<tab>--delete<space>--numeric-ids<space>--relative

# ssh has no args passed by default, but you can specify some here.
#
#ssh_args   -p 22

# Default arguments for the "du" program (for disk space reporting).
# The GNU version of "du" is preferred. See the man page for more details.
# If your version of "du" doesn't support the -h flag, try -k flag instead.
#
#du_args    -csh

# If this is enabled, rsync won't span filesystem partitions within a
# backup point. This essentially passes the -x option to rsync.
# The default is 0 (off).
#
#one_fs     0

# The include and exclude parameters, if enabled, simply get passed directly
# to rsync. If you have multiple include/exclude patterns, put each one on a
# separate line. Please look up the --include and --exclude options in the
# rsync man page for more details on how to specify file name patterns.
#
#include    ???
#include    ???

# THESE DO NOT WORK FOR REMOTE BACKUPS, only LOCAL
# exclude   /media_storage/old_files/
# exclude   /media_storage/recordings/
# exclude   /media_storage/temp_mkv/

# The include_file and exclude_file parameters, if enabled, simply get
# passed directly to rsync. Please look up the --include-from and
# --exclude-from options in the rsync man page for more details.
#
#include_file   /path/to/include/file
#exclude_file   /path/to/exclude/file

# If your version of rsync supports --link-dest, consider enabling this.
# This is the best way to support special files (FIFOs, etc) cross-platform.
# The default is 0 (off).
#
#link_dest  0

# When sync_first is enabled, it changes the default behaviour of rsnapshot.
# Normally, when rsnapshot is called with its lowest interval
# (i.e.: "rsnapshot alpha"), it will sync files AND rotate the lowest
# intervals. With sync_first enabled, "rsnapshot sync" handles the file sync,
# and all interval calls simply rotate files. See the man page for more
# details. The default is 0 (off).
#
#sync_first 0

# If enabled, rsnapshot will move the oldest directory for each interval
# to [interval_name].delete, then it will remove the lockfile and delete
# that directory just before it exits. The default is 0 (off).
#
#use_lazy_deletes   0

# Number of rsync tries. If you experience any network problems or
# network card issues that tend to cause ssh to fail with errors like
# "Corrupted MAC on input", for example, set this to a value > 1
# to have the rsync operation re-tried. The default is 1.
#
#rsync_numtries 1

# Wait between tries in seconds.
# Specify the duration in seconds to wait between retries of the rsync operation.
# The number of retries should be defined in rsync_numtries.
# The default wait time is 0 seconds.
#
#rsync_wait_between_tries   0

# LVM parameters. Used to backup with creating lvm snapshot before backup
# and removing it after. This should ensure consistency of data in some special
# cases
#
# LVM snapshot(s) size (lvcreate --size option).
#
#linux_lvm_snapshotsize 100M

# Name to be used when creating the LVM logical volume snapshot(s).
#
#linux_lvm_snapshotname rsnapshot

# Path to the LVM Volume Groups.
#
#linux_lvm_vgpath   /dev

# Mount point to use to temporarily mount the snapshot(s).
#
#linux_lvm_mountpath    /path/to/mount/lvm/snapshot/during/backup

###############################
### BACKUP POINTS / SCRIPTS ###
###############################

# LOCALHOST
#backup /home/      localhost/
#backup /etc/       localhost/
#backup /usr/local/ localhost/
#backup /var/log/rsnapshot      localhost/
#backup /etc/passwd localhost/
#backup /home/foo/My Documents/     localhost/
#backup /foo/bar/   localhost/  one_fs=1,rsync_short_args=-urltvpog
#backup_script  /usr/local/bin/backup_pgsql.sh  localhost/postgres/
# You must set linux_lvm_* parameters below before using lvm snapshots
#backup lvm://vg0/xen-home/ lvm-vg0/xen-home/

# EXAMPLE.COM
#backup_exec    /bin/date "+ backup of example.com started at %c"
#backup root@example.com:/home/ example.com/    +rsync_long_args=--bwlimit=16,exclude=core
#backup root@example.com:/etc/  example.com/    exclude=mtab,exclude=core
#backup_exec    ssh root@example.com "mysqldump -A > /var/db/dump/mysql.sql"
#backup root@example.com:/var/db/dump/  example.com/
#backup_exec    /bin/date "+ backup of example.com ended at %c"

# CVS.SOURCEFORGE.NET
#backup_script  /usr/local/bin/backup_rsnapshot_cvsroot.sh  rsnapshot.cvs.sourceforge.net/

# RSYNC.SAMBA.ORG
#backup rsync://rsync.samba.org/rsyncftp/   rsync.samba.org/rsyncftp/
backup  root@192.168.0.149:/srv/dev-disk-by-uuid-fe526d98-6ffc-465c-90d8-a5fb21426baa/local_share/  local_share/    +rsync_long_args=--exclude-from=/etc/rsnapshot.exclude
backup  root@192.168.0.250:/sync_pool/nextcloud_data/   nextcloud_data/

Tabs required

rsnapshot.conf requires tabs (not spaces) to separate columns. Verify with cat -A /etc/rsnapshot.conf — tabs appear as ^I.

Place the config at /etc/rsnapshot.conf and the exclude file at /etc/rsnapshot.exclude on Bazzite.

Key settings in the config:

Setting Value Purpose
snapshot_root /mnt/backups/ Where all snapshots are stored on Bazzite
no_create_root 1 rsnapshot will not create the snapshot root — prevents accidental writes if the backup drive isn't mounted
retain daily 6 Keep 6 daily snapshots
retain weekly 4 Keep 4 weekly snapshots
retain monthly 12 Keep 12 monthly snapshots (1 year)
rsync_long_args --delete --numeric-ids --relative --delete-excluded --bwlimit=25600 --delete removes files deleted at source; --bwlimit=25600 caps rsync at 25 MB/s to avoid saturating the LAN
logfile /var/log/rsnapshot Log destination
lockfile /var/run/rsnapshot.pid Prevents two rsnapshot instances from running simultaneously

Backup targets:

# deep_pool/local_share from OMV VM, with exclude list applied
backup  root@192.168.0.149:/srv/dev-disk-by-uuid-fe526d98-6ffc-465c-90d8-a5fb21426baa/local_share/  local_share/  +rsync_long_args=--exclude-from=/etc/rsnapshot.exclude

# sync_pool/nextcloud_data from Proxmox host directly
backup  root@192.168.0.250:/sync_pool/nextcloud_data/  nextcloud_data/

Exclude file (/etc/rsnapshot.exclude) — paths excluded from local_share backup:

media_storage/old_files/
media_storage/recordings/
media_storage/temp_mkv/

Remote excludes

The exclude directive in rsnapshot.conf only works for local backups. For remote SSH backups, the exclude list must be passed via --exclude-from in rsync_long_args, which is why the local_share backup target uses +rsync_long_args=--exclude-from=/etc/rsnapshot.exclude.

Verify the config before first run:

sudo rsnapshot configtest

2.5 Systemd Timers

Create all six files in /etc/systemd/system/. The timers are staggered (monthly at 3:00, weekly at 3:15, daily at 3:30) to avoid lockfile conflicts. Each service uses systemd-inhibit to prevent sleep during a backup run.

rsnapshot-monthly.service:

[Unit]
Description=rsnapshot monthly backup
[Service]
Type=oneshot
ExecStartPre=/usr/bin/sleep 10
ExecStart=/usr/bin/systemd-inhibit --why="monthly backup" /usr/bin/rsnapshot monthly

rsnapshot-monthly.timer (1st of each month at 3:00 AM):

[Timer]
OnCalendar=*-*-01 03:00:00
WakeSystem=true
Persistent=true
[Install]
WantedBy=timers.target

rsnapshot-weekly.service:

[Unit]
Description=rsnapshot weekly backup
[Service]
Type=oneshot
ExecStartPre=/usr/bin/sleep 10
ExecStart=/usr/bin/systemd-inhibit --why="weekly backup" /usr/bin/rsnapshot weekly

rsnapshot-weekly.timer (every Monday at 3:15 AM):

[Timer]
OnCalendar=Mon *-*-* 03:15:00
WakeSystem=true
Persistent=true
[Install]
WantedBy=timers.target

rsnapshot-daily.service:

[Unit]
Description=rsnapshot daily backup
[Service]
Type=oneshot
ExecStartPre=/usr/bin/sleep 10
ExecStart=/usr/bin/systemd-inhibit --why="daily backup" /usr/bin/rsnapshot daily

rsnapshot-daily.timer (every day at 3:30 AM):

[Timer]
OnCalendar=*-*-* 03:30:00
WakeSystem=true
Persistent=true
[Install]
WantedBy=timers.target

Set permissions and enable:

sudo chmod 644 /etc/systemd/system/rsnapshot*
sudo chmod 644 /etc/rsnapshot.conf
sudo systemctl daemon-reload
sudo systemctl enable --now rsnapshot-daily.timer rsnapshot-weekly.timer rsnapshot-monthly.timer

Verify timers are active:

systemctl list-timers | grep rsnapshot