Skip to content
DevOps AI ToolKit
Newsletter
All guides
AI for Automation By James Joyner IV · · 8 min read

systemd Error: 'Failed with result exit-code' — Cause, Fix, and Troubleshooting Guide

Quick answer

Fix myjob.service: Failed with result 'exit-code' from a systemd timer — non-zero ExecStart, wrong WorkingDirectory/User, and systemd's minimal environment.

  • #automation
  • #troubleshooting
  • #systemd
Free toolkit

Fixing errors like this? Get 500 free DevOps AI prompts

500 copy-paste AI prompts for the stack you actually run — one PDF, free.

Overview

myjob.service: Failed with result 'exit-code'. means a systemd unit ran its ExecStart process, that process exited non-zero, and systemd marked the unit failed. When the unit is triggered by a .timer (systemd’s cron replacement), this is the timer-driven equivalent of a cron job failing — the schedule fired, the service ran, and the command returned an error.

The exit-code result is systemd’s classification: the process started and exited on its own with a non-zero status, as opposed to being killed by a signal (signal), timing out (timeout), or failing to start at all (exec / protocol). Like cron, systemd runs services in a minimal environment with an empty PATH-less context, a specific User, and a WorkingDirectory — so a command that works in your shell can fail here.

The full failure in the journal reads:

Jul 12 04:00:12 host systemd[1]: Starting Nightly backup job...
Jul 12 04:00:12 host myjob[3187]: /usr/local/bin/backup.sh: line 6: aws: command not found
Jul 12 04:00:12 host systemd[1]: myjob.service: Main process exited, code=exited, status=1/FAILURE
Jul 12 04:00:12 host systemd[1]: myjob.service: Failed with result 'exit-code'.
Jul 12 04:00:12 host systemd[1]: Failed to start Nightly backup job.

The status=1/FAILURE line names the exit code (1); the Failed with result 'exit-code' line is systemd’s summary; the backup[3187] line above is the actual error.

Symptoms

  • systemctl status myjob.service shows Active: failed (Result: exit-code).
  • Journal contains Main process exited, code=exited, status=N/FAILURE then Failed with result 'exit-code'.
  • systemctl list-timers shows the timer firing but the service never completing successfully.
  • The script runs fine when you execute it by hand but fails under the timer.
  • A Type=oneshot service is marked failed even though “the work looked done”.

Common Root Causes

1. The ExecStart script exits non-zero

The most direct cause — the command genuinely failed. A set -euo pipefail script tripping on an unset variable, a failed curl, a non-zero pg_dump.

journalctl -u myjob.service -n 20 --no-pager | grep -iE 'error|fail|not found'
/usr/local/bin/backup.sh: line 6: aws: command not found

2. Minimal environment — missing PATH and variables

systemd services start with a restricted environment. /usr/local/bin, version-manager shims, and any variable you set in ~/.bashrc are absent. Exit 127 (command not found) is the classic tell.

systemctl show myjob.service -p Environment -p ExecStart
Environment=
ExecStart={ path=/usr/local/bin/backup.sh ; argv[]=/usr/local/bin/backup.sh ; ... }

An empty Environment= and a script that relies on PATH additions will fail.

3. Wrong WorkingDirectory, User, or ExecStart path

The service runs as the configured User (default root) from WorkingDirectory (default /). Relative paths break, and a script owned/readable only by another user hits permission errors.

status=203/EXEC       # ExecStart path wrong or not executable
status=200/CHDIR      # WorkingDirectory does not exist

4. Missing EnvironmentFile

The unit expects secrets/config from an EnvironmentFile that is absent or unreadable, leaving required variables unset.

systemctl show myjob.service -p EnvironmentFiles

5. Type=oneshot semantics

For Type=oneshot, systemd considers the service successful only if the process exits 0. Any non-zero exit — including a script that “finished” but returned the status of its last command — is a failure. A trailing command that returns 1 fails the whole unit.

How to Diagnose

Step 1 — Read the unit status; it shows the result and the last log lines.

systemctl status myjob.service --no-pager
● myjob.service - Nightly backup job
     Loaded: loaded (/etc/systemd/system/myjob.service; static)
     Active: failed (Result: exit-code) since Sun 2026-07-12 04:00:12 UTC
    Process: 3187 ExecStart=/usr/local/bin/backup.sh (code=exited, status=1/FAILURE)
   Main PID: 3187 (code=exited, status=1/FAILURE)

