Mastering Service Discovery: How to List All Running and Available Services on Linux
Knowing what’s active or enabled on a Linux host isn’t optional—it underpins troubleshooting, performance tuning, and especially containment against lateral movement or resource leakage. Relying on a single tool or just the systemd interface is naive; service management in Linux remains fragmented across init systems, legacy startup scripts, and plain userland launches.
Quick view: The usual suspects rarely tell the full story
systemctl list-units --type=service --state=running
Gives you only running systemd services—ignoring everything not registered in its unit database. Try:
systemctl list-unit-files --type=service
for all recognized unit files; but again, only what systemd “sees”. Unregistered binaries or leftover init scripts won’t show.
Legacy and compatibility: /etc/init.d/
, chkconfig
, and Upstart
Init scripts (SysVinit, present on older RPM/DEB distros and occasionally still used for backward compatibility):
ls /etc/init.d/
Many distros keep these directories populated for non-systemd managed services, though not always used at boot.
To check enabled SysVinit services on RHEL 6/CentOS 6:
chkconfig --list | grep '3:on'
(3 is standard multi-user/no GUI; adjust for custom runlevels.)
Upstart (common on Ubuntu ≤ 14.10, some custom appliances)
initctl list
If you see:
init: unrecognized job: <foo>
you’re not running Upstart, move on.
Note: Confirm your primary init system via
ps -p 1 -o comm=
and verify the output (
systemd
,init
, orupstart
).
Hidden daemons: Processes not managed by unit/init scripts
Not every long-running process is a registered service. Look for custom socket servers, Java VMs, or hand-rolled Go daemons kicked off by cron or rc.local.
ps -eo pid,ppid,cmd,etime | grep -E 'nginx|httpd|postgres|sshd|java|go|python'
This surface-scraping approach is blunt but occasionally reveals “zombie” daemons—services started by a user or installer script and forgotten.
Network sockets: Which processes bind to which ports
Network-exposed services remain attack surface #1. Use ss
, or fallback to netstat
(if available):
sudo ss -tulnp
Columns show LISTEN state, associated PIDs, and executable names.
Proto | Local Address | PID/Program name |
---|---|---|
tcp | 0.0.0.0:22 | 651/sshd |
tcp | 127.0.0.1:5432 | 8912/postgres |
Grep for high-numbered ports; they're often where ad-hoc or third-party software listens.
Systemd: What's enabled to start at boot?
systemctl list-unit-files --type=service --state=enabled
This shows everything that will attempt to start at boot (though dependencies and ordering can still interfere). Spot-check for spurious entries—“stale” units from failed uninstallations are common.
For SysVinit:
ls -l /etc/rc3.d/S*
The ordering (numbers after S) determines start sequence, and missing scripts can cause inconsistent system states—seen this trip up more than one migration.
Practical example: Enumerating services on a Fedora 38 host
-
systemd units (running):
sudo systemctl list-units --type=service --state=running
-
Registered services (installed):
sudo systemctl list-unit-files --type=service
-
Legacy init scripts:
ls /etc/init.d/
-
Sockets in use:
sudo ss -tulnp | grep -v '127.0.0.1'
-
Detached or user-launched daemons:
ps aux | grep -E 'python|node|go|java|daemon'
-
Detect unexpected autostarts:
find /etc/rc*.d/ -type l -lname '*foo*'
Pro tip: Compare after patching or software installs
After dnf upgrade
or a third-party app install, diff your service listing (systemctl list-unit-files --type=service > /tmp/services-before.txt
) before and after. Many installers silently drop in autostart units.
Side effects and common missteps
- On hybrid (old-to-new) systems, both SysVinit and systemd scripts can exist concurrently—potentially fighting for control (notably on Debian 8, RHEL 7 upgrades).
- Not every binary in
/etc/init.d/
has meaning, especially after package churn. Check file timestamps and contents for relevance. systemctl daemon-reload
is required after changing unit files for immediate effect; forgetting causes confusion mid-maintenance.
Key takeaway
Comprehensive service inventory requires cross-checking multiple sources: systemd, SysVinit, Upstart, direct process scans, and network binders. Anything less, you'll miss services—opening the door to unreliable operations and incomplete security postures.
For a deeper dive, automate these queries via custom Ansible modules or use tools like osquery
for fleet-wide visibility. But start with rigor at the command line—plenty of surprises still crop up.
Further reading: man pages for systemctl(1)
, ss(8)
, and distribution-specific admin guides. Regular review of these techniques is best practice, especially ahead of quarterly patch cycles or before rolling out software to production estates.