HardStacked
Share:

systemd · Raspberry Pi

"Failed to enable unit: Cannot alias" but the service is enabled anyway (systemd, Raspberry Pi)

Published · HardStacked · 8 min

You typed systemctl enable myservice.service. It answered Failed to enable unit: Cannot alias myservice.service as myservice, exit code 1. You went to check. systemctl is-enabled myservice.service said enabled. You rebooted, and the service was running. Nothing in that sequence agrees with anything else in it.

A question on Unix & Linux Stack Exchange, still without an accepted answer, said it better than any bug tracker ever will:

"Oddly enough, when I check the status, it says it is enabled, and when I reboot, the service is live. I don't understand what is going on."

(Unix & Linux Stack Exchange, question 788862, Raspberry Pi Zero W, 2 answers, none accepted.) One answer tells them to remove the Alias= line, which is the right fix; the other points to a duplicate .service file, a different cause. Neither one tells them why enable said it failed while doing exactly what they asked.

01. The two-line verdict

systemctl enable did the work, then told you it had failed. On the way out it skipped printing the list of what it had just done, and it skipped the reload that would have let the running systemd notice. Three separate signals, three separate lies, and the actual change only shows up at the next reboot or the next daemon-reload.

Everything below was measured on 2026-09-24, on Debian 13 (trixie) running systemd 257 (257.13-1~deb13u1), on a Raspberry Pi 5. The commands ran in the user instance (systemctl --user), so anyone can reproduce them without root. The system instance goes through the exact same code (src/shared/install.c), but we didn't run this specific test as root: take the mechanism as confirmed, the system-instance numbers as not directly measured by us.

02. The three lies, one unit

The unit, a throwaway demo.service, Type=oneshot, ExecStart=/bin/true, with [Install] set to Alias=demo and WantedBy=default.target. Alias=demo has no suffix, which is invalid: man systemd.unit requires an alias to carry the same type suffix as the unit it aliases, here .service.

These commands are provided as is, without warranty. Test on a non critical machine or a snapshot first.

Alias=demo, user instance
$ systemctl --user enable demo.service; echo "exit=$?" Failed to enable unit: Cannot alias demo.service as demo exit=1 $ systemctl --user is-enabled demo.service enabled $ systemctl --user list-dependencies default.target | grep demo $ ls -l ~/.config/systemd/user/default.target.wants/ | grep demo lrwxrwxrwx 1 user user 46 24 sep 15:55 demo.service -> ~/.config/systemd/user/demo.service

