Bug: openclaw doctor --fix regenerates a user-systemd unit even when a system-systemd unit owns the gateway, causing dual-instance deadlock
Environment
- OpenClaw
2026.4.5 (3e72c03)
- Ubuntu 24.04.4 LTS, systemd 255
- Node v24.14.1 via nvm
- openclaw-gateway running as a system-systemd unit at
/etc/systemd/system/openclaw-gateway.service (User=pleresadmin), so the gateway can use LoadCredentialEncrypted= to inject Microsoft Teams client secrets from /etc/credstore.encrypted/
Background — why a system unit instead of user-systemd
The default OpenClaw onboarding installs a user-systemd unit at ~/.config/systemd/user/openclaw-gateway.service. We migrated to a system-systemd unit (still running as User=pleresadmin) because user-systemd cannot consume LoadCredentialEncrypted= paths in /etc/credstore.encrypted/ — only system-systemd can decrypt host-key-sealed credentials. Both LiteLLM and openclaw-gateway use this pattern in our deployment for secret injection.
After migration:
~/.config/systemd/user/openclaw-gateway.service was disabled and removed
/etc/systemd/system/openclaw-gateway.service was installed, enabled, and started
- The gateway came up cleanly, both signal-cli daemons + msteams sidecar live
Steps to reproduce
- Have the gateway running successfully under a system-systemd unit. Confirm:
systemctl --user is-enabled openclaw-gateway # → not-found
sudo systemctl is-active openclaw-gateway # → active
- Run
openclaw doctor --fix (for any reason — we ran it during a security-audit pass).
- Doctor silently writes back both unit files:
~/.config/systemd/user/openclaw-gateway.service (regenerated even though it had been deleted)
- And enables it via
systemctl --user enable
- Now both systemd unit files exist with the same name. Doctor doesn't mention this anywhere in its output.
Expected behavior
Doctor should:
- Detect existing system-systemd installation at
/etc/systemd/system/openclaw-gateway.service
- Skip user-systemd unit regeneration when the system unit owns the gateway
- OR at minimum, warn loudly that it's about to install a second unit with the same name
Actual behavior
Both units come up. Both bind port 18789. They detect each other as "stale gateway" via OpenClaw's internal service-mode cleanup subsystem (which kills processes that hold port 18789 when a new instance starts). Each new instance of one unit kills the running instance of the other. Result:
- 17-second crash loop, perfectly deterministic
- systemd journal shows
Main process exited, code=killed, status=9/KILL every 17 seconds
- restart counter climbs (
Restart=always is set)
- All four service ports (gateway WS 18789, msteams 3978, signal-cli 8080/8081) come up briefly during each instance's brief life, then disappear when the killer fires
Diagnostic difficulty
The 17-second SIGKILL pattern looks like:
- OOM killer (no — system has 12 GB free)
- systemd timeout (no —
TimeoutStartSec=120)
- Channel sidecar hang (no — happens with both signal AND msteams disabled)
- Namespace setup failure (no — minimal hardening, no
ProtectSystem=strict)
It took several hours of debugging to find the dual-unit cause. The actual smoking gun was:
$ systemctl --user is-enabled openclaw-gateway
enabled
$ systemctl --user is-active openclaw-gateway
active
For a unit file I had explicitly deleted earlier in the session.
Workaround
After running openclaw doctor --fix on a system-systemd installation:
systemctl --user stop openclaw-gateway
systemctl --user disable openclaw-gateway
rm -f ~/.config/systemd/user/openclaw-gateway.service ~/.config/systemd/user/openclaw-gateway.service.bak
systemctl --user daemon-reload
sudo systemctl stop openclaw-gateway
sudo pkill -9 -f openclaw-gateway
sudo pkill -9 -f signal-cli
sudo systemctl reset-failed openclaw-gateway
sudo systemctl start openclaw-gateway
Severity
High, especially in combination with bug #22 (signal multi-account corruption) — both fire on the same --fix invocation. A user running openclaw doctor --fix once for a routine cleanup pass can take their entire deployment down with no obvious cause.
Suggested fix paths
- Doctor's daemon-install pathway should check for an existing
/etc/systemd/system/openclaw-gateway.service BEFORE writing the user-systemd version. If present, skip user-unit installation entirely.
- Optionally, surface a warning if the user has both unit files: "Multiple unit files detected — pick one and remove the other."
- Document the system-systemd installation path as a first-class option for users who need
LoadCredentialEncrypted= (currently it's a manual migration with no doctor support).
Bug:
openclaw doctor --fixregenerates a user-systemd unit even when a system-systemd unit owns the gateway, causing dual-instance deadlockEnvironment
2026.4.5 (3e72c03)/etc/systemd/system/openclaw-gateway.service(User=pleresadmin), so the gateway can useLoadCredentialEncrypted=to inject Microsoft Teams client secrets from/etc/credstore.encrypted/Background — why a system unit instead of user-systemd
The default OpenClaw onboarding installs a user-systemd unit at
~/.config/systemd/user/openclaw-gateway.service. We migrated to a system-systemd unit (still running asUser=pleresadmin) because user-systemd cannot consumeLoadCredentialEncrypted=paths in/etc/credstore.encrypted/— only system-systemd can decrypt host-key-sealed credentials. Both LiteLLM and openclaw-gateway use this pattern in our deployment for secret injection.After migration:
~/.config/systemd/user/openclaw-gateway.servicewas disabled and removed/etc/systemd/system/openclaw-gateway.servicewas installed, enabled, and startedSteps to reproduce
openclaw doctor --fix(for any reason — we ran it during a security-audit pass).~/.config/systemd/user/openclaw-gateway.service(regenerated even though it had been deleted)systemctl --user enableExpected behavior
Doctor should:
/etc/systemd/system/openclaw-gateway.serviceActual behavior
Both units come up. Both bind port 18789. They detect each other as "stale gateway" via OpenClaw's internal
service-modecleanup subsystem (which kills processes that hold port 18789 when a new instance starts). Each new instance of one unit kills the running instance of the other. Result:Main process exited, code=killed, status=9/KILLevery 17 secondsRestart=alwaysis set)Diagnostic difficulty
The 17-second SIGKILL pattern looks like:
TimeoutStartSec=120)ProtectSystem=strict)It took several hours of debugging to find the dual-unit cause. The actual smoking gun was:
For a unit file I had explicitly deleted earlier in the session.
Workaround
After running
openclaw doctor --fixon a system-systemd installation:Severity
High, especially in combination with bug #22 (signal multi-account corruption) — both fire on the same
--fixinvocation. A user runningopenclaw doctor --fixonce for a routine cleanup pass can take their entire deployment down with no obvious cause.Suggested fix paths
/etc/systemd/system/openclaw-gateway.serviceBEFORE writing the user-systemd version. If present, skip user-unit installation entirely.LoadCredentialEncrypted=(currently it's a manual migration with no doctor support).