Migrating from OPNsense
Migrate with spare hardware. Your OPNsense router stays untouched and in service until you decide to swap the cables.
1. Export a configuration#
In OPNsense: System → Configuration → Backups → Download configuration. Leave "Encrypt this configuration file" unticked; the importer reads plain XML. The file contains secrets (WireGuard private keys, password hashes, certificates): keep it on an encrypted disk and delete it when done.
2. Import#
pebble import opnsense config-gateway-20261016.xml -o import --map wan=igb0,lan=igb1
--mapassigns OPNsense interface identifiers (wan,lan,optN) to devices on the new machine. Without it, the OPNsense device names are kept only if they look valid, andmigration.interfaces_confirmedstaysfalse;pebble applyrefuses to deploy until you have checked them.--extract-keyswrites WireGuard keys found in the export toimport/keys/(directory 0700, files 0600). Without it, the secrets manifest tells you which keys to copy by hand. Keys never appear inpebble.yamlor the report.
Outputs:
| File | Contents |
|---|---|
pebble.yaml |
translated configuration, already accepted by pebble validate |
secrets-manifest.yaml |
each secret file the router needs, its source and status (never the value) |
migration-report.md |
everything translated, partial, unsupported, ambiguous, dangerous, conflicting or needing verification |
The importer never contacts any system and never applies anything.
3. Read the report#
Sections, most important first:
- Potentially dangerous — exposure to the Internet (WAN pass rules from anywhere, port forwards without source restrictions), placeholders that are not equivalent (for example a PPPoE WAN set to DHCP), dropped full-tunnel peers, scheduled pass rules that would become permanent.
- Conflicts — elements removed because Pebble's validator rejected them (for example a reservation on the router's own address, or a peer network overlapping the LAN), with the reason.
- Requires manual verification — floating rules, negated addresses, automation rules, interface identities, unsigned forwarded domains.
- Secrets, Not translated, Ambiguous, Partially translated, Assumptions, Skipped (disabled) and Translated.
Untranslatable elements are reproduced (with secrets redacted) so nothing is silently discarded.
Supported layouts#
| Feature | OPNsense layouts recognised |
|---|---|
| Interfaces | <interfaces> (wan, lan, optN) |
| DHCP | legacy ISC <dhcpd>; Kea <OPNsense><Kea><dhcp4>; dnsmasq <OPNsense><dnsmasq> (best effort, from documented field names) |
| DNS | legacy <unbound>; <OPNsense><unboundplus> (hosts, aliases, domain overrides, query forwarding, DNSBL) |
| Gateways | <gateways> and <OPNsense><Gateways> |
| Aliases | <OPNsense><Firewall><Alias> host, network and port aliases with literal content |
| Firewall | <filter><rule> (interface rules); automation rules are reported, not translated |
| NAT | <nat><rule> port forwards; outbound mode; one-to-one and NPT reported |
| WireGuard | <OPNsense><wireguard> servers and clients |
The detected layout and model versions are listed at the top of the report.
All fixtures in tests/fixtures/opnsense are synthetic; please report
exports from real releases that translate poorly (with secrets removed).
Semantics to be aware of#
- OPNsense rules are translated literally, in order, per interface.
allow_lan_to_wanis set tofalseso that the default "allow LAN to any" rule (if present) governs, exactly as before. rejectbecomesblock(silent drop).lanip/wanip/(self)becomeself(all router addresses).- Aliases are expanded when they contain literal addresses or ports; nested aliases, URL tables and host names are not.
- DNSBL predefined lists cannot be mapped to URLs reliably; add equivalent HTTPS feeds yourself.
- SSH access becomes
allow_from: [<LAN>].
4. Prepare the test router#
On the spare FreeBSD machine (see installation.md):
sh scripts/install.sh
pebble interfaces
cp import/pebble.yaml /usr/local/etc/pebble/pebble.yaml
vi /usr/local/etc/pebble/pebble.yaml # check devices; set migration.interfaces_confirmed: true
install -m 0600 -o root import/keys/wg0.key /usr/local/etc/pebble/keys/ # if extracted
pebble validate && pebble plan --diff
5. Test in isolation#
Connect the test router's WAN to a spare port on your existing LAN (it will get a private WAN address — that is fine for testing) and its LAN to an isolated switch with a test client. Do not connect its LAN to your production LAN: two DHCP servers would compete.
pebble apply && pebble confirm
Then verify, from the test client:
- DHCP: the client gets an address, gateway and DNS server
(
pebble dhcp leases). - DNS: local names, blocked names, external names
(
pebble dns test <name>on the router). - NAT and Internet access: browse;
pebble firewall test --in lan .... - WireGuard: point a test peer (or the remote site, during a maintenance
window) at the test router;
pebble vpn test <peer>. - Site-to-site rules: confirm allowed and denied flows.
- Rollback: apply a deliberately wrong SSH policy and do not confirm; check that it is restored after the rollback window.
- Reboot and check
pebble status,pebble diagnose. - Compare behaviour with OPNsense for a day if you can.
6. Switch over#
In a maintenance window, from the local console of the new router:
- Update remote WireGuard peers' endpoints to the new public address if it changes.
- Move the WAN cable from OPNsense to the Pebble router and the LAN cable to the production switch.
pebble diagnose; renew a client's DHCP lease; test DNS and Internet.- Keep the OPNsense box unchanged and powered off for quick fallback: swapping the cables back restores the old network.