How to Troubleshoot a Failed systemd Service on Linux
A service that won’t start is one of the most common things you’ll hit running Linux day to day, and it’s also one of the easiest to solve quickly once you know the right order of commands to run. This walks through a reliable sequence to follow, from “why won’t this start” to “here’s the actual root cause.”
Step 1: Check the current status
Start with the basics: this alone tells you the last few log lines and the exact failure state:
systemctl status nginx.service
Look at three things in the output: the Active line (failed, or activating and stuck), the Main PID (was one ever assigned?), and the trailing log lines shown at the bottom. Often the actual error is right there and you don’t need to go further.
Step 2: Pull the full logs for that service
systemctl status only shows a handful of lines. When that’s not enough, go straight to the service’s full journal:
journalctl -u nginx.service --since "10 minutes ago"
journalctl -u nginx.service -b # since the last boot
journalctl -u nginx.service -f # follow live while you try starting it again
The -b flag is worth remembering specifically: if a service has been flapping (starting, failing, restarting) across multiple boots, filtering to the current boot cuts out a lot of noise.
Step 3: Try starting it manually and watch it fail
sudo systemctl start nginx.service
sudo systemctl status nginx.service
If it fails instantly, the problem is almost always one of: a bad config file, a port already in use, or a permissions/ownership issue on a file the service needs. If it starts and then dies a few seconds later, look for the service crashing on an unhandled condition (missing dependency, out of memory, a resource it expects isn’t there yet).
Step 4: Validate the config file directly, don’t guess
Most services that fail due to config problems ship their own syntax-check flag. Use it instead of trying to spot the typo by eye:
sudo nginx -t # nginx
sudo apachectl configtest # Apache
sudo sshd -t # OpenSSH
sudo php-fpm8.3 -t # PHP-FPM
This one step catches the majority of “service won’t start after I edited a config file” cases immediately, with a line number.
Step 5: Check for a port conflict
sudo ss -tulpn | grep :443
If another process already owns the port your service wants, systemctl start will fail (or the service will start and then immediately exit). This is easy to miss if you’re only reading the service’s own logs and not thinking about what else might be listening.
Step 6: Check permissions and SELinux/AppArmor
A service running as a dedicated non-root user (very common, think www-data, nginx, mysql) failing to read a file it needs is one of the most common “invisible” causes:
ls -la /path/to/the/file/it/needs
sudo -u www-data cat /path/to/the/file # does the service's own user have access?
If you’re on a distribution with SELinux enforcing (RHEL/CentOS/Alma/Rocky) or AppArmor (Ubuntu/Debian), also check whether it’s silently blocking access even though the Unix permissions look fine:
sudo ausearch -m avc -ts recent # SELinux denials
sudo aa-status # AppArmor profile status
Step 7: Confirm dependencies actually started first
If a service depends on something else (a database, a socket, a network interface being up) and that dependency wasn’t ready in time, you’ll see the service fail even though its own config is perfectly fine:
systemctl list-dependencies nginx.service
Putting it together: a real example
A concrete case that covers most of the steps above: nginx fails to start after a config change, systemctl status shows “failed” with no useful detail, and journalctl -u nginx -b shows bind() to 0.0.0.0:443 failed (98: Address already in use). Running sudo ss -tulpn | grep :443 shows a leftover apache2 process still bound to the port from before it was disabled. Stopping that process (or properly disabling it with systemctl disable --now apache2) resolves it immediately, since the nginx config itself was never the problem.
Frequently asked questions
Why does “systemctl status” say active (running) but the service still doesn’t actually work?
Systemd only reports that the process is alive, not that it’s functioning correctly. A web server can be “running” while still returning 502s because its upstream (PHP-FPM, a database) is down, so check the application’s own logs, not just systemd’s view of the process.
What’s the difference between “systemctl restart” and “systemctl reload”?
Restart fully stops and starts the process (brief downtime, all connections dropped). Reload asks the service to re-read its config without dropping its process or existing connections, if the service supports it. Prefer reload for config-only changes when it’s available (nginx and Apache both support it).
