Overview
A snapshot is a consistent view of a file system at a certain point in time. If you take a snapshot of a file system, and later create a new file or modify an existing one or make any other changes to the file system, the snapshot will still have the old version of the file system without the changes. A snapshot can be mounted as a file system to give a read-only view of a past state of the file system it's a snapshot of.
Unlike copies, snapshots use space on disk only for data that have been overwritten since the snapshot was taken, and can be taken for a whole file system at once without any cooperation from applications to suspend their activity.
Snapshots are often used to take backups. This ensures that files such as sqlite3 databases aren't concurrently modified while the backup is being taken. Without snapshots, such files can be backed up reliably only with application-specific cooperation like sqlite3 backup, or while applications are not running. Caveats:
Snapshots don't magically make application data consistent. If interrupting an application suddenly, e.g. with SIGKILL or a spontaneous system reboot, would leave corrupt application data in the file system, then a snapshot taken at the same moment will also have corrupt application data.
But for live applications that are resilient to interruption, such as sqlite3, a backup taken without a snapshot may still corrupt their data, while a backup taken with a snapshot will not. (Taking a backup without a snapshot is a standard technique for corrupting sqlite3 databases!)
Incremental backup tools that try to avoid reprocessing unchanged files the next time around must handle mtimes carefully.
On some file systems—ffs and zfs—snapshots can be persistent: you can take a snapshot, stored in the file system itself, and it will persist until it is explicitly deleted, even if the system is rebooted. All disk-backed file systems in NetBSD, such as ext2fs and msdosfs, also support external but non-persistent snapshots.
Persistent snapshots in ffs
Persistent snapshots with fss(4) are currently supported for ffs file systems only—not to be confused with the fss file system snapshot device used to manage them.
A persistent snapshot of a file system is represented by a file inside the file system (with a special flag so it can't be read or written normally), and persists until the file is unlinked. This file is used as a backing store for the snapshot, to save any data overwritten later in the file system. Snapshots can be created with or loaded back into fss(4) devices in order to mount them. Active snapshots are managed with the fssconfig(8) command.
File systems may impose a limit on the number of snapshots that can be stored at any time. For ffs, this limit is 20.
Cost of snapshots. In ffs, overwriting a block that was in use in any snapshots causes a copy of the old block content to be saved in each of the snapshots separately, whether or not they are actively configured with fss(4). So while snapshots are more space-efficient than copies when most blocks are unchanged, they can cause write amplification by a factor of up to the number of snapshots.
Taking a snapshot
To create and mount a persistent snapshot of the /home file system,
via /dev/fss0:
# fssconfig fss0 /home /home/.snap/20260706.store # mkdir /home/.snap/20260706 # mount /dev/fss0 /home/.snap/20260706
Users can then browse their home directories in /home/.snap/20260706
as they existed at the time the snapshot was taken. There's nothing
special about the directory name .snap; you can store snapshots
anywhere inside the file system, and mount them anywhere at all.
Snapshots appear as files with the snap (SF_SNAPSHOT) flag, visible
with the ls(1) option
“-o”; see chflags(1).
The nominal size of a snapshot file is the size of the whole file
system, but it only uses up space for the old content of blocks that
have been overwritten since it was taken:
# ls -hlo /home/.snap total 8.0G -rw------- 1 root wheel snap 4.0G Jun 22 02:29 20260622.store drwxr-xr-x 2 root wheel - 512B Jun 22 02:28 20260622 -rw------- 1 root wheel snap 4.0G Jul 06 01:18 20260706.store drwxr-xr-x 2 root wheel - 512B Jul 06 01:17 20260706 # du -ahx /home/.snap/ 1.6G /home/.snap/20260622.store 1.7M /home/.snap/20260706.store 2.0K /home/.snap/20260622 2.0K /home/.snap/20260706 1.6G /home/.snap/
Note: find(1) cannot currently filter for snapshot files; see PR #60380. Also, file(1) does not recognize snapshot files as such; see PR #60379.
The snapshot can be unmounted and unconfigured:
# umount /home/.snap/20260706 # fssconfig -u fss0
But the snapshot will persist at /home/.snap/20260706.store, even
after a reboot, and can be configured and mounted again:
# fssconfig fss0 /home /home/.snap/20260706.store # mount /dev/fss0 /home/.snap/20260706
Recovering from a snapshot
If you made a mistake and need to recover from the snapshot, you can use, e.g., net/rsync to revert files back to what they were in the snapshot:
# rsync -aHc --delete /home/.snap/20260706/bofh/. /home/bofh/.
It is better to use rsync rather than tar(1) for this purpose, in order to avoid writing unchanged content back to the file system, since that would waste space in the snapshot backing store.
Discarding a persistent snapshot
When done with a snapshot, after unmounting and unconfiguring the fss(4), you can unlink the snapshot's backing store file to irrevocably discard it and free up any space it was using in the file system:
# umount /home/.snap/20260706 # fssconfig -u fss0 # rmdir /home/.snap/20260706 # rm /home/.snap/20260706.store
External snapshots with any disk-backed file system
Any file system mounted from a disk device, such as ext2fs and msdosfs, supports external (non-persistent) snapshots with fss(4). This requires a backing store outside the file system, where any existing data in the file system will be saved if later overwritten while the snapshot is active. The backing store can be either a raw character disk device or a regular file in another file system. Active snapshots are managed with the fssconfig(8) command.
fss(4) only supports file systems mounted from disk devices, so it does not support, for example:
- memory-backed file systems such as tmpfs
- virtual file systems such as procfs, ptyfs, or nullfs
- zfs (which manages disk devices differently through zpools of vdevs)
- nfs
- fuse/puffs
Cost of active snapshots. Overwriting a block that was in use in any active snapshots causes a copy of the old block content to be saved in each of the snapshots separately, causing write amplification by a factor of up to the number of snapshots configured.
Taking a snapshot
To create a snapshot of the /boot directory, using space in
/var/tmp/ to save any data overwritten while the snapshot is active,
and then mount it at /mnt:
# fssconfig fss1 /boot /var/tmp/ # mount /dev/fss1 /mnt
fssconfig(8) will create a
temporary file with an unpredictable name in /var/tmp/ as the backing
store for the snapshot, and immediately unlink it; it will be discarded
when the snapshot is unconfigured.
A snapshot can also use a raw character disk device as the backing store:
# fssconfig fss1 /boot /dev/rld0e
When done, you can unmount the snapshot and unconfigure it:
# umount /mnt # fssconfig -u fss1
zfs snapshots
In a zfs zpool, any dataset—a mountable file system or a zvol
block device—can be snapshotted with the
“zfs snapshot” command; see
zfs(8) for details. The snapshots
for a dataset <pool>/<datasetpath> are named
<pool>/<datasetpath>@<snapname>, and can be listed with
“zfs list -t snap <pool>/<datasetpath>”. Datasets can be
rolled back to a snapshot with “zfs rollback”, or cloned
from a snapshot with “zfs clone”. All snapshots in zfs
are persistent until deleted with “zfs destroy”.
The snapshots of a file system are automounted on demand as
subdirectories of its .zfs/snapshot/ directory, at the root of the
file system. The .zfs/ directory is hidden from directory listings
by default, but it can be exposed with
“zfs set snapdir=visible <pool>/<fs>”.
The snapshots of a zvol are available at
/dev/zvol/dsk/<pool>/<zvol>@<snapname> (block device) and
/dev/zvol/rdsk/<pool>/<zvol>@<snapname> (raw character device).
Taking a snapshot
To create a snapshot of a dataset, and all datasets under it atomically:
# zfs snapshot -r rpool/home@20260706
The current state of /home will now be visible under
/home/.zfs/snapshot/20260706. If you have a separate zfs file system
for each user's home directory, the current state of jruser's home
directory will be visible under /home/jruser/.zfs/snapshot/20260706
(NOTE: not under /home/.zfs/snapshot/20260706/jruser, which will just
appear as an empty directory).
Recovering from a snapshot
You can recover individual files from the .zfs/snapshot/<snapname>/
directory, but you can also roll back a whole file system to the latest
snapshot with “zfs rollback”:
# zfs rollback rpool/home/jruser@20260706
Discarding a snapshot
When done with a snapshot, you can destroy it with
“zfs destroy”:
# zfs destroy rpool/home/jruser@20260706
You can also recursively destroy all snapshots of the same name on datasets under a path:
# zfs destroy -r rpool/home/jruser@20260706
WARNING: If you enter the command before you finished typing, you
might accidentally destroy the file system instead of just a
snapshot!
zfs(8) uses the same
“zfs destroy” command for destroying file systems, zvols,
and snapshots.
One technique to avoid this mistake is to start by typing
“xxxzfs” instead of “zfs” so if you enter
the command too early, it will fail harmlessly:
# xxxzfs destroy -r rpool/home -sh: xxxzfs: not found # xxxzfs destroy -r rpool/home@20260706 -sh: xxxzfs: not found
Or start by typing “echo zfs” instead of
“zfs”, or use “zfs destroy -n”, especially
if you are running a loop over many different snapshot names, to review
what you would do.
Then, once you have finished typing the whole command, go back to the
beginning of the line and remove the xxx:
# zfs destroy -r rpool/home@20260706
Taking backups with snapshots
A snapshot of a file system won't help you recover if the disk it is mounted from fails. You can use snapshots to take backups, without corrupting live application data in the backups, in various ways:
For ffs, the dump(8) tool supports an option “
-X” or “-x <snapstore>” to take a temporary snapshot and then dump the content of the snapshot. The content can later be restored with the restore(8) tool.For lfs, the dump_lfs(8) tool supports an option “
-X” to dump a snapshot of the file system. (However, lfs does not support persistent snapshots, nor does it support mounting snapshots other than via generic external fss(4) snapshots.)For zfs, the zfs(8) tool's “
zfs send <rpool>/<datasetpath>@<snapname>” command dumps the content of a snapshot. The content can later be restored with the “zfs receive” command.
Many other tools exist to back up data from arbitrary file systems, such as:
- Traditional Unix tools: tar(1), pax(1), cpio(1)
- Packages available in pkgsrc: rsync, borgbackup, restic, and various others.
These tools can be used on any mounted snapshot.
Unprivileged snapshots and backups
By default, any user in the “operator” group (see
groups(7)) can configure
fss(4) snapshots, read disk devices,
and write to tape devices to take backups—this way backups can be
taken without root privileges, at least for some file systems like ffs
and lfs.
However, since “dump -X ... /fs” requires creating a
snapshot backing store at /fs/<tempfile>, running commands such as
“dump -X ... /” or “dump -X ... /usr”
unprivileged will fail because / and /usr are writable only by
root. Instead, you can create a directory /operator that group
“operator” can write to, and use
“dump -x /operator/ ... /” to create the snapshot file
for / at /operator/<tempfile>.
Example: tar
Mount a persistent snapshot of /home and back it up with
tar(1):
# fssconfig fss0 /home /home/.snap/20260706.store
# mkdir /home/.snap/20260706
# mount /dev/fss0 /home/.snap/20260706
# tar -C /home/.snap/20260706 -czf - . \
>/nfs/backups/home/20260706.tgz
Example: tarsnap
Mount a (single-use) “persistent” snapshot of /var and
back it up with tarsnap:
# fssconfig fss3 /var /var/ # mount /dev/fss3 /mnt # touch /tmp/20260706 # tarsnap -C /mnt -cf var-20260706 --snaptime /tmp/20260706 . # rm /tmp/20260706 # umount /mnt # fssconfig -u fss3
Although this uses a “persistent” snapshot, when
fssconfig(8) is passed a directory as
the backing store (here /var/), it will immediately unlink the
backing store when the snapshot is created; later, the snapshot will be
discarded as soon as it is unconfigured.
Tarsnap takes incremental backups and caches the mtimes of files each
time it runs in order to avoid spending time processing files that
haven't changed the last time. The “--snaptime” argument
gives a file whose ctime must predate the snapshot; see
caveat about mtime below for why this is necessary to
avoid data loss in later incremental backups.
Example: borgbackup
Take a zfs snapshot of /usr and back it up with
sysutils/py-borgbackup:
# zfs snap -r rpool/usr@20260706
# (cd /usr/.zfs/snapshot/20260706 &&
borg create borghost:/path/to/repo::usr-20260706 .)
WARNING: If used to take incremental backups, this may be vulnerable to data loss with naive mtime checks; see caveat about mtime below.
Example: rsync
Mount an external snapshot of the msdos volume /data and back it up
incrementally with net/rsync:
# fssconfig fss1 /data /var/tmp/
# mount /dev/fss1 /mnt
# ssh rsynchost mkdir /backups/$(hostname)/data/20260706
# rsync -aHc --link-dest=../latest/. \
/mnt/. rsynchost:/backups/$(hostname)/data/20260706/.
# ssh rsynchost ln -sfn 20260706 /backups/$(hostname)/latest
# umount /mnt
# fssconfig -u fss1
This will use a temporary file in /var/tmp/ as the backing store
while the snapshot is active, and then discard it as soon as it is
unconfigured.
WARNING: This may be vulnerable to data loss with naive mtime checks; see caveat about mtime below.
Caveat: Incremental backups and mtimes
Some incremental backup tools such as tarsnap, borg, or rsync will record the mtimes of files they have backed up to avoid reprocessing unchanged files the next time around. Although backing up a snapshot rather than the live file system avoids writing corrupted live application data into the backup (at least from applications that can recover from interruption), mtime-based incremental backups may have a subtle race condition that can lead to data loss if you are not careful.
Suppose:
- A file is modified at time t + 0.1sec.
- A snapshot is taken at time t + 0.2sec and backed up.
- The file is modified again at time t + 0.3sec.
In this case, the file's mtime before the snapshot and after the snapshot may both be rounded down—whether by the file system, by the backup tool, or by the backup archive format—to just a number of seconds t since the epoch. The next time the tool runs, it may conclude the file was unchanged since the last run, and lose the second modification. (Of course, in this scenario, trying to back up the live file system instead of a snapshot may lead to the even worse outcome of storing a corrupted file with some garbled mixture of the modifications instead!)
The tarsnap
“--snaptime <reference>”
option avoids this problem by treating any file with an mtime equal to
or newer than the ctime t of <reference> as if it had an mtime of
t − 1 in this run when comparing mtimes on the next run. Other
backup tools may have—or may not have, but may
need!—similar options to reliably take incremental backups.
The dump(8) options
“-X” and “-x <snapstore>” also avoid this
problem by recording a time just before taking the snapshot as the
dump date; on the next incremental run,
dump(8) will include any file
with an mtime equal to the last dump date or newer.
