You add tun0 to the right zone, the application starts working, and the fix evaporates at the next reconnect. Running the same command again works, which makes it look like firewalld forgot. It did not forget; something with a stronger claim to that interface reassigned it.

The zone survives until the interface is recreated

A firewalld zone assignment for an interface is a binding, and bindings are tied to the interface object as it exists now. A VPN client that tears down the device and recreates it produces a new object, and the new one starts from whatever the assigning component says. That is why the symptom repeats at every reconnect and not at every packet.

The same logic explains reboot behavior: runtime bindings do not survive a restart, and permanent bindings only apply to devices firewalld believes it owns.

Before changing anything, find out which component assigns the zone.

Read three things before changing anything

Record the zone the interface currently has, the active zones with their interfaces, and the permanent interface list for the zone you intend. Run those queries before the change, after a reconnect and after a reboot. Comparing the three results tells you whether the binding is being replaced, ignored or merely applied late. If the permanent list already names the device while the runtime zone disagrees, something is overriding your edit.

Also record the actual device name at each step, because a tunnel that lands on tun0 today can land on tun1 tomorrow.

A saved binding to a generic name is not an identity check, and two minutes of recording saves an hour of guessing.

When NetworkManager owns the connection

If NetworkManager manages the VPN connection, its profile carries the zone and passes it to firewalld when the connection activates. A permanent firewalld interface binding for the same device may then be ignored, because NetworkManager is the owner. Set the zone on the connection, and confirm you are editing the VPN profile rather than an unrelated physical connection or a temporary record.

Changing that property can affect an active connection immediately.

Keep a management path available and read the destination zone's policy before applying it. Editing an ifcfg file's ZONE field is correct only where that file is still the configuration source.

When firewalld owns the binding

For a standalone tunnel with a deliberately stable interface name, a permanent interface change is the right tool. Apply it through the reload or lifecycle your deployment uses, then inspect runtime state, because a permanent-only edit describes intent rather than what is active. The two can disagree for as long as it takes to reload.

If the interface name is dynamic, maintain the assignment through the connection lifecycle instead of racing whichever tunnel claims the name first.

Do not drop the interface into trusted just to make the application work.

Test the reload, the reconnect and the reboot

A change is only finished when it survives all three.

Reload first, because that is where runtime-only edits disappear. Then reconnect the tunnel, which is where a replaced interface loses its binding. Then reboot, which is where an owner mismatch finally shows up. Record the zone at each stage and compare it with the intent you wrote down, and note which step produced the difference.

If the zone is correct and the application still fails, the problem is the zone's rules or a source-based classification rather than the membership. The firewalld documentation on connections and interfaces is the reference for how ownership is meant to work.

Write the owner, the value and the expected result into the runbook, and the next reassignment will be explainable.

Artiglyn mynjee