When migrating from older Ubuntu versions (using SysV init) to systemd-based systems (Ubuntu 16.04+), many administrators encounter a frustrating behavior: systemd often overrides custom status
commands defined in legacy init scripts. This occurs because:
- systemd has its own service status detection mechanism
- The legacy init script compatibility layer doesn't always respect custom status commands
- Systemd prioritizes its native unit file configuration over script contents
The proper way to handle this is to create a native systemd service unit file. Here's an example that respects your custom status command:
[Unit]
Description=My Custom Service
After=network.target
[Service]
Type=forking
ExecStart=/etc/init.d/my_service start
ExecStop=/etc/init.d/my_service stop
ExecReload=/etc/init.d/my_service restart
ExecStatus=/etc/init.d/my_service status # This is the critical line
RemainAfterExit=yes
[Install]
WantedBy=multi-user.target
If you must keep using the init script while forcing systemd to execute your custom status command, you have several options:
Option 1: Use the Direct Script Path
While systemctl status
will still show systemd's view, you can force execution of your status command with:
/etc/init.d/my_service status
Option 2: Create a Status Exec Directive
Modify your systemd service file to include:
ExecStatus=/path/to/your/script status
Then create a helper script:
#!/bin/bash
systemctl status my_service.service | head -n 3
/etc/init.d/my_service status
exit 0
Ubuntu's systemd actually maintains backward compatibility through the service
command. If your script follows LSB headers correctly, try:
sudo service my_service status --full
For scripts that must work across both init systems, consider this hybrid approach:
#!/bin/bash
# Systemd detection
if pidof systemd &>/dev/null; then
case "$1" in
status)
# Your custom status implementation
if [ -f /var/run/my_service.pid ]; then
echo "Running (PID $(cat /var/run/my_service.pid))"
else
echo "Stopped"
fi
exit 0
;;
*)
# Delegate other commands to systemctl
systemctl "$1" my_service
;;
esac
else
# Original SysV init implementation
case "$1" in
# ... original case statements ...
esac
fi
- Always test changes in a development environment first
- The
Type=forking
directive is often needed for legacy compatibility - Ensure your status command returns proper exit codes (0 for running, 3 for stopped)
- Consider migrating fully to systemd unit files for better long-term maintainability
When migrating from Ubuntu 8.04 to 16.04, many developers encounter a frustrating behavior: systemd replaces the custom status
command from legacy init scripts with its own standardized output. This becomes particularly problematic when:
- Existing monitoring tools parse specific status output formats
- You need backward compatibility across different Linux versions
- The service's status logic contains business-specific checks
systemd implements LSB compliance differently than traditional init systems. When it encounters an init script:
1. It first checks for native systemd unit files
2. For legacy scripts, it provides "compatibility" through systemd-sysv-generator
3. The generator creates transient units that don't fully respect all script commands
Instead of fighting systemd, create a dedicated unit file that delegates status checks to your script:
# /etc/systemd/system/my_service.service
[Unit]
Description=My Custom Service
After=network.target
[Service]
Type=forking
ExecStart=/etc/init.d/my_service start
ExecStop=/etc/init.d/my_service stop
ExecReload=/etc/init.d/my_service restart
ExecStatus=/etc/init.d/my_service status # This is the key line
RemainAfterExit=yes
[Install]
WantedBy=multi-user.target
Create a status override file:
# /etc/systemd/system/my_service.service.d/status.conf
[Service]
ExecStart=
ExecStart=/etc/init.d/my_service start
ExecStatus=/etc/init.d/my_service status
Then run:
sudo systemctl daemon-reload
sudo systemctl reset-failed my_service.service
For quick debugging, bypass systemd entirely:
sudo /etc/init.d/my_service status
# Or using the direct path:
sudo /path/to/actual/executable --status-flag
Check if your status command now works through systemd:
systemctl status my_service.service --no-pager
# Should show your custom output instead of systemd's standard format
- Always test after modifying unit files:
systemctl daemon-reload
- Consider migrating fully to systemd for better integration
- Document the status output format for future maintenance