Introduction

Systemd exit code 203 (EXEC) occurs when the executable defined in the ExecStart directive of a unit file cannot be located or executed. This is one of the most common startup failures on Linux systems and typically points to a missing binary, incorrect path, wrong interpreter, or file permission problem.

Symptoms

  • systemctl status shows Main process exited, code=exited, status=203/EXEC
  • Journal logs contain Failed at step EXEC spawning /path/to/binary: No such file or directory
  • Service enters failed state immediately after start attempt
  • systemd[1]: myapp.service: Main process exited, code=exited, status=203/EXEC

Common Causes

  • The binary path in ExecStart does not exist or contains a typo
  • The script interpreter specified in the shebang line is missing
  • The executable lacks the execute permission bit
  • The binary is a 32-bit ELF on a 64-bit-only system without compat libraries
  • SELinux or AppArmor is blocking execution of the binary

Step-by-Step Fix

  1. 1.Identify the failing executable path:
  2. 2.```bash
  3. 3.journalctl -u myapp.service -n 20 --no-pager
  4. 4.`
  5. 5.Look for the line containing Failed at step EXEC spawning.
  6. 6.Verify the binary exists at the specified path:
  7. 7.```bash
  8. 8.ls -la /usr/local/bin/myapp
  9. 9.which myapp 2>/dev/null || echo "Binary not in PATH"
  10. 10.file /usr/local/bin/myapp
  11. 11.`
  12. 12.Check file permissions and set execute bit if needed:
  13. 13.```bash
  14. 14.chmod 755 /usr/local/bin/myapp
  15. 15.# For scripts, also verify interpreter exists
  16. 16.head -1 /usr/local/bin/myapp
  17. 17.# If shebang is #!/usr/bin/python3, verify:
  18. 18.ls -la /usr/bin/python3
  19. 19.`
  20. 20.Verify systemd unit file ExecStart path:
  21. 21.```bash
  22. 22.systemctl cat myapp.service | grep ExecStart
  23. 23.# Fix the unit file if path is wrong
  24. 24.sudo nano /etc/systemd/system/myapp.service
  25. 25.sudo systemctl daemon-reload
  26. 26.`
  27. 27.Check SELinux/AppArmor denials:
  28. 28.```bash
  29. 29.# SELinux
  30. 30.ausearch -m avc -ts recent | grep myapp
  31. 31.# AppArmor
  32. 32.dmesg | grep -i apparmor | grep myapp
  33. 33.`
  34. 34.Restart the service and verify:
  35. 35.```bash
  36. 36.sudo systemctl restart myapp.service
  37. 37.systemctl status myapp.service
  38. 38.`

Prevention

  • Use absolute paths in all ExecStart directives, never rely on $PATH
  • Add ConditionPathExists=/usr/local/bin/myapp to fail gracefully with a clear message
  • Include a Type=notify or Type=simple with proper ExecStartPre= checks to validate the binary before starting
  • Use configuration management (Ansible, Puppet) to ensure binary paths stay in sync with unit files