Fixing systemd Service Startup Failures on Ubuntu 22.04
A comprehensive guide to diagnosing and resolving systemd service startup failures on Ubuntu 22.04 LTS, including common error patterns and recovery strategies.
Fixing systemd Service Startup Failures on Ubuntu 22.04
When you migrate from cron-based initialization to systemd services, startup failures can feel cryptic. I encountered this firsthand while deploying a data pipeline on an Ubuntu 22.04 server in Lisbon. The service refused to start, and the logs offered little clarity. This post walks through the diagnosis and resolution process step by step.
Environment
- OS: Ubuntu 22.04.3 LTS (Jammy Jellyfish)
- systemd version: 249
- Service type: Custom Python application managed by systemd
- Deployment: DigitalOcean droplet, 4GB RAM, 2 vCPUs
$ lsb_release -a
Distributor ID: Ubuntu
Description: Ubuntu 22.04.3 LTS
Release: 22.04
Codename: jammy
$ systemd --version
systemd 249 (249.11-0ubuntu3.12)The Problem
After creating a new systemd service unit file for a Python-based data collector, the service refused to start:
$ sudo systemctl start data-collector
Job for data-collector.service failed because the control process exited with error code.
See "systemctl status data-collector.service" and "journalctl -xe" for details.
$ sudo systemctl status data-collector
Γ data-collector.service - Data Collector Service
Loaded: loaded (/lib/systemd/system/data-collector.service; enabled; vendor preset: enabled)
Active: failed (Result: exit-code) since Sat 2025-07-20 14:23:11 WET; 5s ago
Process: 12345 ExecStart=/usr/bin/python3 /opt/data-collector/main.py (code=exited, status=1/FAILURE)
Main PID: 12345 (code=exited, status=1/FAILURE)
CPU: 234msThe exit code 1/FAILURE is generic. We need more detail.
Analysis
Step 1: Check the Journal Logs
$ journalctl -u data-collector --no-pager -n 50
-- Logs begin at Sat 2025-07-20 08:00:00 WET, end at Sat 2025-07-20 14:25:00 WET. --
Jul 20 14:23:11 ubuntu systemd[1]: Starting Data Collector Service...
Jul 20 14:23:11 ubuntu python3[12345]: Traceback (most recent call last):
Jul 20 14:23:11 ubuntu python3[12345]: File "/opt/data-collector/main.py", line 5, in
Jul 20 14:23:11 ubuntu python3[12345]: import psycopg2
Jul 20 14:23:11 ubuntu python3[12345]: ModuleNotFoundError: No module named 'psycopg2' Step 2: Verify the Unit File
$ cat /lib/systemd/system/data-collector.service
[Unit]
Description=Data Collector Service
After=network.target postgresql.service
[Service]
Type=simple
User=datacollector
Group=datacollector
WorkingDirectory=/opt/data-collector
ExecStart=/usr/bin/python3 /opt/data-collector/main.py
Restart=on-failure
RestartSec=5
[Install]
WantedBy=multi-user.targetThe unit file itself looks correct. The issue is that the virtual environment's packages are not being loaded.
Step 3: Identify the Root Cause
The service was running as datacollector user, but the Python packages were installed in a virtual environment under /opt/data-collector/venv. The ExecStart was using the system Python, not the venv Python.
$ sudo -u datacollector /opt/data-collector/venv/bin/python -c "import psycopg2; print('OK')"
OKThe venv Python works when invoked directly. The fix is to point ExecStart to the venv Python binary.
Solution
Option A: Use the Virtual Environment Python
[Service]
Type=simple
User=datacollector
Group=datacollector
WorkingDirectory=/opt/data-collector
ExecStart=/opt/data-collector/venv/bin/python /opt/data-collector/main.py
Restart=on-failure
RestartSec=5
Environment="PATH=/opt/data-collector/venv/bin:/usr/bin:/bin"Option B: Source an Environment File
Create /etc/data-collector.env:
VIRTUAL_ENV=/opt/data-collector/venv
PATH=/opt/data-collector/venv/bin:$PATH
PYTHONUNBUFFERED=1Update the unit file:
[Service]
Type=simple
User=datacollector
Group=datacollector
WorkingDirectory=/opt/data-collector
EnvironmentFile=/etc/data-collector.env
ExecStart=/usr/bin/python3 /opt/data-collector/main.py
Restart=on-failure
RestartSec=5Reload and Test
$ sudo systemctl daemon-reload
$ sudo systemctl start data-collector
$ sudo systemctl status data-collector
β data-collector.service - Data Collector Service
Loaded: loaded (/lib/systemd/system/data-collector.service; enabled; vendor preset: enabled)
Active: active (running) since Sat 2025-07-20 14:30:00 WET; 3s ago
Main PID: 13001 (python3)
Tasks: 1 (limit: 4567)
Memory: 45.2M
CPU: 1.234s
CGroup: /system.slice/data-collector.service
ββ13001 /opt/data-collector/venv/bin/python /opt/data-collector/main.pyEnable on Boot
$ sudo systemctl enable data-collector
Created symlink /etc/systemd/system/multi-user.target.wants/data-collector.service β /lib/systemd/system/data-collector.service.Common Patterns for systemd Startup Failures
Here are the most frequent causes I have encountered:
| Error Pattern | Likely Cause | Fix |
|---|---|---|
| `ModuleNotFoundError` | Wrong Python interpreter | Use venv Python path |
| `Permission denied` | Incorrect User/Group | Check file ownership |
| `No such file or directory` | Wrong WorkingDirectory or ExecStart | Verify paths exist |
| `Address already in use` | Port conflict | Stop conflicting process |
| `Timeout` | Service takes too long to start | Increase TimeoutStartSec |
Lessons Learned
- Always test the ExecStart command manually as the target user before enabling the service.
- Use
journalctl -u <service> -fto follow logs in real time during debugging. - Set
EnvironmentorEnvironmentFileexplicitly rather than relying on shell profiles. - Keep unit files in
/etc/systemd/system/for custom services, not/lib/systemd/system/. - Run
systemd-analyze verify <unit>to catch syntax errors before starting the service.
This blog does not accept any external sponsorships, affiliate marketing, or ad revenue.