Automatic ZFS snapshots with Sanoid on Fedora Server 44#
This is a continuation of the previous article about building your own NAS on Fedora Server 44. Last time, we installed Fedora Server, set up ZFS, created our first dataset, and shared it over the network with Samba.
Today we continue polishing our NAS. This time we’ll tackle one of ZFS’s coolest features - snapshots. And to avoid doing everything by hand, we’ll immediately automate the process using the Sanoid utility.
Why do we even need snapshots?#
ZFS is a very powerful and incredibly reliable filesystem. Of course, it’s not without its drawbacks, but its advantages far outweigh them.
One of ZFS’s main features is snapshots. Essentially, this is a snapshot of the filesystem’s state at a particular point in time. If something goes wrong - you accidentally deleted files, botched a system update, or got hit by ransomware - you can simply roll back to a previous state, as if nothing had happened.
Of course, this doesn’t come completely for free. Snapshots take up disk space, but only for changed data. As long as files don’t change, a snapshot weighs almost nothing. So you only pay for the changes, not for a full copy of the data.
In TrueNAS, snapshot management is exposed through a convenient WebUI. Fedora Server doesn’t have that convenience - Cockpit doesn’t yet fully support working with ZFS.
But that’s not a big deal, we’ll manage here too.
Working with snapshots manually#
First, let’s take a look at how snapshots work manually.
# Create a snapshot
sudo zfs snapshot data/media@before-upgrade
# Roll back to a snapshot
sudo zfs rollback data/media@before-upgrade
# Check pool integrity (recommended roughly once a month)
sudo zpool scrub dataFor local access, you can enable display of the .zfs directory:
sudo zfs set snapdir=visible data/mediaHowever, Samba doesn’t expose this internal directory to SMB clients. If you want to use snapshots directly from Windows Explorer through the “Previous Versions” menu, you’ll need to configure the
shadow_copy2module. This is exactly how this feature is implemented in TrueNAS. After that, a hidden.zfsfolder will appear in the directory, inside which you can browse the contents of all snapshots. Sometimes this turns out to be very handy if you need to quickly grab an accidentally deleted file without a full filesystem rollback.
However, even though manual labor ennobles a person, like any other labor, our memory is an unreliable thing. Personally, I don’t want to use AI agents, and giving them access to your data is a pretty bold idea. Bold, but dumb.
So let’s automate this whole process using the widely-known-in-narrow-circles utility set Sanoid.
Sanoid can automatically create snapshots on a schedule, remove outdated ones, and maintain a defined retention policy without any user involvement.
Installing Sanoid#
Step 1. Install the required dependencies#
First, let’s install Git and all the libraries needed for Sanoid to work.
sudo dnf install -y \
git \
perl-Config-IniFiles \
perl-Data-Dumper \
perl-Capture-Tiny \
perl-Getopt-Long \
lzop \
mbuffer \
mhash \
pvOn Fedora, all the necessary Perl modules are already in the official repositories, so there’s no need to enable EPEL or install anything via CPAN.
Step 2. Download Sanoid#
Move to a temporary directory and clone the project’s official repository.
cd /tmp
sudo git clone https://github.com/jimsalterjrs/sanoid.git
cd sanoidLet’s switch to the latest stable version.
sudo bash -c 'cd /tmp/sanoid && git checkout "$(git tag | grep "^v" | tail -n 1)"'Of course, you could stay on the master branch, but personally I prefer to use stable releases. A home NAS isn’t the place where you want to unexpectedly hit a bug after some random update.
Step 3. Install the program#
Now let’s install Sanoid itself.
Copy the executable files:
sudo cp sanoid syncoid findoid sleepymutex /usr/local/sbinCreate a directory for the config files:
sudo mkdir -p /etc/sanoidCopy the default settings:
sudo cp sanoid.defaults.conf /etc/sanoid/Create the main configuration file:
sudo touch /etc/sanoid/sanoid.confAnd just in case, let’s save an example config. It’ll come in handy if you want to set up more complex scenarios.
sudo cp sanoid.conf /etc/sanoid/sanoid.example.confThat’s it for installing Sanoid. All that’s left is to teach the system to run it automatically.
Setting up automatic startup#
Since Fedora uses systemd, it makes sense to let it handle running Sanoid.
Let’s create two small services: one will be responsible for creating snapshots, and the other for removing outdated ones.
Creating the snapshot creation service#
Create the file:
sudo nano /etc/systemd/system/sanoid.serviceInsert the following content:
[Unit]
Description=Create ZFS Snapshots
Requires=zfs.target
After=zfs.target
Wants=sanoid-prune.service
Before=sanoid-prune.service
ConditionFileNotEmpty=/etc/sanoid/sanoid.conf
[Service]
Type=oneshot
Environment=TZ=UTC
ExecStart=/usr/local/sbin/sanoid --take-snapshots --verboseThere’s nothing complicated here. This service simply runs Sanoid, which creates snapshots according to the configuration we’re about to write.
Pay attention to this line:
ConditionFileNotEmpty=/etc/sanoid/sanoid.confIt prevents the service from starting until we’ve created the configuration file. A very useful little detail that saves us from unnecessary errors during initial setup.
Creating the snapshot cleanup service#
Now let’s create the second service.
sudo nano /etc/systemd/system/sanoid-prune.serviceAnd insert:
[Unit]
Description=Prune ZFS Snapshots
Requires=zfs.target
After=zfs.target sanoid.service
ConditionFileNotEmpty=/etc/sanoid/sanoid.conf
[Service]
Type=oneshot
Environment=TZ=UTC
ExecStart=/usr/local/sbin/sanoid --prune-snapshots --verbose
[Install]
WantedBy=sanoid.serviceThis service handles cleanup: it removes snapshots whose retention period has already expired according to our policy.
This way, we won’t have to think at all about when to clean up old snapshots - Sanoid handles everything itself.
Creating a timer#
All that’s left is to teach systemd to run Sanoid on a schedule.
Let’s create a timer:
sudo nano /etc/systemd/system/sanoid.timerAnd insert:
[Unit]
Description=Run Sanoid every 15 minutes
Requires=sanoid.service
[Timer]
OnCalendar=*:0/15
Persistent=true
[Install]
WantedBy=timers.targetIn my case, Sanoid runs every 15 minutes.
For a home NAS, that’s more than enough. If your data changes rarely, you can safely increase the interval, for example to once an hour.
Enabling the services#
Now that all the services have been created, all that’s left is to inform systemd about them and enable automatic startup.
First, let’s reload the configuration:
sudo systemctl daemon-reloadLet’s allow the snapshot cleanup service to run:
sudo systemctl enable sanoid-prune.serviceAnd enable the timer:
sudo systemctl enable --now sanoid.timerYou can check that the timer started successfully with:
systemctl list-timers sanoid.timerThe expected result will look something like this:
NEXT LEFT LAST
Fri 2026-07-17 10:15:00 12 min Fri 2026-07-17 10:00:00At this point, the installation is fully complete.
Verifying the installation#
For your own peace of mind, you can confirm that Sanoid is indeed installed.
For example, check its version:
sanoid --versionOr open the built-in help:
sanoid --helpIf the commands run without errors, everything went fine.
Configuring the snapshot schedule#
At this point, there’s one small caveat.
Sanoid is already installed.
The timer is already running.
Systemd is already trying to run Sanoid every 15 minutes.
But… it won’t actually do anything yet.
That’s because we haven’t yet told it which datasets to maintain and how many snapshots to keep.
So let’s open the main configuration file:
sudo nano /etc/sanoid/sanoid.confBy default, Sanoid ships with a large number of ready-made templates. If you’re curious to see all of the utility’s capabilities, I recommend checking out the official example configuration:
https://github.com/jimsalterjrs/sanoid/blob/master/sanoid.conf
But for a home NAS, that level of configuration complexity usually isn’t needed.
Let’s assume our pool is called data, and all user data lives in the data/media dataset.
Then the sanoid.conf file will look like this:
[data/media]
use_template = production
[template_production]
frequently = 0
hourly = 36
daily = 30
weekly = 4
monthly = 3
yearly = 0
autosnap = yes
autoprune = yesLet’s break down what these parameters mean.
With this configuration, Sanoid will automatically:
- keep 36 hourly snapshots;
- keep 30 daily snapshots;
- keep 4 weekly snapshots;
- keep 3 monthly snapshots;
- not create “frequent” (
frequently) or yearly (yearly) snapshots.
The parameter
autosnap = yesenables automatic snapshot creation.
And the parameter
autoprune = yesautomatically removes old snapshots whose retention period has already ended.
Important. Replace
data/mediawith the name of your own ZFS dataset. You can find it out with the command:zfs list
If you need to protect the entire pool at once#
If your pool has multiple datasets (media, documents, backups, nextcloud, and so on), it’s much more convenient to use recursive processing.
In that case, it’s enough to write:
[data]
use_template = production
recursive = yes
[template_production]
frequently = 0
hourly = 36
daily = 30
weekly = 4
monthly = 3
yearly = 0
autosnap = yes
autoprune = yesNow Sanoid will automatically maintain all child datasets inside the data pool.
In my view, this is the most convenient option for a home NAS. You won’t need to add new datasets to the configuration by hand every time - they’ll automatically fall under the retention policy. This is especially handy if you experiment a lot and frequently create new datasets.
Verifying that everything works#
Let’s take another look at what we’ve already done.
- ✅ Installed Sanoid.
- ✅ Created the snapshot creation service.
- ✅ Created the old-snapshot cleanup service.
- ✅ Configured the systemd timer.
- ✅ Defined the snapshot retention policy.
Now, every 15 minutes, systemd will try to run Sanoid.
You can view the list of all active timers with the command:
sudo systemctl list-timersAnd here there’s one important point.
As long as the /etc/sanoid/sanoid.conf file is empty, nothing will happen. That’s exactly why we earlier added the line:
ConditionFileNotEmpty=/etc/sanoid/sanoid.confAs soon as you save a configuration with at least one dataset, the next timer run will automatically start creating snapshots.
You don’t need to do anything else. No need to restart the timer, reboot the server, or re-enable the services - systemd will pick up the new configuration on its own.
If you don’t want to wait for the next 15 minutes, you can test it manually.
Create snapshots:
sudo sanoid --take-snapshots --verboseOr run the service that’s normally triggered by the timer:
sudo systemctl start sanoid.serviceAfter that, you can confirm the snapshot actually appeared:
zfs list -t snapshotIf you see a new snapshot - congratulations, everything is working exactly as it should.
But what about backups?#
Here it’s very important to understand one thing.
Snapshots are not backups.
Yes, they protect very well against accidental file deletion, botched updates, or ransomware actions. But if the disk itself fails, or the controller dies, or the entire server burns down, all the snapshots will disappear along with it.
So snapshots are the first line of data defense, not a full-fledged backup.
Syncoid - the next step#
Fortunately, the Sanoid developers thought of this too.
Sanoid comes bundled with another great utility - Syncoid. It can replicate ZFS datasets to another pool, another server, or even another geographic location over SSH.
The simplest example looks like this:
sudo syncoid -r data backupwhere:
data- the pool or dataset we’re replicating;backup- the pool or dataset the data will be sent to.
At first glance, the command looks very simple, but under the hood Syncoid does quite serious work. The utility figures out on its own which data has changed since the last sync, transfers only the difference between snapshots, and automatically resumes the transfer if the connection unexpectedly drops.
That’s exactly why the Sanoid + Syncoid combo is considered one of the best solutions for organizing backups on top of ZFS.
Conclusion#
With that, setting up automatic snapshots can be considered complete.
Your NAS can now, on its own:
- create snapshots on a schedule;
- automatically remove outdated snapshots;
- maintain a defined retention policy without any user involvement.
In the next article, we’ll take a detailed look at Syncoid and set up full-fledged replication of data between two ZFS pools. After that, our NAS will be able to survive not just accidental file deletion, but the failure of an entire server.