The Process: line gives the exact status=N and the ExecStart that failed.

Step 2 — Read the full journal for this unit and this run.

journalctl -u myjob.service --since '-1h' --no-pager
Jul 12 04:00:12 host myjob[3187]: /usr/local/bin/backup.sh: line 6: aws: command not found
Jul 12 04:00:12 host systemd[1]: myjob.service: Main process exited, code=exited, status=1/FAILURE

The line above systemd’s summary is the real error.

Step 3 — Confirm the timer is firing and see when it last/next runs.

systemctl list-timers myjob.timer --all --no-pager
NEXT                        LEFT     LAST                        PASSED   UNIT         ACTIVATES
Mon 2026-07-13 04:00:00 UTC 20h left Sun 2026-07-12 04:00:12 UTC 30m ago  myjob.timer  myjob.service

If LAST shows the timer fired but the service failed, the schedule is fine — the job is the problem.

Step 4 — Run the service on demand to reproduce without waiting for the timer.

sudo systemctl start myjob.service; systemctl is-failed myjob.service
journalctl -u myjob.service -n 5 --no-pager

Step 5 — Reproduce systemd’s minimal environment to catch PATH/env issues:

sudo systemd-run --pty --same-dir --wait \
  --property=User=myuser /usr/local/bin/backup.sh

If it fails here but works in your shell, the cause is environment (PATH, missing vars), not logic.

Fixes

Fix the failing command. If the journal named the error, correct it. For command not found, set an explicit PATH or use absolute paths in the unit:

[Service]
Environment=PATH=/usr/local/bin:/usr/bin:/bin
ExecStart=/usr/local/bin/backup.sh

Set the correct User, WorkingDirectory, and absolute ExecStart.

[Service]
Type=oneshot
User=myuser
WorkingDirectory=/opt/jobs
ExecStart=/opt/jobs/backup.sh

Provide environment via EnvironmentFile so secrets and config are present:

[Service]
EnvironmentFile=/opt/jobs/backup.env
ExecStart=/opt/jobs/backup.sh
sudo install -m 600 -o myuser /dev/stdin /opt/jobs/backup.env <<'EOF'
AWS_PROFILE=backup
DATABASE_URL=postgres://user@10.0.0.5/app
EOF

Apply changes and re-test — systemd caches unit files:

sudo systemctl daemon-reload
sudo systemctl start myjob.service
systemctl status myjob.service --no-pager | head -5
     Active: inactive (dead)   # oneshot succeeded and exited 0

Make the script return a clean status. For Type=oneshot, ensure the script ends with a command that exits 0 on success, and use set -euo pipefail so real failures surface rather than being masked by a trailing echo.

Add controlled restart/notification so a transient failure recovers or alerts:

[Service]
Restart=on-failure
RestartSec=30

What to Watch Out For

  • The status=N/NAME code narrows the cause fast: 1/FAILURE = script error, 127 = command not found (PATH), 203/EXEC = bad ExecStart path or not executable, 200/CHDIR = missing WorkingDirectory, 226/NAMESPACE = sandbox/permission setting.
  • systemd’s environment is minimal like cron’s — do not rely on login-shell PATH or ~/.bashrc variables. Declare everything in the unit or an EnvironmentFile.
  • Result: exit-code means the process exited on its own; signal, timeout, and oom-kill are different results with different fixes — read the exact word.
  • Always systemctl daemon-reload after editing a unit file, or systemd runs the stale version and your fix appears to do nothing.
  • For Type=oneshot, the unit fails on any non-zero exit; a script whose last line returns 1 fails the whole job even if the real work succeeded.
  • The timer and the service are separate units. If list-timers shows the timer firing but the service fails, debug the .service, not the .timer.
Free download · 368-page PDF

Get 500 Battle-Tested DevOps AI Prompts — Free

500 battle-tested, copy-paste AI prompts engineered by a senior systems engineer — every one with fill-in placeholders and safety/back-out notes. Drop your email and it's yours.

  • 500 prompts: Linux · Kubernetes · Terraform · OpenStack · GitLab · Docker · Monitoring · Incident Response
  • Instant PDF download — yours free, forever
  • Plus one practical AI-workflow email a week (no spam)

Single opt-in · unsubscribe anytime · no spam.