My OPNsense firewall was still on 25.7.11_9, while the firmware page offered 26.7.7. That is two major releases in one go: 26.1 moved ISC DHCP out of the core, and 26.7 switched the base system from FreeBSD 14.3 to 15.1 and OpenSSL from 3.0 to 3.5. To make it more interesting, the box was installed on UFS years ago, so there was no ZFS boot environment or snapshot to roll back to. This post covers what changed, what I cleaned up beforehand, how the upgrade went and what the logs said afterwards.
What changed between 25.7 and 26.7
26.1: the DHCP shake-up
- ISC DHCP moved to a plugin (
os-isc-dhcp). On upgrades it is installed automatically, so an existing ISC setup keeps working. On fresh installs it is not installed. ISC DHCP itself is end of life and gets no more security fixes. - Dnsmasq is now the default for DHCPv4, DHCPv6 and router advertisements on new installs. The docs position Dnsmasq as the replacement for small and medium setups and Kea for larger, HA-oriented ones. Dnsmasq cannot do DHCPv6 prefix delegation.
- The IPv6 mode Track interface got a sibling called Identity Association, which does the same but does not automatically start ISC DHCPv6 and radvd.
- Port forwarding is now called Destination NAT and the new MVC rules GUI takes over.
26.7: new base system
- FreeBSD 15.1, OpenSSL 3.5, OpenVPN 2.7, PHP 8.5, Python 3.13.
- The old static firewall rules pages moved to the
os-firewall-legacyplugin. The rules keep working either way, and there is a migration assistant to move them to the new GUI. - Since 26.7.5 the Outbound NAT page lives in that legacy plugin too, with a migration assistant to Source NAT.
- The release notes warn that third-party repositories can break the upgrade because of the OS and OpenSSL jump.
- The built-in periodic local backup settings were removed.
The good news: an in-place upgrade keeps an existing ISC DHCP setup running. Migrating to Kea or Dnsmasq can (and should) be a separate job on another day. One change at a time.
The safety net without ZFS
An in-place upgrade cannot convert UFS to ZFS. The cleanest route would have been a fresh 26.7 install on ZFS on a new SSD with the old disk kept as a perfect rollback, but I had no spare SSD. So the plan was:
- A full disk image is the real rollback on bare metal (for example Clonezilla to a NAS). On a VM, a hypervisor snapshot does the same job. A
config.xmlrestores settings, not a working system. - A fresh
config.xmldownload before each major step. - A second, independent config backup channel (more on that below).
- Console access in case the web GUI does not come back.
Pre-flight 1: review the installed plugins
Most of my plugins come from the official OPNsense repository (ACME, CrowdSec, ddclient, FreeRADIUS, nginx, ntopng, Redis, netdata, Telegraf, NUT, apcupsd, ZeroTier, OpenVPN legacy and so on). Three things stood out:
- Zenarmor (
os-sensei,os-sensei-updater,os-sunnyvalley): the only third-party repository and the biggest risk for the FreeBSD 15 jump. - Three red “missing” entries (
os-homeassistant-maxit,os-speedtest-community,os-zabbix4-proxy): leftovers from the old mimugmail community repository. They are only stale references in the config. - os-gdrive-backup: the Google Drive backup plugin was moved to tier 3 in 26.1, so I did not want to rely on it alone.
The CPU microcode plugins were not installed. That matters because the 26.7 notes recommend removing them before the upgrade on setups where early microcode loading is flaky.
Pre-flight 2: check the package repositories
ls /usr/local/etc/pkg/repos/
# FreeBSD.conf FreeBSD.conf.sample OPNsense.conf OPNsense.conf.sample
# SunnyValley.conf SunnyValley.conf.sample
cat /usr/local/etc/pkg/repos/FreeBSD.conf
# FreeBSD: { enabled: no }
# FreeBSD-kmods: { enabled: no }
No mimugmail repository left, and the FreeBSD repositories are disabled as they should be. Mixing upstream FreeBSD packages into an OPNsense major upgrade is a reliable way to get into trouble. That left Sunny Valley (Zenarmor) as the only non-OPNsense repository.
Pre-flight 3: Zenarmor
Zenarmor had a rough start with 26.7. On release day, Sunny Valley asked users to hold off until compatibility was confirmed, and people who added the repository on 26.7 found that it broke package list updates because it was built for the older FreeBSD version. Compatibility was announced later, and Zenarmor 2.7 (end of September 2026) fixed a crash report on 26.7. After upgrading, some users had to reinstall the Elasticsearch reporting database or reboot once more.
I was already on 2.7, so keeping it would probably have been fine. But when I opened the Zenarmor page, it showed the initial setup wizard: installed, never configured, not inspecting any traffic. 305 MiB of packages and a third-party repository in the upgrade path for nothing. Decision made: uninstall.
- Click Uninstall in the Zenarmor wizard.
- Remove any remaining
os-sensei,os-sensei-updaterandos-sunnyvalleyunder System → Firmware → Plugins. - Check that the repository file is gone and the package list refreshes without errors.
ls /usr/local/etc/pkg/repos/
# FreeBSD.conf FreeBSD.conf.sample OPNsense.conf OPNsense.conf.sample
From here on, every installed package came from the official OPNsense repository. If I want Zenarmor again, a fresh install on 26.7 is cleaner anyway.
Pre-flight 4: Git config backup to Gitea
Instead of trusting a tier 3 Google Drive plugin, I set up os-git-backup against my self-hosted Gitea. Every configuration change becomes a commit, which gives a full history with diffs, which is exactly what you want after a major upgrade.
- Create an empty private repository in Gitea, default branch
main, without initialising it (no README), so the first push does not conflict. - Generate a dedicated key pair without a passphrase (the plugin cannot enter one):
ssh-keygen -t ed25519 -C "opnsense-git-backup" -f ~/opnsense_backup -N ""
- Add the
.pubkey in Gitea under the repository’s deploy keys, with write access enabled. - In OPNsense under System → Configuration → Backups → Git, fill in URL, branch, private key and user name, then click Setup/Test Git.
It took me a few attempts. These were the traps:
- User name is
git, not your Gitea account. Gitea identifies you by the key. - Do not put
git@in the URL as well. The plugin adds the user name itself, and I ended up with things likegit@git@hostin the log. - Wrong SSH server.
Permission denied (publickey,password)means you are talking to the host’s OpenSSH, not to Gitea. Gitea’s own SSH only offerspublickey. In my Docker setup, Gitea’s SSH listens on port 222. - Wrong address. My public hostname resolves through a wildcard record to a reverse proxy elsewhere, so from inside the network it pointed to the wrong machine. The internal IP works.
The working configuration:
URL: ssh://<gitea-internal-ip>:222/<user>/opnsense-config.git
Branch: main
User Name: git
Password: (empty)
SSH key: contents of opnsense_backup (the private key)
To debug, test from the OPNsense shell first. Gitea answers with a greeting if the key is accepted:
cat > /tmp/opnsense_backup # paste the private key, Enter, Ctrl+D
chmod 600 /tmp/opnsense_backup
ssh -i /tmp/opnsense_backup -p 222 -T git@<gitea-internal-ip>
rm /tmp/opnsense_backup
Step 1: 25.7.11 → 26.1
System → Firmware → Status, start the major upgrade, wait for the reboot. A few minutes later the box was on 26.1.2 (FreeBSD 14.3, OpenSSL 3.0.19). Before going further I checked:
- Services widget: everything that ran before was running again. Only ISC DHCPv6, iperf (only runs on demand) and Net-SNMP were stopped, and none of them were in use.
- DHCP: the menu now shows ISC DHCPv4 [legacy] with all my interfaces (LAN, Guest, IoT, ZeroTier) and their settings intact.
- Leases: all Shelly plugs had renewed their leases with today’s date. The simplest proof that DHCP works.
Then the regular updates within 26.1 up to 26.1.11_10 (FreeBSD 14.3-p16, OpenSSL 3.0.21), another reboot, and the same services check. The 26.7 upgrade is only offered from the latest 26.1 release.
Step 2: os-firewall-legacy, then 26.1 → 26.7
I installed os-firewall-legacy on 26.1 before the jump. On 26.1 it is only a 750-byte placeholder, because the legacy pages are still part of the core there:
New packages to be INSTALLED:
os-firewall-legacy: 0.1 [OPNsense]
750 B to be downloaded.
During the 26.7 upgrade it is replaced by the real plugin, so the old rules pages stay available without any extra step.
The 26.7 upgrade itself took noticeably longer to reboot than the previous steps. That is expected: new kernel, new base system, and roughly 440 packages reinstalled. Do not interrupt it if the GUI is gone for a few minutes. When it came back, it was already on 26.7.7 with FreeBSD 15.1. The whole thing, from 25.7 to 26.7.7, took about half an hour.
After the upgrade: reading the logs
The services widget looked exactly like on 26.1, and the boot log showed all interfaces, DHCP, OpenVPN, WireGuard, ZeroTier and the WAN coming up. Going through System → Log Files → General turned up a few things, most of which were older than the upgrade:
- ddclient could not determine the IP:
checkip: error: argument -s/--service: invalid choice: 'dyndns'. Thedyndnscheck-IP method no longer exists. Behind a modem with a private WAN address, Interface would publish the wrong IP, so a web service likeipify-ipv4is the right choice. This error was already there on 26.1. - An orphaned package from the old PHP stack:
php83-pecl-mongodb has a missing dependency. Fix:pkg delete php83-pecl-mongodb. - WireGuard could not resolve a peer hostname at boot. That was already in the September logs, so it is a DNS or timing issue for later, not an upgrade problem.
- snmpd failed to start, as before. Only relevant if something polls SNMP.
- Repeated
dhcp6c: Sending Solicit on igb0with growing intervals: the WAN asks for IPv6, the cable modem sends router advertisements but never answers. Harmless. If you do not use IPv6 on the WAN, set the IPv6 configuration type to None and the noise stops. - A syslog-ng target was unreachable during the upgrade and reconnected on its own.
Things to test on purpose after 26.7: an OpenVPN client actually connecting (OpenVPN 2.7 and OpenSSL 3.5 can reject old legacy ciphers), WireGuard and ZeroTier tunnels, and services published through Destination NAT.
Result
- OPNsense 26.7.7 on FreeBSD 15.1, still on UFS
- ISC DHCP keeps running as a legacy plugin, all leases fine
- Only official OPNsense packages installed, Zenarmor removed
- Config changes are committed to Gitea automatically
- Total time: about 30 minutes for the upgrade itself, plus preparation
Still on the list, each as its own session: migrate ISC DHCP to Kea, move the legacy firewall and outbound NAT rules to the new GUI with the migration assistants, fix ddclient, and one day reinstall on ZFS so the next major upgrade comes with a snapshot.