Troubleshooting Systemd Service Failures: A Step-by-Step Guide
Systemd has become the de-facto system and service manager for most modern Linux distributions, playing a critical role in managing services, daemons, and processes. While powerful and efficient, services managed by systemd can sometimes fail to start, leading to application downtime or system instability. Diagnosing these failures requires a systematic approach, leveraging systemd's robust logging and introspection capabilities.
This guide provides a comprehensive, step-by-step methodology to troubleshoot common systemd service startup failures. We'll cover everything from initial status checks and deep-diving into logs to inspecting unit files and resolving complex dependency issues. By the end of this article, you'll have the practical knowledge and tools to efficiently diagnose and resolve most systemd service failures, ensuring your applications and services run smoothly.
The First Line of Defense: systemctl status
When a service fails to start, the very first command you should run is systemctl status <service_name>. This command provides a snapshot of the service's current state, including whether it's active, loaded, and, crucially, a snippet of its recent logs. This often provides enough information to quickly identify the problem.
Let's say your web application service, mywebapp.service, isn't starting:
systemctl status mywebapp.service
Example Output Interpretation:
● mywebapp.service - My Web Application
Loaded: loaded (/etc/systemd/system/mywebapp.service; enabled; vendor preset: disabled)
Active: failed (Result: exit-code) since Mon 2023-10-26 10:30:05 UTC; 10s ago
Process: 12345 ExecStart=/usr/local/bin/mywebapp-start.sh (code=exited, status=1/FAILURE)
Main PID: 12345 (code=exited, status=1/FAILURE)
CPU: 10ms
Oct 26 10:30:05 hostname systemd[1]: Started My Web Application.
Oct 26 10:30:05 hostname mywebapp-start.sh[12345]: Error: Port 8080 already in use
Oct 26 10:30:05 hostname systemd[1]: mywebapp.service: Main process exited, code=exited, status=1/FAILURE
Oct 26 10:30:05 hostname systemd[1]: mywebapp.service: Failed with result 'exit-code'.
From this output, we can immediately see:
* The service mywebapp.service is failed.
* It failed with Result: exit-code, meaning the ExecStart command exited with a non-zero status.
* The Process line shows the command mywebapp-start.sh failed with status=1/FAILURE.
* Crucially, the log lines indicate: Error: Port 8080 already in use. This is a clear indicator of the problem.
This command is your first diagnostic tool, often pointing directly to the cause or narrowing down where to look next.
Diving Deep with journalctl
While systemctl status provides a quick summary, journalctl is your go-to command for detailed logging. It queries the systemd journal, which collects logs from all parts of the system, including services.
Basic Log Review
To view all logs for a specific service, including historical entries:
journalctl -u mywebapp.service
This will show all log entries associated with mywebapp.service. If the service fails repeatedly, you'll see entries from each failed attempt.
Filtering and Time-Based Queries
To narrow down the results, especially after a recent failure, you can use flags like --since and --priority:
- Show logs since a specific time:
bash journalctl -u mywebapp.service --since "10 minutes ago" journalctl -u mywebapp.service --since "2023-10-26 10:00:00" - Show only error-level messages or higher:
bash journalctl -u mywebapp.service -p err - Combine with
-xefor extended explanation and verbose output:
bash journalctl -u mywebapp.service -xe --since "5 minutes ago"
This is incredibly useful asjournalctl -xeprovides additional context, including explanations for certain log messages and stack traces if available.
Understanding Log Messages
Look for keywords like Error, Failed, Warning, or application-specific messages that indicate what went wrong. Pay attention to timestamps to understand the sequence of events leading up to the failure.
Tip: If your service's ExecStart script prints to standard output or standard error, those messages are usually captured by journalctl. Ensure your scripts log descriptive error messages.
Inspecting the Unit File: The Blueprint of Your Service
Every systemd service is defined by a unit file (e.g., mywebapp.service). Misconfigurations in this file are a common source of startup failures. You need to understand what the service is trying to do.
Retrieving the Unit File
To view the active unit file for your service:
systemctl cat mywebapp.service
This command shows the exact unit file that systemd is using, including any overrides.
Key Directives to Check
Focus on the [Service] section for execution-related issues and [Unit] for dependencies.
ExecStart: This is the command systemd executes to start your service. Verify the path is correct and the command itself is executable and runs successfully when invoked manually (e.g., as theUserspecified).
ini ExecStart=/usr/local/bin/mywebapp-start.shType: Defines the process startup type. Common types include:simple(default):ExecStartis the main process.forking:ExecStartforks a child process and the parent exits. Systemd waits for the parent to exit.oneshot:ExecStartruns and exits; systemd considers the service active as long as the command is running.notify: Service sends a notification to systemd when ready.- Incorrect
Typecan lead to systemd thinking a service failed when it actually started, or vice-versa.
User/Group: The user and group under which the service will run. Permissions issues often stem from the service attempting to access files or resources it doesn't have rights to under this user.
ini User=mywebappuser Group=mywebappgroupWorkingDirectory: The directory the service will execute from. Relative paths inExecStartor other commands depend on this.Restart: Defines when the service should be restarted. If set toon-failureoralways, a failing service might constantly restart, making it harder to catch the initial failure.TimeoutStartSec/TimeoutStopSec: How long systemd waits for the service to start or stop. If a service takes longer to initialize thanTimeoutStartSec, systemd will kill it and report a failure.
Common Unit File Issues
- Incorrect paths: Typo in
ExecStartor other file paths. - Missing
Environmentvariables: Services often require specific environment variables (e.g.,PATH) that might not be present in systemd's clean environment (see below). - Permissions: The
Userspecified doesn't have execute permissions for the script or read/write permissions for necessary data files. - Syntax errors: Simple typos in the unit file itself.
To test ExecStart manually:
Switch to the service's user and try running the command directly:
sudo -u mywebappuser /usr/local/bin/mywebapp-start.sh
This often reproduces the error seen in journalctl directly in your terminal, making debugging easier.
Dependency Management: When Services Can't Start Alone
Services often rely on other services or system components to be active before they can start themselves. Systemd uses Wants, Requires, After, and Before directives to manage these dependencies.
Identifying Dependencies
Use systemctl list-dependencies <service_name> to see what a service explicitly requires or wants to run.
systemctl list-dependencies mywebapp.service
Common directives in [Unit] section:
After=: Specifies that this service should start after the listed units. If the listed unit fails, this service will still attempt to start (unlessRequires=is also used).Requires=: Specifies that this service requires the listed units. If any of the required units fail to start, this service will not start.Wants=: A weaker form ofRequires=. If a wanted unit fails, this service will still attempt to start.
Example:
[Unit]
Description=My Web Application
After=network.target mysql.service
Requires=mysql.service
Here, mywebapp.service will only start after network.target and mysql.service have started, and it requires mysql.service to be successful. If mysql.service fails, mywebapp.service will not start.
Resolving Dependency Conflicts
If a service fails due to a dependency issue, journalctl will usually indicate which dependency couldn't be met. For example, it might state Dependency failed for My Web Application followed by details about mysql.service's failure.
Steps to resolve:
1. Check the dependent service: Run systemctl status <dependent_service> (e.g., systemctl status mysql.service) and journalctl -u <dependent_service> to troubleshoot its failure first.
2. Verify After= and Requires= directives: Ensure they correctly reflect the desired startup order and strictness. Sometimes, a service needs to wait for a specific port to be open, not just the service to be active. For complex cases, systemd-socket-activate or custom ExecStartPre scripts can be useful.
Environment Variables and Paths: The Hidden Gotchas
Systemd services run in a very clean and minimal environment. This often leads to issues where commands that work perfectly in a user's shell fail when run by systemd because crucial environment variables (like PATH) are missing.
Systemd's Clean Environment
When systemd starts a service, it doesn't inherit the full environment of the user who initiated systemctl start. The PATH variable, for instance, is often stripped down, meaning commands like python or node might not be found if they're not in standard locations like /usr/bin or /bin.
Symptom: ExecStart=/usr/local/bin/myscript.sh fails with