majorwiki/05-troubleshooting/fedora-usrmerge-ebtables-blocker.md
Marcus Summers 181c04bed8 wiki: add Fedora usrmerge ebtables blocker troubleshooting article
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.
2026-04-19 04:55:54 -04:00

5.2 KiB

title domain category tags status created updated
Fedora usrmerge: ebtables Symlink Blocks Directory Consolidation troubleshooting fedora
fedora
usrmerge
ebtables
update-alternatives
ansible
dnf
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/sbin was still a real directory
  • Point to a target (/etc/alternatives/ebtables) that in turn points back into /usr/sbin/ebtables-legacy or /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

  1. Confirm which hosts are affected:
    ansible fedora -m shell -a '[ -e /usr/sbin/ebtables ] && ls -la /usr/sbin/ebtables'
    
  2. Inspect the alternatives registration:
    update-alternatives --display ebtables
    
    Note whether the link points at /usr/bin/ebtables-nft (nft backend) or /usr/sbin/ebtables-legacy (legacy backend). Different Fedora images ship with different defaults.
  3. Confirm ownership:
    rpm -qf /usr/sbin/ebtables /etc/alternatives/ebtables
    
    Both should report "not owned by any package." That's the signal.

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: false on the detection query — otherwise ansible.builtin.command is 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:

  • ebtables is 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.