systemctl Service Not Starting: Complete Fix Guide

Fix systemctl service not starting — diagnose failed units, read exit codes, resolve dependency errors, and fix the most common reasons a Linux service won't start.

April 22, 2026·4 min read·Damon

Service won't start and systemctl start myapp returns no useful output. The error is always in the logs — you just need to know where to look.


TL;DR

systemctl status myapp           # exit code and last log lines
journalctl -u myapp -n 50 --no-pager  # full logs
journalctl -u myapp -p err       # errors only
systemctl cat myapp              # view unit file
systemd-analyze verify /etc/systemd/system/myapp.service  # syntax check

Step 1: Read the Status Output

systemctl status myapp

The Active: line tells you everything:

Active: failed (Result: exit-code)   → process exited non-zero
Active: failed (Result: signal)      → killed by signal (OOM, SIGKILL)
Active: failed (Result: start-limit-hit) → crashed too many times
Active: inactive (dead)              → stopped, not failed
Active: activating (start)           → stuck starting

The code= and status= at the bottom:

Status Meaning
status=1/FAILURE App returned exit code 1 — check app logs
status=2/INVALIDARGUMENT Wrong argument to systemd directive
status=203/EXEC Binary not found or not executable
status=217/USER Service user doesn't exist
status=226/NAMESPACE Namespace/permission setup failed
code=killed, signal=KILL OOM killer or manual kill -9

Step 2: Read Full Logs

systemctl status truncates. Get everything:

journalctl -u myapp -n 100 --no-pager

# Since last restart attempt
journalctl -u myapp --since "5 minutes ago" --no-pager

# Errors only
journalctl -u myapp -p err --since "today"

Look for the line just BEFORE "Failed to start" or "Main process exited" — that's the actual error.


Fix by Exit Code

status=203/EXEC — binary not found

# Check path in unit file
systemctl cat myapp | grep ExecStart

# Does the binary exist?
ls -la /opt/app/start.sh

# Is it executable?
chmod +x /opt/app/start.sh

# Is it a script with Windows line endings?
file /opt/app/start.sh
# "CRLF line terminators" = fix with:
sed -i 's/\r//' /opt/app/start.sh

status=217/USER — service user missing

# Which user does the unit expect?
systemctl cat myapp | grep User

# Create missing user
useradd -r -s /bin/false appuser

start-limit-hit — too many crashes

# Reset the counter
systemctl reset-failed myapp
systemctl start myapp

# Prevent future start-limit issues
# Add to [Service] section:
# StartLimitBurst=5
# StartLimitIntervalSec=60s
# RestartSec=5s

exit-code — app crashes immediately

Test the command manually as the service user:

# Get the ExecStart command
systemctl cat myapp | grep ExecStart
# ExecStart=/opt/app/start.sh --config /etc/app/prod.conf

# Run it as the service user
runuser -u appuser -- /opt/app/start.sh --config /etc/app/prod.conf
# This shows the real error without systemd buffering it

Common Fixes

Missing environment variable

journalctl -u myapp | grep -iE "env|variable|not set|required"
# If found, add to unit file:
# [Service]
# Environment="DATABASE_URL=postgres://localhost/mydb"
# EnvironmentFile=/etc/myapp/env

Port already in use

journalctl -u myapp | grep -i "address already in use\|bind\|EADDRINUSE"
ss -tlnp | grep :<port>
# Something else is on the port — kill it or change the port

Permission denied on file or directory

journalctl -u myapp | grep -i "permission denied\|EACCES"
# Fix ownership
chown -R appuser:appuser /var/log/myapp /opt/app
chmod 755 /opt/app

Dependency not ready

# Check what the service needs
systemctl list-dependencies myapp

# Is the dependency running?
systemctl status postgresql    # if myapp needs postgres

Unit file error after edit

# ALWAYS run this after editing a unit file
systemctl daemon-reload
systemctl start myapp

Full Diagnostic Script

#!/bin/bash
SERVICE=$1
echo "=== Status ==="
systemctl status $SERVICE --no-pager

echo -e "\n=== Last 30 log lines ==="
journalctl -u $SERVICE -n 30 --no-pager

echo -e "\n=== Errors only ==="
journalctl -u $SERVICE -p err --since "1 hour ago" --no-pager

echo -e "\n=== Unit file ==="
systemctl cat $SERVICE

Common Mistakes

Not running daemon-reload after editing unit files. Changes are ignored until you reload.

Testing as root when service runs as another user. Root can read files the service user can't. Always test with runuser -u <serviceuser>.

Killing the process PID when start-limit is hit. Systemd already marked it failed. Use systemctl reset-failed first.


Conclusion

The fix is always in the logs. journalctl -u myapp -n 100 --no-pager tells you what happened. The exit code in systemctl status narrows it down. Test the binary manually as the service user before debugging systemd configuration.


Related: systemctl Restart Service Not Working — when the service exists but restart fails. journalctl Filter by Time Range — extract logs from the exact window when startup failed.