What inetd (the super-server) is
Older services such as telnet and FTP traditionally do not listen themselves; they are started only when a connection arrives. The component that accepts those connections is inetd, the super-server.
| File to edit | Purpose |
|---|---|
/etc/inetd.conf | Switching the built-in services on and off |
/etc/inetd.d/<name> | Adding one service per file (packages put their entries here too) |
/etc/hosts.allow / /etc/hosts.deny | Restricting sources with TCP Wrappers |
These implementations are available on Ubuntu 26.04. This article uses inetutils-inetd (the one inetutils-telnetd pulls in).
| Package | Notes |
|---|---|
inetutils-inetd | The GNU Inetutils version, used here |
openbsd-inetd | The OpenBSD version |
xinetd | The extended version. Its last upstream release was in 2012, and RHEL 9 dropped it |
| systemd socket activation | Does the same thing without inetd (compared at the end) |
Installing
sudo apt install -y inetutils-inetdBefore installing, the configuration files do not exist.
kazulog@sv1:~$ apt-cache policy inetutils-inetd | head -3
inetutils-inetd:
Installed: (none)
Candidate: 2:2.7-2ubuntu1.1
kazulog@sv1:~$ ls -l /etc/inetd.conf /etc/inetd.d 2>&1
ls: cannot access '/etc/inetd.conf': No such file or directory
ls: cannot access '/etc/inetd.d': No such file or directory
kazulog@sv1:~$ sudo ss -tlnp | grep -E ":(7|13|10013|10014) " ; echo "ss rc=$?"
ss rc=1tcpd for TCP Wrappers and update-inetd for editing the configuration come with it.
kazulog@sv1:~$ sudo apt install -y inetutils-inetd | cat
WARNING: apt does not have a stable CLI interface. Use with caution in scripts.
Reading package lists...
Building dependency tree...
Reading state information...
Solving dependencies...
Installing:
inetutils-inetd
Installing dependencies:
tcpd update-inetd
Summary:
Upgrading: 0, Installing: 3, Removing: 0, Not Upgrading: 96
Download size: 92.8 kB
Space needed: 361 kB / 63.1 GB available
Get:1 http://archive.ubuntu.com/ubuntu resolute/universe amd64 tcpd amd64 7.6.q-36build2 [24.6 kB]
Get:2 http://archive.ubuntu.com/ubuntu resolute/main amd64 update-inetd all 4.54build1 [23.0 kB]
Get:3 http://archive.ubuntu.com/ubuntu resolute-updates/universe amd64 inetutils-inetd amd64 2:2.7-2ubuntu1.1 [45.2 kB]
Preconfiguring packages ...
Fetched 92.8 kB in 1s (62.2 kB/s)
Selecting previously unselected package tcpd.
(Reading database ... 91400 files and directories currently installed.)
Preparing to unpack .../tcpd_7.6.q-36build2_amd64.deb ...
Unpacking tcpd (7.6.q-36build2) ...
Selecting previously unselected package update-inetd.
Preparing to unpack .../update-inetd_4.54build1_all.deb ...
Unpacking update-inetd (4.54build1) ...
Selecting previously unselected package inetutils-inetd.
Preparing to unpack .../inetutils-inetd_2%3a2.7-2ubuntu1.1_amd64.deb ...
Unpacking inetutils-inetd (2:2.7-2ubuntu1.1) ...
Setting up update-inetd (4.54build1) ...
Setting up tcpd (7.6.q-36build2) ...
Setting up inetutils-inetd (2:2.7-2ubuntu1.1) ...
Created symlink '/etc/systemd/system/inetd.service' → '/usr/lib/systemd/system/inetutils-inetd.service'.
Created symlink '/etc/systemd/system/multi-user.target.wants/inetutils-inetd.service' → '/usr/lib/systemd/system/inetutils-inetd.service'.What the configuration files contain
/etc/inetd.conf appears, but all four built-in service lines (17-20) are commented out, and /etc/inetd.d/ is empty.
kazulog@sv1:~$ ls -l /etc/inetd.conf /etc/inetd.d
-rw-r--r-- 1 root root 1062 Sep 12 09:46 /etc/inetd.conf
/etc/inetd.d:
total 0
kazulog@sv1:~$ cat /etc/inetd.conf
# /etc/inetd.conf: see inetd(8) for further informations.
#
# Internet superserver configuration database.
#
#
# Lines starting with "#:LABEL:" or "#<off>#" should not
# be changed unless you know what you are doing!
#
# If you want to disable an entry so it is not touched during
# package updates just comment it out with a single '#' character.
#
# Packages should modify this file by using update-inetd(8).
#
# <service_name> <sock_type> <proto> <flags> <user> <server_path> <args>
#
#:INTERNAL: Internal services
#discard stream tcp6 nowait root internal
#discard dgram udp6 wait root internal
#daytime stream tcp6 nowait root internal
#time stream tcp6 nowait root internal
#:STANDARD: These are standard services.
#:BSD: Shell, login, exec and talk are BSD protocols.
#:MAIL: Mail, news and uucp services.
#:INFO: Info services
#:BOOT: TFTP service is provided primarily for booting. Most sites
# run this only on machines acting as "boot servers."
#:RPC: RPC based services
#:HAM-RADIO: amateur-radio services
#:OTHER: Other servicesEach line has this format.
<service_name> <sock_type> <proto> <flags> <user> <server_path> <args>| Field | Meaning |
|---|---|
service_name | The service name in /etc/services, which decides the port |
sock_type | stream (TCP) or dgram (UDP) |
proto | tcp / udp / tcp6 / udp6. tcp6 also accepts IPv4 connections |
flags | nowait (start per connection) or wait |
user | The user to run as |
server_path | The program to start. internal means inetd handles it itself |
With no active line at all, inetd does not start.
kazulog@sv1:~$ systemctl status inetutils-inetd --no-pager | head -8 | cat
○ inetutils-inetd.service - GNU Network Utilities internet superserver
Loaded: loaded (/usr/lib/systemd/system/inetutils-inetd.service; enabled; preset: enabled)
Active: inactive (dead) (Result: exec-condition) since Sat 2026-09-12 09:46:26 JST; 7s ago
Invocation: 1648d08c1ead40689a99100fac11824a
Condition: start condition unmet at Sat 2026-09-12 09:46:26 JST; 7s ago
Docs: man:inetutils-inetd(8)
https://www.gnu.org/software/inetutils/manual/
Process: 2617 ExecCondition=grep -qr ^[0-9A-Za-z/] /etc/inetd.conf /etc/inetd.d/ (code=exited, status=1/FAILURE)Enabling a built-in service (/etc/inetd.conf)
Enable daytime (port 13, which returns the date and time). Delete the single # at the start of line 19.
sudo vi /etc/inetd.confdaytime stream tcp6 nowait root internalkazulog@sv1:~$ grep -n "daytime\|discard" /etc/inetd.conf
17:#discard stream tcp6 nowait root internal
18:#discard dgram udp6 wait root internal
19:daytime stream tcp6 nowait root internal
kazulog@sv1:~$ sudo systemctl restart inetutils-inetd
kazulog@sv1:~$ systemctl is-active inetutils-inetd
active
kazulog@sv1:~$ sudo ss -tlnp 'sport = :13'
State Recv-Q Send-Q Local Address:Port Peer Address:Port Process
LISTEN 0 10 *:13 *:* users:(("inetutils-inetd",pid=3158,fd=4)) Note that it listens on *:13. The protocol field says tcp6, so the IPv6 socket also accepts IPv4 connections — and an IPv4 client does get the date back.
kazulog@sv2:~$ timeout 5 nc 192.168.100.10 13 < /dev/null
Sat Sep 12 09:47:04 2026Adding one service (/etc/inetd.d/)
Instead of extending /etc/inetd.conf, you can drop a file into /etc/inetd.d/. Here we add echo (port 7, which returns what it receives).
sudo vi /etc/inetd.d/echo# echo サービス(inetd の内蔵サービス)
echo stream tcp nowait root internalkazulog@sv1:~$ ls -l /etc/inetd.d/
total 4
-rw-r--r-- 1 root root 90 Sep 12 09:47 echo
kazulog@sv1:~$ cat /etc/inetd.d/echo
# echo サービス(inetd の内蔵サービス)
echo stream tcp nowait root internal
kazulog@sv1:~$ sudo systemctl restart inetutils-inetd
kazulog@sv1:~$ sudo ss -tlnp 'sport = :7'
State Recv-Q Send-Q Local Address:Port Peer Address:Port Process
LISTEN 0 10 0.0.0.0:7 0.0.0.0:* users:(("inetutils-inetd",pid=3692,fd=6)) kazulog@sv2:~$ printf 'hello inetd\n' | timeout 5 nc 192.168.100.10 7Running your own service
Put your own program in server_path and it is started for every connection. Here is a hello service on port 10013, which needs three files.
sudo vi /etc/inetd.d/hello
sudo vi /usr/local/bin/hello.sh
sudo vi /etc/services# 自作サービス: 10013 番につながると /usr/local/bin/hello.sh を実行する
hello stream tcp nowait nobody /usr/sbin/tcpd /usr/local/bin/hello.sh#!/bin/sh
echo "hello from inetd on $(hostname)"hello 10013/tcp # 検証用の自作サービスSetting server_path to /usr/sbin/tcpd and passing the real program as its argument means the connection goes through the TCP Wrappers check (next section) before the program starts.
kazulog@sv1:~$ cat /etc/inetd.d/hello
# 自作サービス: 10013 番につながると /usr/local/bin/hello.sh を実行する
hello stream tcp nowait nobody /usr/sbin/tcpd /usr/local/bin/hello.sh
kazulog@sv1:~$ grep -n hello /etc/services
366:hello 10013/tcp # 検証用の自作サービス
kazulog@sv1:~$ cat /usr/local/bin/hello.sh
#!/bin/sh
echo "hello from inetd on $(hostname)"
kazulog@sv1:~$ sudo systemctl restart inetutils-inetd
kazulog@sv1:~$ sudo ss -tlnp 'sport = :10013'
State Recv-Q Send-Q Local Address:Port Peer Address:Port Process
LISTEN 0 10 0.0.0.0:10013 0.0.0.0:* users:(("inetutils-inetd",pid=4189,fd=7)) kazulog@sv2:~$ timeout 5 nc 192.168.100.10 10013 < /dev/null
hello from inetd on sv1Restricting sources (/etc/hosts.allow and /etc/hosts.deny)
Services that go through tcpd can be restricted with /etc/hosts.allow and /etc/hosts.deny. hosts.allow is evaluated first, and anything allowed there never reaches hosts.deny.
ALL: ALL in /etc/hosts.deny also cuts off SSH. Since OpenSSH 9.8 the per-connection sshd-session binary is linked against libwrap. Put sshd: ALL in /etc/hosts.allow first.sudo vi /etc/hosts.allow
sudo vi /etc/hosts.deny# SSH は必ず許可する(締め出し防止)
sshd: ALL# 自作サービスは 192.168.100.20 からの接続を拒否する
hello.sh: 192.168.100.20The name to write is not the service name but the file name of the program being started. The server_path in /etc/inetd.d/hello is /usr/local/bin/hello.sh, so the name is hello.sh. tcpdmatch confirms it.
kazulog@sv1:~$ cat /etc/hosts.allow
# /etc/hosts.allow: list of hosts that are allowed to access the system.
# See the manual pages hosts_access(5) and hosts_options(5).
#
# Example: ALL: LOCAL @some_netgroup
# ALL: .foobar.edu EXCEPT terminalserver.foobar.edu
#
# If you're going to protect the portmapper use the name "rpcbind" for the
# daemon name. See rpcbind(8) and rpc.mountd(8) for further information.
#
# SSH は必ず許可する(締め出し防止)
sshd: ALL
kazulog@sv1:~$ cat /etc/hosts.deny
# /etc/hosts.deny: list of hosts that are _not_ allowed to access the system.
# See the manual pages hosts_access(5) and hosts_options(5).
#
# Example: ALL: some.host.name, .some.domain
# ALL EXCEPT in.fingerd: other.host.name, .other.domain
#
# If you're going to protect the portmapper use the name "rpcbind" for the
# daemon name. See rpcbind(8) and rpc.mountd(8) for further information.
#
# The PARANOID wildcard matches any host whose name does not match its
# address.
#
# You may wish to enable this to ensure any programs that don't
# validate looked up hostnames still leave understandable logs. In past
# versions of Debian this has been the default.
# ALL: PARANOID
# 自作サービスは 192.168.100.20 からの接続を拒否する
hello.sh: 192.168.100.20
kazulog@sv1:~$ tcpdmatch hello.sh 192.168.100.20
client: address 192.168.100.20
server: process hello.sh
access: deniedThe refused side connects but receives nothing.
kazulog@sv2:~$ timeout 5 nc 192.168.100.10 10013 < /dev/null; echo "nc rc=$?"
nc rc=0kazulog@sv1:~$ sudo journalctl --since "-5 min" --no-pager | grep -a "hello.sh" | tail -3
Sep 12 09:47:46 sv1 tcpdmatch[5059]: warning: hello.sh: no such process name in /etc/inetd.conf
Sep 12 09:48:11 sv1 tcpdmatch[5271]: warning: hello.sh: no such process name in /etc/inetd.conf
Sep 12 09:48:15 sv1 hello.sh[5575]: refused connect from 192.168.100.20 (192.168.100.20)Comparing with systemd socket activation
The same idea — start a program only when a connection arrives — also works without inetd, using systemd. Here the same hello.sh is started on port 10014.
sudo vi /etc/systemd/system/hello-socket.socket
sudo vi /etc/systemd/system/hello-socket@.service
sudo systemctl daemon-reload
sudo systemctl enable --now hello-socket.socketkazulog@sv1:~$ ls -l /etc/systemd/system/hello-socket.socket /etc/systemd/system/hello-socket@.service
-rw-r--r-- 1 root root 125 Sep 12 09:48 /etc/systemd/system/hello-socket.socket
-rw-r--r-- 1 root root 127 Sep 12 09:48 /etc/systemd/system/hello-socket@.service
kazulog@sv1:~$ cat /etc/systemd/system/hello-socket.socket
[Unit]
Description=hello service socket (systemd)
[Socket]
ListenStream=10014
Accept=yes
[Install]
WantedBy=sockets.target
kazulog@sv1:~$ cat /etc/systemd/system/hello-socket@.service
[Unit]
Description=hello service (systemd socket activation)
[Service]
ExecStart=/usr/local/bin/hello.sh
StandardInput=socket
kazulog@sv1:~$ systemctl is-active hello-socket.socket
active
kazulog@sv1:~$ sudo ss -tlnp 'sport = :10014'
State Recv-Q Send-Q Local Address:Port Peer Address:Port Process
LISTEN 0 4096 *:10014 *:* users:(("systemd",pid=1,fd=79)) What listens is systemd itself (pid=1). It answers even while hosts.deny still denies hello.sh, because systemd socket activation does not go through tcpd.
kazulog@sv2:~$ timeout 5 nc 192.168.100.10 10014 < /dev/null; echo "nc rc=$?"
hello from inetd on sv1
nc rc=0| Item | inetd | systemd socket activation |
|---|---|---|
| What listens | inetutils-inetd | systemd (pid=1) |
| Configuration | /etc/inetd.conf / /etc/inetd.d/ | .socket and .service units |
| TCP Wrappers | Works if server_path is tcpd | Not applied (use IPAddressDeny= and similar instead) |
Final state of the configuration files
This is the state with everything above applied. Six files were touched.
kazulog@sv1:~$ grep -vE '^(#|$)' /etc/inetd.conf
daytime stream tcp6 nowait root internal
kazulog@sv1:~$ ls -l /etc/inetd.d/
total 8
-rw-r--r-- 1 root root 90 Sep 12 09:47 echo
-rw-r--r-- 1 root root 160 Sep 12 09:47 hello
kazulog@sv1:~$ cat /etc/inetd.d/echo /etc/inetd.d/hello
# echo サービス(inetd の内蔵サービス)
echo stream tcp nowait root internal
# 自作サービス: 10013 番につながると /usr/local/bin/hello.sh を実行するkazulog@sv1:~$ cat /etc/hosts.allow
# /etc/hosts.allow: list of hosts that are allowed to access the system.
# See the manual pages hosts_access(5) and hosts_options(5).
#
# Example: ALL: LOCAL @some_netgroup
# ALL: .foobar.edu EXCEPT terminalserver.foobar.edu
#
# If you're going to protect the portmapper use the name "rpcbind" for the
# daemon name. See rpcbind(8) and rpc.mountd(8) for further information.
#
# SSH は必ず許可する(締め出し防止)
sshd: ALL
kazulog@sv1:~$ cat /etc/hosts.deny
# /etc/hosts.deny: list of hosts that are _not_ allowed to access the system.
# See the manual pages hosts_access(5) and hosts_options(5).
#
# Example: ALL: some.host.name, .some.domain
# ALL EXCEPT in.fingerd: other.host.name, .other.domain
#
# If you're going to protect the portmapper use the name "rpcbind" for the
# daemon name. See rpcbind(8) and rpc.mountd(8) for further information.
#
# The PARANOID wildcard matches any host whose name does not match its
# address.
#
# You may wish to enable this to ensure any programs that don't
# validate looked up hostnames still leave understandable logs. In past
# versions of Debian this has been the default.
# ALL: PARANOID
# 自作サービスは 192.168.100.20 からの接続を拒否する
hello.sh: 192.168.100.20
kazulog@sv1:~$ systemctl is-enabled inetutils-inetd; systemctl is-active inetutils-inetd
enabled
active
kazulog@sv1:~$ systemctl is-active hello-socket.socket
active
kazulog@sv1:~$ sudo ss -tlnp | grep -E ":(7|13|10013|10014) "
LISTEN 0 10 0.0.0.0:10013 0.0.0.0:* users:(("inetutils-inetd",pid=4189,fd=7))
LISTEN 0 10 0.0.0.0:7 0.0.0.0:* users:(("inetutils-inetd",pid=4189,fd=6))
LISTEN 0 4096 *:10014 *:* users:(("systemd",pid=1,fd=79))
LISTEN 0 10 *:13 *:* users:(("inetutils-inetd",pid=4189,fd=4)) | File | Change |
|---|---|
/etc/inetd.conf | Remove the # from line 19 (daytime) |
/etc/inetd.d/echo | New file (built-in service echo) |
/etc/inetd.d/hello | New file (custom service, via tcpd) |
/usr/local/bin/hello.sh | New file (the custom service itself) |
/etc/services | Append hello 10013/tcp |
/etc/hosts.allow / /etc/hosts.deny | sshd: ALL and hello.sh: 192.168.100.20 |
/etc/systemd/system/hello-socket.socket / hello-socket@.service | New files (for the comparison) |
Removing
sudo apt purge -y inetutils-inetdIf files you created remain in /etc/inetd.d/, the directory stays (dpkg warns about it). /etc/inetd.conf also remains, so delete them by hand if you no longer need them.
kazulog@sv1:~$ sudo apt purge -y inetutils-inetd | cat
WARNING: apt does not have a stable CLI interface. Use with caution in scripts.
Reading package lists...
Building dependency tree...
Reading state information...
Solving dependencies...
The following packages were automatically installed and are no longer required:
tcpd update-inetd
Use 'sudo apt autoremove' to remove them.
REMOVING:
inetutils-inetd*
Summary:
Upgrading: 0, Installing: 0, Removing: 1, Not Upgrading: 96
Freed space: 159 kB
(Reading database ... 91429 files and directories currently installed.)
Removing inetutils-inetd (2:2.7-2ubuntu1.1) ...
Processing triggers for man-db (2.13.1-1build1) ...
(Reading database ... 91420 files and directories currently installed.)
Purging configuration files for inetutils-inetd (2:2.7-2ubuntu1.1) ...
dpkg: warning: while removing inetutils-inetd, directory '/etc/inetd.d' not empty so not removed
kazulog@sv1:~$ dpkg -l | grep -E "inetd" | cat
ii update-inetd 4.54build1 all inetd configuration file updater
kazulog@sv1:~$ ls -l /etc/inetd.conf /etc/inetd.d 2>&1
-rw-r--r-- 1 root root 1061 Sep 12 09:46 /etc/inetd.conf
/etc/inetd.d:
total 8
-rw-r--r-- 1 root root 90 Sep 12 09:47 echo
-rw-r--r-- 1 root root 160 Sep 12 09:47 hello
kazulog@sv1:~$ sudo ss -tlnp | grep -E ":(7|13|10013) " ; echo "ss rc=$?"
ss rc=1Test environment and session logs
Two Ubuntu 26.04 LTS Servers on CML. The disks were wiped immediately beforehand, because packages left over from earlier testing would stop the procedure from reproducing.
sv1 sv2
ens3 192.168.100.10/24 ens3 192.168.100.20/24
inetutils-inetd 7/13/10013 nc
systemd socket 10014
| |
+----------- LAB-SW ------------+| Step | sv1 | sv2 |
|---|---|---|
| Initial state (just wiped) | show / conf / log | — |
| Installing inetutils-inetd | show / conf / log | — |
| Enabling daytime | show / conf / log | — |
| Connecting to daytime | — | show / conf / log |
| Adding echo and connecting | show / conf / log | show / conf / log |
| Adding the custom service and connecting | show / conf / log | show / conf / log |
| Restricting sources | show / conf / log | show / conf / log |
| The refusal in the log | show / conf / log | — |
| systemd socket activation | show / conf / log | show / conf / log |
| Final state of the files | show / conf / log | — |
| Removing | show / conf / log | — |
Reference
Related articles
- Changing the Hostname on Ubuntu 26.04 LTS Server (hostnamectl)
- Updating Packages on Ubuntu 26.04 LTS Server (apt update / upgrade)
- Setting the Timezone and Time Synchronisation on Ubuntu 26.04 LTS Server
- Creating Users and Granting sudo Privileges on Ubuntu 26.04 LTS Server
- Configuring the SSH Server on Ubuntu 26.04 LTS Server
- Setting Up a telnet Server on Ubuntu 26.04 LTS Server
- Configuring inetd (the Super-Server) on Ubuntu 26.04 LTS Server
- Setting Up a TFTP Server on Ubuntu 26.04 LTS Server (tftpd-hpa)
- FTP Server Setup on Ubuntu 26.04 LTS Server (vsftpd)
- Setting Up a syslog Server on Ubuntu 26.04 LTS Server (rsyslog)
- Managing Services with systemctl and Reading Logs with journalctl on Ubuntu 26.04 LTS Server
- Configuring Automatic Updates on Ubuntu 26.04 LTS Server (unattended-upgrades)
- Automating the Initial Setup of Ubuntu 26.04 LTS Server with cloud-init
- Configuring Kernel Parameters on Ubuntu 26.04 LTS Server (sysctl)
- Configuring the Network on Ubuntu 26.04 LTS Server (Netplan)
- Configuring Name Resolution on Ubuntu 26.04 LTS Server (systemd-resolved)
- Changing the NTP Source on Ubuntu 26.04 LTS Server (chrony)
- Static Routes on Ubuntu 26.04 LTS Server (Netplan)
- How to Install the Latest neovim from the Official Site on Ubuntu 26.04 LTS Server