Manage Binary Format Handlers with systemd-binfmt
A .py file that should just run instead throws exec format error, and systemd-binfmt is the reason it usually just works. This guide inspects the binary-format handlers systemd has registered, works out which binfmt.d file wins when two disagree, and adds or removes a handler without losing track of kernel state. Examples match systemd 255.4 on Ubuntu, package version 255.4-1ubuntu8.17. Allow about fifteen minutes.
The route
Jump straight to the step you need, or tick off Done means at the end.
- Reading configuration is unprivileged.
- Writing under
/etc/binfmt.d, restarting the service, and unregistering handlers need elevated privileges. The last one is destructive and gets its own section below.
1. Confirm the installed tool
The service uses the helper at /usr/lib/systemd/systemd-binfmt. Check the version and available options before copying a command into a script:
$ /usr/lib/systemd/systemd-binfmt --version
systemd 255 (255.4-1ubuntu8.17)
$ /usr/lib/systemd/systemd-binfmt --help
systemd-binfmt [OPTIONS...] [CONFIGURATION FILE...]
...
--cat-config Show configuration files
--tldr Show non-comment parts of configuration
--no-pager Do not pipe output into a pager
--unregister Unregister all existing entries
The exact feature list varies with the systemd release. In this release, --unregister exists and has been available since systemd 246.
Warning
Do not use --unregister as a routine reload command. It removes every currently registered entry, not just yours.
2. See the effective configuration
Start with the read-only view. --cat-config prints each file and its filename; --tldr omits blank lines and comments:
$ /usr/lib/systemd/systemd-binfmt --no-pager --tldr
# /usr/lib/binfmt.d/llvm-18-runtime.binfmt.conf
:llvm-18-runtime.binfmt:M::BC::/usr/bin/lli-18:
# /usr/lib/binfmt.d/llvm-20-runtime.binfmt.conf
:llvm-20-runtime.binfmt:M::BC::/usr/bin/lli-20:
# /usr/lib/binfmt.d/python3.12.conf
:python3.12:M::\xcb\x0d\x0d\x0a::/usr/bin/python3.12:
Your output depends on installed packages. These entries are kernel binfmt_misc rules, not shell aliases: a magic rule matches bytes at an offset in a file and invokes the named interpreter. The kernel documentation describes the full registration shape as :name:type:offset:magic:mask:interpreter:flags; the separator can change when a rule needs a literal colon.
Checkpoint
Compare this output with the live kernel directory:
$ ls -l /proc/sys/fs/binfmt_misc
$ cat /proc/sys/fs/binfmt_misc/status
Registered names such as python3.12 should have corresponding files. An empty or missing directory usually means the kernel interface is not mounted or available. Do not write to register by hand while diagnosing a systemd-managed setup.
3. Understand which file wins
Configuration files must end in .conf. systemd reads administrator files from /etc/binfmt.d, runtime files from /run/binfmt.d, and package or local files from /usr/lib/binfmt.d or /usr/local/lib/binfmt.d. Higher-priority directories override a same-named file, then files are sorted by filename across directories, so a later lexicographic name can change one setting.
- Replacing a vendor file entirely: a vendor file named
50-example.confcan be replaced completely by an/etc/binfmt.d/50-example.conffile. - Adding a rule without replacing the vendor one: use a later name, such as
90-local-example.conf. The recommended two-digit prefix makes the ordering visible during review.
Blank lines and lines beginning with # or ; are comments, so those characters cannot be used as a rule's field separator. Keep comments outside the registration line.
4. Add a local rule deliberately
Use a real interpreter path and a rule designed for the file format you intend to run. This is a template, not a safe claim that the placeholder interpreter exists:
# /etc/binfmt.d/90-example.conf
:example:M::MAGIC::/absolute/path/to/trusted-interpreter:
M means magic-byte matching, example is the name, MAGIC is the byte sequence, and the final path is the interpreter.
Security warning
The kernel can execute this handler whenever a file matches, so a wrong or untrusted interpreter is a security decision, not a harmless test.
Create the file with your normal administrative editor, then inspect it before applying it:
$ sudoedit /etc/binfmt.d/90-example.conf
$ sudo /usr/lib/systemd/systemd-binfmt --no-pager --cat-config | sed -n '/90-example.conf/,+2p'
Do not use a broad shell redirection with an unquoted path or user-supplied rule. Keep the file mode ordinary for configuration, and confirm the interpreter is an absolute, executable file owned and maintained by the system administrator.
5. Apply and verify the change
After editing a persistent file, restart the service during a suitable maintenance window. This changes kernel execution behaviour for matching files:
$ sudo systemctl restart systemd-binfmt.service
$ systemctl is-active systemd-binfmt.service
active
$ ls -l /proc/sys/fs/binfmt_misc/example
The service is normally a static unit, so is-enabled may print static even when is-active prints active. That is not a failure. If the restart fails, read the unit log, fix or remove the new file, then restart again:
$ journalctl -u systemd-binfmt.service -b --no-pager
$ sudoedit /etc/binfmt.d/90-example.conf
$ sudo systemctl restart systemd-binfmt.service
Keep the old configuration until a representative file has run successfully. A matching rule can affect ordinary command launches, and a handler that exits unexpectedly can make the original program look broken.
6. Override or remove a vendor rule
To disable a packaged configuration file, place a symlink with the same filename in /etc/binfmt.d pointing to /dev/null. Resolve the exact vendor filename first:
$ find /usr/lib/binfmt.d /usr/local/lib/binfmt.d -maxdepth 1 -type f -name '*.conf' -printf '%f\n' | sort
$ sudo ln -s /dev/null /etc/binfmt.d/EXACT-VENDOR-FILENAME.conf
$ sudo systemctl restart systemd-binfmt.service
Replace EXACT-VENDOR-FILENAME.conf with the filename you actually found; do not create a guessed link. If the configuration was baked into an initrd, the manpage requires regenerating that initrd as a separate, distribution-specific operation.
To undo the override, remove only the symlink you created, then restart the service:
$ test -L /etc/binfmt.d/EXACT-VENDOR-FILENAME.conf
$ sudo rm /etc/binfmt.d/EXACT-VENDOR-FILENAME.conf
$ sudo systemctl restart systemd-binfmt.service
Warning
rm is irreversible for that directory entry. The vendor file itself is not removed, but check the target with test -L first so you do not delete a real administrator file.
7. Treat unregister as an emergency operation
--unregister unregisters all currently registered binary formats, including entries that did not come from the files you are editing. It needs the privilege to change the kernel interface and can disrupt unrelated applications. Use it only with a documented recovery plan:
$ sudo /usr/lib/systemd/systemd-binfmt --unregister
$ sudo systemctl restart systemd-binfmt.service
$ /usr/lib/systemd/systemd-binfmt --no-pager --tldr
The restart re-registers the configured rules, but it cannot restore an entry that was registered by another mechanism or a configuration file you have removed. Prefer correcting binfmt.d and restarting the service when the problem is a bad local rule.
Done means
- You confirmed the installed systemd version and inspected the effective rules with
--tldr. - You checked both configuration precedence and the live
/proc/sys/fs/binfmt_miscentries. - Any new rule uses a reviewed absolute interpreter path and a deliberate filename order.
- You verified the service state after applying a change and kept a recovery edit ready.
- You did not reach for
--unregisterunless clearing every handler was genuinely required.