A snapshot policy file tells
autosnap(8)
how many ZFS snapshots of each cadence to create,
autocleansnap(8)
how many to keep, and
autosync(8)
where to replicate them. Each policy is a plain
bash(1)
fragment that the three jobs
source
into their own shell, so the file is a set of shell assignments and not a
key/value format parsed by OmniCube itself.
default.conf
is the policy shipped with the package and the one used by every dataset that
does not name another. Additional policies are ordinary files in the same
directory named
policy.conf
and are selected per dataset (see
Policy resolution
below).
The jobs never read a policy for its own sake: they first enumerate the
datasets they are responsible for, using the site-prefixed ZFS user properties
described in
omnicube(7),
and then resolve one policy per dataset:
processes the same
site:snapshotting=yes
datasets across
every
configured
svc:/omnicube/sysadm
instance and uses
RETENTION[type]
to decide how many to keep, plus the
BKP*
keys to clean the remote counterparts.
processes every dataset with
site:remotebackup=yes
and uses the
BKP*
keys,
IS_SYNC
and
IS_ENCRYPT
to replicate snapshots to the backup host.
File syntax
*
The file is sourced by
bash(1).
One assignment per line, no white space around
=,
shell quoting rules apply.
*
A line whose first non-blank character is
#
is a comment. Blank lines are ignored.
*
RETENTION
is a bash associative array and must be declared before use, with
declare -A RETENTION
as the first statement of the file. Without the declaration the
RETENTION[type]=N
assignments silently become assignments to element 0 of an indexed array and
the policy has no effect.
*
Anything else valid in a shell script is accepted, because the file is
executed. Do not rely on this: the consumers only read the variables listed in
KEYS
below, and any command in the file runs with the privileges of the job.
Policy resolution
The policy of a dataset is the value of its
site:snappolicy
ZFS user property, where
site
is the value of
config/site
on the SMF instance the job runs against
(svc:/omnicube/sysadm:default
unless overridden; see
ENVIRONMENT).
The property value is a bare policy name without the
.conf
suffix. It is resolved by
get_snap_policy_path
in
omnicube_utils(3)
(see also
oc_policy(3))
in this order:
1.
An unset property, an empty value, or the ZFS "no value" placeholder
-
is normalised to the name
default.
2.
/var/opt/omnicube/backup/policies/policy.conf
is used if it exists. This is the site-local directory: a file placed here
shadows the packaged policy of the same name and survives a package upgrade.
3.
Otherwise
/opt/omnicube/backup/policies/policy.conf
is used. This is the packaged directory, and the only file the package ships
into it is
default.conf.
4.
If neither path exists the job logs
Backup policypolicydoes not exist
through
error()
(syslog plus a throttled
oc-alert
mail) and skips the dataset. No snapshot is created, kept or sent for it.
Overriding is therefore per file and per dataset, never per key: a policy file
is used
whole
or not at all. There is no inheritance from
default.conf
into another policy and no merging of individual keys. Each consumer clears
the policy variables with
unset
before sourcing, so a key a policy does not set is
unset
for that dataset rather than left over from
default.conf
or from the dataset processed just before it. Every policy file must
consequently be self-contained: copy
default.conf
and edit it rather than writing a file that sets only the keys that differ.
autocleansnap(8)
is the one exception, and it merges across
instances
rather than across files. It enumerates every
svc:/omnicube/sysadm
instance, collects the distinct policies that the instances' own
site:snappolicy
properties select for one dataset, and keeps, for each snapshot type, the
highestRETENTION[type]
any of those policies defines — the most conservative value wins, so no
instance's policy can cause another instance's snapshots to be destroyed
early. The
BKP*
keys are
not
merged: remote cleanup is performed once per policy, each with its own
destination.
KEYS
Each key is introduced by its units and its default, in parentheses. The
default given is the value assigned in the shipped
default.conf;
"unset" means the key is absent from
default.conf
and the consumers' fallback applies.
Retention
RETENTION[type]
(number of snapshots; see below.) How many snapshots of cadence
type
to create and to keep.
type
is one of
frequent,
hourly,
daily,
weekly
or
yearly.
The shipped
default.conf
sets
RETENTION[yearly]=0,
RETENTION[weekly]=1,
RETENTION[daily]=4,
RETENTION[hourly]=6
and
RETENTION[frequent]=0.
The value is interpreted differently by the two jobs, and the difference
matters:
0
or unset means
do not create
snapshots of this type; a value greater than 0 means create one on every tick
of that cadence (subject to the
-time
and
-day
gates below). The numeric value itself is not a limit here.
unset means
leave existing snapshots of this type alone
— they may be managed by something outside these policies. A value of
0
is an explicit "keep none" and destroys every snapshot of that type. A value
N
greater than 0 keeps the
N
newest and destroys the rest, oldest first.
A value that is not a string of digits is a configuration error. Both jobs log
a warning naming the policy and the type and then skip that type
(autosnap
skips creation, and
autocleansnap
skips cleanup) rather than treating the value as 0, which shell arithmetic
would otherwise do.
Deletion is additionally gated by the per-snapshot safety properties
site:bckcandel,site:insync
and
site:remotebackup;
a snapshot that a remote-backed instance has not yet confirmed as sent is
kept regardless of the retention count. See
autocleansnap(8).
RETENTION[type-time]
(hour of the day, 0-23; unset.) Read by
autosnap(8)
only. When set, a snapshot of that type is created only on the run whose
date +%H
equals this value; on every other run the type is skipped. Unset means create
on every run of the job, i.e. at whatever cadence
cron(8)
invokes it. Leading zeros are accepted (the value is normalised with
printf's
%d
conversion), so
03
and
3
are the same hour.
RETENTION[type-day]
(day of the week, 1-7, Monday to Sunday; unset.) Read by
autosnap(8)
only. When set, a snapshot of that type is created only on the run whose
date +%u
equals this value. Unset means every day. Combines with
RETENTION[type-time]
— both gates must match on the same run.
Replication
BKPUSER
(user name;
JDOE.)
Login on the backup host used for
ssh,
zfs recv
and remote
zfs destroy.
Validated with
validate_name
before it reaches a command line, so it must begin with an alphanumeric
character followed by alphanumerics, dot, underscore or hyphen. The shipped
value is a placeholder and must be changed.
autosync(8)
skips a dataset whose policy fails this check;
autocleansnap(8)
warns and disables remote cleanup for that policy but still cleans locally.
BKPHOST
(host name;
EXAMPLE.COM.)
Backup host. Same validation and the same failure behaviour as
BKPUSER,
and likewise a placeholder in the shipped file. Both jobs pre-flight the host
with a non-interactive
ssh
probe
(ssh_reachable
in
oc_ssh(3))
before locking anything, and skip the dataset when it fails; the host key must
already be in
known_hosts
because
StrictHostKeyChecking=yes
is enforced.
BKPDS
(ZFS dataset;
omnicube/backup.)
Destination dataset on the backup host. Its meaning depends on
IS_SYNC:
IS_SYNC=1
root-pool replacement.
The local dataset's top-level pool name is replaced by
BKPDS,
the rest of the path being preserved, so
rpool/zones/VM/root
becomes
BKPDS/zones/VM/root.BKPDS
must therefore be a pool or a single dataset with no path components of its
own.
IS_SYNC=0
legacy container mode.
On a first sync the whole local path is nested under
BKPDS,
so
rpool/zones/VM
becomes
BKPDS/rpool/zones/VM.
If the backup host already holds a dataset whose leaf name matches the zone,
autosync(8)
sends under
that
dataset's parent instead, so an existing replication chain is continued rather
than duplicated. Because the destination depends on remote state,
autocleansnap(8)
does
not
clean remote snapshots in this mode; they are left to their own cycle.
In both modes the value is validated with
validate_dataset
(alphanumerics, dot, underscore, hyphen and
/).
It is required unless
BKPZP
is set.
BKPZP
(ZFS dataset; unset.)
Zones-path mirror.
When set it takes precedence over
BKPDS
and over
IS_SYNC:
the destination is
BKPZP
followed by whatever follows the first
/zones/
segment of the local dataset name, so with
BKPZP=dpool/zones
both
rpool/zones/VM
and
tank/zones/VM
map to
dpool/zones/VM.
A dataset with no
/zones/
segment cannot be mapped: the dataset is skipped with a warning.
autosync(8)
also warns when
BKPZP
and
BKPDS
are both set, and asks for
BKPDS
to be removed from the policy.
IS_SYNC
(0
or
1;
0.)
Selects the
BKPDS
mode described above. Any other value is a configuration error:
autocleansnap(8)
warns and treats it as
0,
which disables remote cleanup for that policy. Note that this key has nothing
to do with SYNC and ASYNC
zones
— it names the replication layout only.
IS_ENCRYPT
(0
or
1;
0.)
Read by
autosync(8)
only. When 1, the receive side runs
zfs recv -x encryption
so the sent
encryption
property is discarded and the received dataset takes the encryption of its
parent on the backup host. Use it when the source is unencrypted and the
destination pool encrypts, or the reverse. Any value other than 1 means no
option is passed.
BKPPRIV
(bare command name; empty.) Privilege prefix for the
zfs(8)
commands
(create,
recv,
destroy)
that
autosync(8)
and
autocleansnap(8)
run
on the backup host.
Leave it empty when the backup host is Linux (where
pfexec
does not exist), when
BKPUSER
is
root,
or when the destination datasets are delegated with
zfs allow.
Set it to
pfexec
when the backup host is an illumos system and
BKPUSER
is a non-root account holding an RBAC profile. Only a bare command name is
accepted — no arguments, no second command; a value containing anything
else is ignored with a diagnostic on stderr and in syslog
(_bkp_priv
in
omnicube_utils(3)).
The unprivileged probes are deliberately left unprefixed, so a missing
privilege grant surfaces as a failed destroy rather than as a missing
snapshot.
EXAMPLES
Example 1: the shipped default.conf
Keep six hourly, four daily and one weekly snapshot, create no frequent and no
yearly snapshots, and configure no usable replication destination (the
BKP*
values are placeholders):
Example 2: a site policy with timed dailies and a real destination
/var/opt/omnicube/backup/policies/prod.conf,
keeping 14 dailies taken at 03:00 and 8 weeklies taken on Sunday, replicating
to an illumos backup host with a non-root account:
Attach it to a dataset and let the jobs pick it up (the
efit
prefix below is this site's
config/site
value):
pfexec zfs set efit:snapshotting=yes rpool/zones/zvm01
pfexec zfs set efit:remotebackup=yes rpool/zones/zvm01
pfexec zfs set efit:snappolicy=prod rpool/zones/zvm01
Example 3: local-only high-cadence snapshots
A policy for a dataset that needs minute-granularity local recovery but no
off-node copy. Note that
@frequent-
snapshots are excluded from replication by
autosync(8)
in any case:
declare -A RETENTION
RETENTION[frequent]=60
RETENTION[hourly]=24
RETENTION[daily]=7
weekly
and
yearly
are left unset, so
autocleansnap(8)
does not touch snapshots of those types even if some exist, and
BKPUSER
and
BKPHOST
are absent, which is harmless as long as the dataset is not marked
site:remotebackup=yes.
Example 4: checking which policy a dataset resolves to
zfs get -H -o value efit:snappolicy rpool/zones/zvm01
ls -l /var/opt/omnicube/backup/policies/prod.conf \\
/opt/omnicube/backup/policies/prod.conf
The first of those two paths that exists is the file in force.
FILES
/opt/omnicube/backup/policies/default.conf
The packaged default policy. Overwritten by package upgrades; do not edit it
in place.
/opt/omnicube/backup/policies/policy.conf
Packaged policies. Searched second.
/var/opt/omnicube/backup/policies/policy.conf
Site-local policies. Searched first, and the correct place for local
modifications, including a local
default.conf.
/opt/omnicube/lib/common/utils.sh
Defines
get_snap_policy_path
and
_bkp_priv,
which implement the resolution and validation described here.
ENVIRONMENT
OC_SMF_INSTANCE
Selects the
svc:/omnicube/sysadm
instance whose
config/site
property supplies the ZFS property prefix, and therefore which
site:snappolicy
property is consulted. Unset means the
default
instance.
autosync(8)
also accepts
-iNAME,
which overrides the variable.
autocleansnap(8)
ignores it and iterates over every instance.
SECURITY
A policy file is
executed
by
bash(1)
inside jobs that go on to run
pfexec zfs
locally and privileged commands over
ssh
remotely. Anything in the file runs with those jobs' privileges, so write
access to a policy file is equivalent to root on the node. Both policy
directories and every
.conf
in them must be owned by root and must not be group or world writable. The
validate_name
and
validate_dataset
checks on
BKPUSER,
BKPHOST,
BKPDS
and
BKPZP,
and the bare-command-name check on
BKPPRIV,
exist to stop a typo or a copy-and-paste error from injecting a second
command into a remote command line; they are not a containment boundary
around an untrusted policy file.
autosnap(8)
creates only
frequent,
hourly,
daily
and
weekly
snapshots.
RETENTION[yearly]
is honoured by
autocleansnap(8),
so it governs the retention of yearly snapshots created by some other means,
but setting it does not cause any to be created.
Snapshot names encode the cadence and the time, from
date
at creation:
frequent-YY-WW-DD-HH-MM,hourly-YY-WW-DD-HH,daily-YY-WW-DD,weekly-YY-WW
and
yearly-YY.
Retention is applied per type by matching the type prefix, so renaming a
snapshot takes it out of the policy's control.
Changing a retention count takes effect on the next run of the jobs; lowering
one deletes the excess snapshots at that point, subject to the safety
properties. Changing
BKPDS,
BKPZP
or
IS_SYNC
on a policy that is already replicating changes the destination path and
starts a new full send, because no snapshot at the new destination is marked
site:insync=yes.
A dataset whose
site:snappolicy
names a file that does not exist is skipped entirely and generates an alert
mail on every run of every job. Create the policy or clear the property.
man5/default.conf.5generated 2026-09-02 05:17 CEST