Read that in order. The command reports failure. is-enabled reports success. list-dependencies (the running systemd's own view) shows nothing. ls -l on the .wants/ directory shows the symlink sitting right there on disk. Four commands, three different answers to the same question.

A witness unit without Alias= prints Created symlink '~/.config/systemd/user/default.target.wants/demo.service' → '~/.config/systemd/user/demo.service'. and exits 0. Our unit never prints that line. Nothing about the symlink appears anywhere in the output, even though it's on disk.

The consequence isn't cosmetic. A script running under set -e that calls systemctl enable demo.service stops right there, exit 1, on a step that in fact succeeded:

script under set -e
Failed to enable unit: Cannot alias demo.service as demo script exit=1

Whatever the script meant to do after enable (start the unit, enable a second one, log success) never runs, because the shell already believes the first step failed.

03. Why, verified in the systemd source (v257)

Three things happen in an order that produces exactly this behavior, and none of them is a crash: it is how the code is structured.

install_info_apply(), in src/shared/install.c, creates the alias symlink first. If that fails, the function does not stop: it keeps going through WantedBy=, RequiredBy=, UpheldBy=, creating every one of those links regardless. It keeps the first error it saw and returns that at the end. The alias fails, the activation links get created anyway, and the caller is told "failed" last.

systemctl, in the normal case where systemd is running, asks for that work over D-Bus. When the reply comes back as an error, systemctl prints the error and returns immediately, it never opens the part of the reply that lists what was actually created. That's the whole reason Created symlink never shows up on a failing enable: the message existed, systemctl just didn't read it.

The automatic daemon-reload that normally follows a successful enable only runs after success. On an error, systemctl returns before reaching it. The running systemd instance is never told a new .wants/ entry exists, so list-dependencies keeps reporting nothing, until something else triggers a reload, a manual daemon-reload, or a reboot. That's exactly the story from the question above: is-enabled reading the disk says enabled, list-dependencies reading the running systemd says nothing, and the reboot performs the reload that enable skipped.

A witness unit without Alias= gets picked up by the running systemd right away, no gap. The unit that hits the alias bug does not:

before and after daemon-reload
$ systemctl --user enable demo.service; echo "exit=$?" Failed to enable unit: Cannot alias demo.service as demo exit=1 $ ls ~/.config/systemd/user/default.target.wants/ | grep demo demo.service $ systemctl --user list-dependencies default.target | grep -c demo 0 $ systemctl --user daemon-reload $ systemctl --user list-dependencies default.target | grep -c demo 1

enable reports failure. The symlink is already sitting in .wants/ (the ls line). Before any reload, list-dependencies counts it 0 times, the running systemd doesn't know it exists. One systemctl --user daemon-reload, no other change, and the same list-dependencies count goes to 1. Nothing about the unit changed between those two counts, only whether the running systemd had been told to look again.

One more measurement supports the same mechanism from a different angle. Run enable through --root=, a path that never talks to a running systemd (used for building images or applying a unit to an offline root), and the error and the symlink creation line show up in the same output:

--root=, no running systemd
$ systemctl --root=/rootfs enable demo.service; echo "exit=$?" Failed to enable unit: Cannot alias demo.service as demo Created symlink '/rootfs/etc/systemd/system/multi-user.target.wants/demo.service' → '/etc/systemd/system/demo.service'. Unit /rootfs/etc/systemd/system/demo.service is added as a dependency to a non-existent unit multi-user.target. exit=1 $ find /rootfs -type l /rootfs/etc/systemd/system/multi-user.target.wants/demo.service

Same failure, same symlink, but this time you see both. The missing line on the normal path isn't the symlink not being created, it's systemctl not reading the reply that would have told you about it. This confirms the mechanism specifically for --root=; we didn't measure a plain chroot without that flag, and won't claim the same for it without a test.

04. Two neighboring traps

Activating by the alias name instead of the real unit name fails on its own terms:

enable by the alias name
$ systemctl --user enable demo-alias.service; echo "exit=$?" Failed to enable unit: Refusing to operate on linked unit file demo-alias.service exit=1

demo-alias.service is itself a symlink, not a real unit file. systemctl refuses to enable a linked unit file directly; you enable the unit it points to. A valid alias (one with a matching suffix, Alias=demo-alias.service) works cleanly: two Created symlink lines, exit 0, no ambiguity.

The other neighbor is two units competing for the same alias, the shape of two graphical login managers both claiming display-manager.service. Reproduced with two throwaway user units sharing an Alias=, not with a real display manager: the second one to enable gets Failed to enable unit: File '.../skyshared.service' already exists and is a symlink to .../skyA.service, exit 1, and is-enabled on the second unit still answers enabled. The alias itself never moves, it keeps pointing at the first unit. is-enabled answering enabled here doesn't mean the second unit is the one that will actually start through that alias. Treat this as the same failure-reporting pattern showing up in a different shape, not as a statement about what any specific display manager package does; that would need testing the real packages, which we didn't do.

One more cause is reported for the same error message but wasn't reproduced here: two .service files with the same name, one under /etc, one under /usr/lib. Worth checking with find if the mechanism above doesn't match what you're seeing, but not something we measured.

05. The fix, and the habit that generalizes

Remove Alias=demo, or give it a real name with the correct suffix. Alias=demo-alias.service, measured, works cleanly: two Created symlink lines, exit 0. Then run systemctl daemon-reload followed by systemctl reenable demo.service to put the unit back in a clean, known state. reenable isn't something we captured a test bench output for; treat it as the documented way to reset, not as a claim backed by our own output.

The reusable lesson sits under all of this: is-enabled answers by reading the unit files and symlinks on disk. list-dependencies answers by reading what the running systemd instance actually knows. They are two different questions, and enable failing is exactly the case where they can disagree. When enable reports an error, check both before trusting either, and run ls -l on the relevant .wants/ directory to see what actually landed on disk. If you're on an older systemd (this was measured on 257; the person who asked the Unix & Linux question was on Raspbian, with a systemd version they didn't specify) and you don't see a symlink where this article says there should be one, that's a real difference worth checking with ls -l rather than assuming the mechanism doesn't apply to your version.

The same shape runs through the rest of this series, where the system declares one thing and does another: a systemd service that runs as root and still gets Permission denied, a firewall the systemd unit reports as enabled while it is switched off, and a WireGuard service that reports active with no interface at all.

Share: