xinetd is one of those pieces of Linux infrastructure that quietly does its job in the background of older systems, rarely gets a second thought, and then becomes a genuine puzzle the first time you need to add a new service to it and can’t remember the config syntax. It’s the “extended internet services daemon,” a super-server that listens on behalf of multiple network services and only launches the actual service process when a connection actually arrives — a resource-conservation and access-control pattern that predates the always-running-daemon model most modern services use under systemd.
What a Super-Server Does and Why It Existed
Before xinetd (and its predecessor, the original inetd), every network service — telnet, FTP, finger, talk, and dozens of smaller utilities — had to run as its own persistent daemon, sitting in memory and listening on its port continuously, whether or not anyone was actually using it. On systems with limited memory (which was essentially every system, historically), this was wasteful.
The super-server model solves this: one process, xinetd, listens on all the ports for configured services simultaneously. When a connection arrives on one of those ports, xinetd accepts it, forks, and executes the actual service binary to handle that specific connection, then the service process exits when the connection ends — no persistent per-service daemon required.
This model has genuinely fallen out of favor for most workloads, replaced by systemd socket activation (which does something conceptually similar but integrates with the modern service manager) or simply running services as always-on daemons on systems with plenty of RAM to spare. But xinetd is still present, actively used, and worth understanding on older RHEL/CentOS systems, embedded distributions, and specific service categories (like certain TFTP, time, or diagnostic services) that still default to it.
Installing xinetd
Debian/Ubuntu:
sudo apt update
sudo apt install xinetd
RHEL/CentOS/Fedora:
sudo dnf install xinetd
Enable and start it:
sudo systemctl enable --now xinetd
sudo systemctl status xinetd
Configuration Structure
xinetd’s configuration lives in two places:
/etc/xinetd.conf— global defaults applied to all services unless overridden./etc/xinetd.d/— a directory containing one file per service, each defining that service’s specific settings.
Global Config Example (/etc/xinetd.conf)
defaults
{
instances = 60
log_type = SYSLOG authpriv
log_on_success = HOST PID
log_on_failure = HOST
cps = 25 30
}
includedir /etc/xinetd.d
instances— max simultaneous instances of any one service.log_type— where logs go (typically syslog’sauthprivfacility).log_on_success/log_on_failure— what detail to log for successful/failed connection attempts.cps— connections-per-second rate limiting:cps 25 30allows 25 connections/second, then waits 30 seconds if that rate is exceeded, a basic built-in DoS mitigation.includedir— pulls in every service definition file from/etc/xinetd.d/.
Per-Service Configuration File Structure
Each file in /etc/xinetd.d/ follows this pattern:
service SERVICE_NAME
{
disable = no
socket_type = stream
protocol = tcp
wait = no
user = root
server = /path/to/server/binary
server_args = arguments
port = PORT_NUMBER
only_from = allowed hosts/networks
no_access = denied hosts/networks
log_on_success += extra logging options
log_on_failure += extra logging options
}
Key Directive Reference
| Directive | Meaning |
|---|---|
disable | yes/no — whether this service is active |
socket_type | stream (TCP), dgram (UDP), raw, seqpacket |
protocol | tcp, udp |
wait | yes (single-threaded, service handles its own concurrency) or no (xinetd forks a new instance per connection) |
user/group | Which local user/group the service process runs as |
server | Full path to the executable |
server_args | Arguments passed to the executable |
port | Which port to listen on |
only_from | Whitelist of allowed client addresses/networks |
no_access | Blacklist of denied client addresses/networks |
access_times | Time-of-day window the service is available |
instances | Per-service override of the global max instance count |
bind/interface | Bind to a specific local interface/IP only |
Example: Configuring a TFTP Server
TFTP is one of the more common services still legitimately run under xinetd today, particularly for PXE network boot environments.
sudo dnf install tftp-server # RHEL/Fedora
sudo apt install tftpd-hpa # Debian/Ubuntu (note: Debian's tftpd-hpa typically uses its own init/systemd unit rather than xinetd by default)
/etc/xinetd.d/tftp (RHEL-style):
service tftp
{
socket_type = dgram
protocol = udp
wait = yes
user = root
server = /usr/sbin/in.tftpd
server_args = -s /var/lib/tftpboot
disable = no
per_source = 11
cps = 100 2
flags = IPv4
}
Restart xinetd to apply:
sudo systemctl restart xinetd
Access Control Within xinetd
Beyond the older TCP Wrappers integration (xinetd can be compiled with libwrap support, honoring /etc/hosts.allow//etc/hosts.deny in addition to its own rules), xinetd has native access control directives that don’t depend on that separate mechanism:
service telnet
{
disable = no
only_from = 192.168.1.0/24 10.0.0.5
no_access = 192.168.1.100
access_times = 08:00-18:00
}
This example allows telnet only from the 192.168.1.0/24 subnet and 10.0.0.5, explicitly blocks 192.168.1.100 even though it’s inside that subnet (a more specific rule overriding a broader one), and only during business hours.
Rate Limiting and DoS Mitigation
xinetd includes several built-in protections worth knowing:
per_source = 5 # max simultaneous connections from a single source IP
cps = 25 30 # max connections per second globally, then pause N seconds if exceeded
max_load = 4 # refuse new connections if system load average exceeds this
These are genuinely useful defenses against simple flooding attacks targeting a specific service, applied before the connection ever reaches the actual service binary.
Logging
Check what xinetd has logged:
sudo journalctl -u xinetd
sudo grep xinetd /var/log/syslog # Debian/Ubuntu
sudo grep xinetd /var/log/messages # RHEL/CentOS
Increase verbosity for troubleshooting by adding to the service block:
log_on_success += DURATION
log_on_failure += RECORD
Enabling and Disabling Individual Services
Rather than editing files by hand every time, RHEL-family systems traditionally provided a helper:
sudo chkconfig telnet on
sudo chkconfig telnet off
On systems without chkconfig, edit the disable line directly and restart xinetd:
sudo sed -i 's/disable.*=.*yes/disable = no/' /etc/xinetd.d/telnet
sudo systemctl restart xinetd
Always verify the actual running state after any change:
sudo ss -tulnp | grep xinetd
The wait Directive: A Deeper Look at Concurrency Handling
The wait directive is one of the more commonly misunderstood settings, and getting it wrong produces genuinely confusing behavior — either a service that can’t handle concurrent connections at all, or one that spawns far more instances than intended.
wait = yes (single-threaded mode) — xinetd hands off the listening socket itself to the service and then stops managing that socket entirely until the service process exits. The service is fully responsible for its own concurrency (accepting multiple simultaneous connections on the same socket itself). This mode is appropriate for datagram-based services like TFTP, where the “connection” is really a self-contained series of independent request/response exchanges the daemon manages internally.
wait = no (multi-threaded mode) — xinetd keeps managing the listening socket itself, and for every new connection, forks a fresh instance of the service to handle just that one connection, then continues listening for the next. This is the appropriate mode for typical connection-oriented services like a shell or FTP session, where each client genuinely needs its own separate, isolated process.
# Verify which mode a running instance is actually configured for
grep wait /etc/xinetd.d/tftp
Getting this backwards for a stream-oriented service (setting wait = yes when it should be no) typically manifests as the service handling only one client at a time, with subsequent connection attempts hanging until the first client disconnects — a symptom that looks like a completely different kind of bug until you check this specific setting.
A Complete Walkthrough: Adding a New Custom Service to xinetd
Beyond configuring existing packaged services, it’s worth walking through adding an entirely custom service — a pattern that comes up when integrating a small internal tool that doesn’t ship its own systemd unit or standalone daemon mode.
Suppose you have a simple diagnostic script at /usr/local/bin/status-check.sh that you want reachable on a specific internal port, restricted to your monitoring subnet:
sudo tee /etc/xinetd.d/status-check <<'EOF'
service status-check
{
disable = no
type = UNLISTED
socket_type = stream
protocol = tcp
port = 9999
wait = no
user = nobody
server = /usr/local/bin/status-check.sh
only_from = 10.0.5.0/24
log_on_success += DURATION
log_on_failure += HOST
}
EOF
The type = UNLISTED directive is required for any service not already registered in /etc/services under that exact name — without it, xinetd refuses to start the service, since it can’t resolve the service name against the standard services database. You’d also add a matching entry to /etc/services if you want the port to have a friendly name for other tools’ benefit:
echo "status-check 9999/tcp" | sudo tee -a /etc/services
Restart and verify:
sudo systemctl restart xinetd
sudo ss -tulnp | grep 9999
Environment and Resource Controls Per Service
Beyond access control, xinetd can constrain the resource footprint of each service it manages, which is genuinely useful for preventing one misbehaving service from starving the whole system:
service heavy-tool
{
disable = no
rlimit_cpu = 60
rlimit_as = 256M
nice = 10
env = PATH=/usr/local/bin:/usr/bin
passenv = LANG
}
rlimit_cpu— maximum CPU seconds a single instance may consume before being killed.rlimit_as— maximum virtual memory (address space) a single instance may use.nice— process scheduling priority adjustment, useful for de-prioritizing a background/diagnostic service relative to more important workloads.env/passenv— explicitly set or pass through specific environment variables to the launched service, rather than inheriting xinetd’s own full environment by default.
Interfacing xinetd With systemd on Modern Distros
On systemd-based systems, xinetd itself runs as a regular systemd-managed service (xinetd.service), which means it benefits from systemd’s own dependency ordering, restart policies, and logging integration even though the services xinetd itself manages don’t get individual systemd units of their own.
systemctl cat xinetd
This shows xinetd’s own unit file — worth checking if you need to adjust its restart behavior, resource limits at the xinetd process level itself (as opposed to per-service limits configured within xinetd’s own config), or startup ordering relative to networking being fully available.
[Unit]
Description=xinetd Service
After=network.target
[Service]
ExecStart=/usr/sbin/xinetd -stayalive -pidfile /run/xinetd.pid
Type=forking
PIDFile=/run/xinetd.pid
[Install]
WantedBy=multi-user.target
If you need to override any of this (a common override being adding Restart=on-failure for extra resilience), use systemctl edit xinetd rather than modifying the packaged unit file directly, so your customization survives package upgrades.
Comparing xinetd Service Definitions Across Distro Conventions
A subtlety worth knowing if you maintain configs across both Debian and RHEL-family systems: while the xinetd configuration syntax itself is identical (it’s the same upstream project), the packaged default service files that ship with common daemons sometimes differ slightly in default settings between distros — RHEL-family packages have historically been somewhat more conservative with default only_from and logging settings than some Debian-packaged equivalents. Always read the actual shipped file after installing a package rather than assuming a configuration you’ve seen on one distro applies identically on another.
diff <(cat /etc/xinetd.d/tftp) <(ssh other-distro-host cat /etc/xinetd.d/tftp)
Troubleshooting
Service enabled in config but not responding — confirm xinetd itself is running and was restarted after the config change:
sudo systemctl status xinetd
sudo systemctl restart xinetd
“Address already in use” errors — another process (possibly a standalone daemon for the same service) is already bound to that port:
sudo ss -tulnp | grep :PORT
Connections accepted but immediately dropped — check server path is correct and the binary is actually executable by the configured user:
ls -l /path/to/server/binary
only_from/no_access rules not behaving as expected — remember more specific rules can override broader ones; test carefully and check logs (log_on_failure) to confirm which rule actually matched a given connection attempt.
xinetd vs systemd Socket Activation
Modern systemd provides a conceptually similar mechanism — socket units (.socket files) that listen on a port and start the associated service unit on first connection. The philosophical difference: systemd socket activation is meant primarily for startup efficiency and dependency ordering on services that will typically run continuously once started, while xinetd’s model assumes services genuinely start and stop per connection, with more built-in per-connection access control and rate limiting baked directly into the super-server itself.
For new deployments on systemd-based distros, socket activation is generally the more idiomatic choice; xinetd remains relevant primarily for maintaining existing configurations, embedded systems, and specific legacy services that ship with xinetd definitions by default.
Security Implications
- xinetd centralizes exposure for every service it manages — a misconfiguration in
/etc/xinetd.conf(like defaultlog_on_successsettings, or an overly permissiveonly_from) can weaken every service under its control at once. - Because many of the classic xinetd-managed services (telnet, tftp, finger, rsh-related daemons) are themselves legacy plaintext protocols, running xinetd at all is sometimes a signal worth auditing — the question isn’t just “is xinetd configured securely” but “should this particular service exist on this network at all.”
- Combine xinetd’s own access controls with kernel-level firewall rules (iptables/firewalld) rather than relying on xinetd’s
only_from/no_accessas your only line of defense — defense in depth matters here as much as anywhere else.
Summary
xinetd is a super-server that listens on behalf of multiple network services and launches them on demand, configured through /etc/xinetd.conf for global defaults and per-service files in /etc/xinetd.d/. It provides genuinely useful built-in access control (only_from/no_access/access_times) and rate limiting (cps/per_source/max_load) beyond what the underlying services might implement themselves. While largely superseded by always-on daemons and systemd socket activation for new deployments, it remains actively relevant for services like TFTP and for maintaining legacy infrastructure.