Documents the cosmetic but persistent warning during dnf upgrades: "/usr/sbin cannot be merged yet, /usr/sbin/ebtables points to /etc/alternatives/ebtables" Stale update-alternatives symlinks (not rpm-owned) block Fedora's /usr/sbin -> /usr/bin consolidation. Article covers root cause, investigation steps, and the fix (tear down + re-register with /usr/bin paths only). References the Ansible playbook fix_ebtables_usrmerge.yml that implements this fleet-wide. Applied 2026-04-19 across majorlab, majorhome, majormail, majordiscord.
5.2 KiB
| title | domain | category | tags | status | created | updated | ||||||
|---|---|---|---|---|---|---|---|---|---|---|---|---|
| Fedora usrmerge: ebtables Symlink Blocks Directory Consolidation | troubleshooting | fedora |
|
published | 2026-04-19 | 2026-04-19 |
Fedora usrmerge: ebtables Symlink Blocks Directory Consolidation
Symptom
Every dnf upgrade on Fedora 43 (and some earlier Fedora releases) emits a warning partway through the transaction:
/usr/sbin cannot be merged yet, /usr/sbin/ebtables points to /etc/alternatives/ebtables
When the upgrade is driven by Ansible, the warning contaminates the module's JSON output and surfaces in a play run as:
TASK [Upgrade all packages on CentOS/Fedora servers] ***
changed: [majorlab]
[WARNING]: Module invocation had junk after the JSON data:
/usr/sbin cannot be merged yet, /usr/sbin/ebtables points to /etc/alternatives/ebtables
changed: [majordiscord]
The upgrade succeeds — the warning is cosmetic — but it keeps firing on every run until the underlying state is cleaned up.
Why It Happens
Fedora's usrmerge transition turns /usr/sbin into a symlink to /usr/bin. The filesystem package's post-install scriptlet enforces that at every transaction: it walks /usr/sbin looking for any entity still pinned to the old path and refuses to consolidate until they're removed.
ebtables triggers this because update-alternatives can create registrations at /usr/sbin/<cmd> with targets in /etc/alternatives/<cmd>. Those symlinks:
- Are not owned by any rpm (confirmable with
rpm -qf /usr/sbin/ebtables→ "not owned") - Predate the usrmerge — they were created when
/usr/sbinwas still a real directory - Point to a target (
/etc/alternatives/ebtables) that in turn points back into/usr/sbin/ebtables-legacyor/usr/bin/ebtables-nft
Because these live outside rpm, no package upgrade can clean them up. The filesystem scriptlet detects the blocker and backs off.
Investigation
- Confirm which hosts are affected:
ansible fedora -m shell -a '[ -e /usr/sbin/ebtables ] && ls -la /usr/sbin/ebtables' - Inspect the alternatives registration:
Note whether the link points atupdate-alternatives --display ebtables/usr/bin/ebtables-nft(nft backend) or/usr/sbin/ebtables-legacy(legacy backend). Different Fedora images ship with different defaults. - Confirm ownership:
Both should report "not owned by any package." That's the signal.rpm -qf /usr/sbin/ebtables /etc/alternatives/ebtables
Fix
Tear down the alternative, delete the blocker symlinks, then re-register with /usr/bin paths on both sides of the registration so the scriptlet has nothing left to complain about.
# Capture current provider first (nft or legacy)
update-alternatives --display ebtables
# Remove the stale registration
update-alternatives --remove-all ebtables
# Clear the blocking symlinks (not rpm-owned)
rm -f /usr/sbin/ebtables /etc/alternatives/ebtables
# Re-register with /usr/bin paths — example for nft backend
update-alternatives --install /usr/bin/ebtables ebtables /usr/bin/ebtables-nft 10 \
--slave /usr/bin/ebtables-restore ebtables-restore /usr/bin/ebtables-nft-restore \
--slave /usr/bin/ebtables-save ebtables-save /usr/bin/ebtables-nft-save \
--slave /usr/share/man/man8/ebtables.8.gz ebtables.8.gz /usr/share/man/man8/ebtables-nft.8.gz
# For legacy backend, swap -nft suffixes for -legacy
Verify:
which ebtables # should resolve to /usr/bin/ebtables
ebtables -V # should print the version without error
test -e /usr/sbin/ebtables && echo BLOCKER || echo clean
Next dnf upgrade will consolidate /usr/sbin cleanly with no warning.
Ansible Playbook
MajorAnsible/fix_ebtables_usrmerge.yml handles this fleet-wide:
- Detects the backend (nft vs legacy) per host via
update-alternatives --display - Uses
check_mode: falseon the detection query — otherwiseansible.builtin.commandis skipped in--check, the detection fact defaults, and downstream conditionals misfire (see Ansible Check Mode False Positives for the broader pattern) - Safety check: bails out if
/usr/bin/ebtables-<backend>is missing before touching anything - Idempotent on re-run — no alternative registered →
end_host
Applied 2026-04-19 across the four Fedora hosts:
| Host | Backend |
|---|---|
| majorlab | nft (ebtables v1.8.11 nf_tables) |
| majorhome | nft |
| majormail | legacy (ebtables v2.0.11 (legacy)) |
| majordiscord | legacy |
Why not just remove ebtables?
Tempting, since nothing on the fleet currently writes L2 bridge firewall rules. But:
ebtablesis a transitive dependency of iptables/libvirt/networking packages on Fedora — removing it fights the package manager- The package itself isn't the problem; the stale alternatives state is
Cleaning up the registration is cheaper than untangling the dependency graph.
Related
- Ansible Check Mode False Positives in Verify/Assert Tasks
- Playbook:
MajorAnsible/fix_ebtables_usrmerge.yml - Fedora usrmerge background:
man file-hierarchy, Fedora Change page "UsrMove"