Skip to content

Sync & Delivery

The sync mechanism is how configuration changes propagate from NetDefense to devices. Whenever you modify a snippet, change a template, or reassign an OU, the affected devices need to receive the updated configuration.

NetDefense computes a SHA-256 hash of the expected configuration payload for each device, based on its current OU assignments, templates, snippets, and variables. This hash is compared against the hash stored from the device’s last successful sync.

  • SYNCED — the device’s active configuration matches the current expected hash. No action needed.
  • NOT SYNCED — the hashes differ, meaning configuration changes have been made since the device last synced.

The sync hash is calculated based on the origin policy and kept in the NetDefense database, not on the device itself.

In addition to the hash-based sync state, NetDefense tracks a drift status for each device. Where the sync hash answers “does the device have the latest configuration we sent?”, the drift status answers “is the device’s running configuration still what we last delivered?”.

Drift StatusMeaning
IN_SYNCThe running configuration matches the last-delivered configuration. No action needed.
DRIFTThe running configuration has diverged from the last sync. A manual change was likely made directly on the device.
NEVER_SYNCEDThe device has never received a sync. No baseline to compare against.
UNKNOWNDrift information has not yet been collected for this device.
ERRORNetDefense couldn’t build the device’s expected configuration, so there is nothing to compare against — see When a configuration can’t be built.

A DRIFT status does not block syncs — it’s informational. To push the expected configuration to a drifted device again, run ndcli sync apply --device <name> --force: without --force, a sync skips a device whose hash already matches, as the caution above says.

Syncs can be triggered manually or on a recurring schedule.

On Synchronize, set the filter and check it with Preview matching devices. Then switch Mode to On schedule, pick a schedule, and click Register. See Scheduling a sync.

This creates a scheduled task that fires on every tick of the named schedule. The target set — OU members, drift filter, etc. — is fixed at creation time.

Before running or scheduling a sync from the web console, use the filter builder to narrow the target set. Click Preview matching devices to see the exact list of devices that will receive the sync before committing. This works for both immediate runs and when attaching a schedule.

A future release will let devices opt into pulling new configuration on their own — the Auto-Sync column already shown in ndcli sync status is the placeholder for that flag. Until that ships, the column has no effect on behaviour: every sync still requires an explicit trigger.

Open Synchronize in the sidebar, set the filter (device name, OU, template or drift state), and click Preview matching devices. The Matching devices table lists each device with its OUs, its state, its auto-sync flag and when it last synced. See Synchronize.

On Synchronize, set the filter, check the Matching devices, and click Sync with Mode on Sync now. Force sync pushes even where the hashes already match. To sync one device, use Sync on its page; for a whole OU, Sync OU on the OU’s page. Each sync creates a task you can follow.

Before anything is sent, NetDefense builds each device’s configuration from its templates, snippets, and variables, and checks it. When the build fails, the device gets no task: ndcli sync apply lists the device under Errors with the reason, ndcli sync status shows it as ERROR (the reason is in the error field of -f json output), and scheduled syncs skip it until the cause is fixed. Common causes are a variable conflict, an undefined variable, a snippet that needs Superuser clearance, two snippets that share a uuid (SNIPPET_UUID_CONFLICT), and a rule, alias or host override the device’s agent is too old to apply in full (SNIPPET_AGENT_TOO_OLD).

Most snippet types behave symmetrically: attaching a snippet creates the entry on the device; detaching it (and triggering a sync) removes the entry on the device. The NetDefense agent identifies its own entries by an ownership marker — a UUID prefix or a name prefix, depending on the snippet type — and on every sync it diffs the device’s managed entries against the desired set from your templates, deleting anything in the former but not the latter.

A few snippet types are asymmetric by design: removing them from a template does not undo what was already applied on the device. These are always singleton configurations (one record per device, no concept of “delete the row”) where the safest default is to stop pushing changes rather than guess what “removed” should mean.

Snippet typeWhat detach doesHow to undo
ZABBIX_SETTINGSDevice keeps the last-applied Zabbix Agent configuration. Sync stops touching Services › Zabbix Agent › Settings.Push a ZABBIX_SETTINGS snippet with "enabled": false to disable the agent, or edit the original values directly in the OPNsense UI.
AUTH_ORDERThe device keeps whatever login order was last written for that facility (e.g. webadmin). NetDefense stops touching it.Push an AUTH_ORDER snippet with "order": ["Local Database"] and sync — the same reset decommission uses. See Authentication Servers.

The companion list snippet types (ZABBIX_USERPARAMETER, ZABBIX_ALIAS) follow the symmetric rule — detach + sync removes them from the device. AUTH_SERVER is symmetric too: NetDefense deletes an authentication server it created when its snippet is detached, unless something else on the device — a written login order among them — still references it. See Blocked deletes. TRUST_CA and TRUST_CERT are symmetric as well, with the same kind of exception: a certificate or CA that something on the device still uses is kept, and the sync fails until it’s unbound. See Removing a certificate or CA.

For symmetric snippet types — firewall aliases, firewall rules, Unbound host overrides / forwards / aliases / ACLs, Zabbix UserParameters, Zabbix item-key aliases, WireGuard servers/peers, authentication servers, certificates and CAs — “delete” is a well-defined operation: the agent knows which records it created (by a UUID prefix, a name prefix, or its own marker for authentication servers) and removes each one — through the OPNsense REST API for everything except authentication servers, which have none.

For a singleton config tree there’s no analogous operation. Imagine detaching ZABBIX_SETTINGS from a template: should that:

  1. Leave the device alone (current behavior) — last-applied settings persist, sync stops touching them.
  2. Reset to OPNsense defaults — the agent would have to know the plugin’s defaults, which drift across OPNsense versions.
  3. Disable the service entirely — surprise outage if the template was detached for an unrelated reason.

Option 1 is the conservative default. Detaching a singleton snippet is treated as “stop managing this config from NetDefense” rather than “revert this config on the device”. Disabling the service is an explicit operator action, not a side effect of template reorganization.

If future snippet types introduce additional singleton configurations, they’ll be added to the table above with the same semantics.