diff¶
The diff command compares two OPNsense configurations and shows what changed between them. Unlike a raw text diff, it understands the structure of OPNsense configs -- it groups changes by section, scores their security impact, and can detect rule reordering separately from content changes.
When to use it:
- Reviewing what changed between two config backups (before/after a maintenance window)
- Change management auditing -- documenting exactly what was modified and why it matters
- Security review of configuration changes, filtering to only security-relevant differences
- Comparing configs across environments (production vs staging)
Usage¶
Flags¶
| Flag | Short | Default | Description |
|---|---|---|---|
--format |
-f |
terminal |
Output format: terminal, markdown, json, html |
--output |
-o |
stdout | Output file path |
--mode |
-m |
unified |
Display mode: unified, side-by-side (terminal only) |
--section |
-s |
all | Comma-separated list of sections to compare |
--security |
false |
Show only security-relevant changes | |
--normalize |
false |
Normalize displayed values and leave out formatting-only changes | |
--detect-order |
false |
Detect rule reordering without content changes |
For global flags (--verbose, --quiet, --config, etc.), see Configuration Reference.

Display Modes¶
The --mode flag controls how changes are presented in terminal output.
| Mode | Description |
|---|---|
unified |
Shows changes inline with + and - markers, similar to git diff. This is the default and works well in narrow terminals. |
side-by-side |
Shows old and new values in adjacent columns. Easier to scan when comparing many small changes, but requires a wider terminal. Only applies to terminal output -- other formats ignore this flag. |
Security Filtering¶
Every change detected by diff is scored for security impact using pattern-based rules. The --security flag filters the output to show only changes that have a security impact, hiding cosmetic or low-risk differences.
This is useful when reviewing a large set of changes and you only care about what affects the security posture -- for example, during a change management review or incident investigation.
Security Impact Levels¶
| Level | Examples |
|---|---|
| HIGH | Permissive any-to-any firewall rules, overly broad access |
| MEDIUM | Firewall rule removals, user account changes, NAT mode changes, port forwarding modifications, WebGUI protocol changes, interface enable/disable, an alias added, removed, disabled or changed in what it matches while an enabled rule or port forward uses it |
| LOW | New firewall rules, DNS server changes, user modifications, the same alias changes when no enabled rule or port forward uses the alias |
Normalization¶
OPNsense configurations can contain cosmetic differences that are not meaningful -- extra whitespace or an IPv6 address written in its long form. The --normalize flag normalizes the values the diff displays and leaves out a change whose two values differ only in that formatting.
When normalization is enabled:
- Whitespace is standardized
- IPv6 addresses are shortened to their canonical form, an IPv4-mapped IPv6 address such as
::ffff:10.0.0.1is shown as its IPv4 form, and a CIDR prefix length loses leading zeros
Normalization is applied after the comparison, to the displayed values. Whitespace is collapsed in every value, rule summaries included. Addresses are normalized only in a value that is an address or a CIDR by itself; a DHCP range or a list of several DNS servers is not, so leading zeros inside one are reported as a change. A change whose value is displayed by itself, such as an interface address or an interface description, is skipped when its two values are identical after normalization. A digits-only value such as a hostname or a VLAN tag is never treated as formatting: 007 and 7 are different names, so that change is reported. The same holds for a value shaped like a numeric range or a date, such as a description of 2024-01 and 2024-1. A zero-padded IPv4 octet, such as 172.016.000.001, is never treated as formatting: parsers in the inet_aton family read a leading zero as octal, and validate rejects the form, so the change is reported with the value as written.
A change to a firewall rule, an outbound NAT rule, a port forward, a user or an alias's members is never skipped by --normalize, including one that only reformats a field. Rules and port forwards are displayed as a one-line summary of a few fields, and a modified user has no displayed value, so a rule that loses its logging appears with the same text on both sides, and a user that gains an API key appears with no old or new value.
# Skip cosmetic differences
opndossier diff --normalize old-config.xml new-config.xml
# Combine with security filtering for a focused review
opndossier diff --security --normalize old-config.xml new-config.xml
Rule Reordering Detection¶
Firewall rule order matters in OPNsense -- rules are evaluated top to bottom, and the first match wins. The --detect-order flag identifies rules that were reordered without any content changes. These are reported separately from added/removed/modified changes so you can see at a glance whether rule precedence shifted.
By default, reordering detection is off because it requires comparing every rule against every other rule, which adds processing time for large rulesets.
Available Sections¶
system, firewall, nat, interfaces, vlans, dhcp, users, routing, aliases
A rule that uses an alias stores only the alias name, so a change to what the alias contains is not a change to the rule. The aliases section reports it instead: aliases are paired by name, and each changed field gets its own entry. The fields are the alias's type, members and description, and on OPNsense whether it is enabled, its address families, the interface it tracks, its JSON path expression and its expire time. A legacy OPNsense url alias lists its URLs and the addresses it last fetched, since the release decides which of them the firewall loads.
An alias that is added or removed, or that changes in any of those ways other than its description, is rated medium when an enabled firewall rule, outbound NAT rule or port forward uses it, directly or through a group alias, and low otherwise. An outbound NAT rule counts only in hybrid or advanced mode, the two modes that load it. Rules of every action count: an alias that a block rule uses losing a member lets that member through to the rules below. Disabling an alias does that to all of its members, since OPNsense loads a disabled alias with no members, and a rule that inverts the alias then matches every address. OPNsense leaves the table of an external or internal alias as it is, so for those types the entry overstates the change. The expire time of an external alias sets when OPNsense purges an unused address from its table, so lowering it on an alias a block rule uses can let addresses through early. A description change is not rated, so --security leaves it out.
A rule holds a reference to the alias it names, so adding or removing an alias that a rule still names also reports that rule as modified, with the same text on both sides. The aliases entry is the one that says what changed.
Examples¶
# Compare two configs in terminal
opndossier diff old-config.xml new-config.xml
# Generate markdown diff report
opndossier diff old-config.xml new-config.xml -f markdown -o changes.md
# Compare only firewall and NAT sections
opndossier diff old-config.xml new-config.xml -s firewall,nat
# Show only security-relevant changes with normalization
opndossier diff old-config.xml new-config.xml --security --normalize
Related¶
- CLI Reference —
diff-- auto-generated exhaustive flag list - convert -- generate full documentation from a single config
- Configuration Reference -- global flags and settings