Skip to main content
  1. Linux Articles/
  2. Ubuntu 26.04 LTS Server/

Configuring inetd (the Super-Server) on Ubuntu 26.04 LTS Server

Table of Contents

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 editPurpose
/etc/inetd.confSwitching 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.denyRestricting sources with TCP Wrappers

These implementations are available on Ubuntu 26.04. This article uses inetutils-inetd (the one inetutils-telnetd pulls in).

PackageNotes
inetutils-inetdThe GNU Inetutils version, used here
openbsd-inetdThe OpenBSD version
xinetdThe extended version. Its last upstream release was in 2012, and RHEL 9 dropped it
systemd socket activationDoes the same thing without inetd (compared at the end)

Installing

Installation command
sudo apt install -y inetutils-inetd

Before installing, the configuration files do not exist.

Before installing - example
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=1

tcpd for TCP Wrappers and update-inetd for editing the configuration come with it.

Installation - example
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.

/etc/inetd.conf (right after installing)
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 services

Each line has this format.

Format of one line
<service_name> <sock_type> <proto> <flags> <user> <server_path> <args>
FieldMeaning
service_nameThe service name in /etc/services, which decides the port
sock_typestream (TCP) or dgram (UDP)
prototcp / udp / tcp6 / udp6. tcp6 also accepts IPv4 connections
flagsnowait (start per connection) or wait
userThe user to run as
server_pathThe program to start. internal means inetd handles it itself

With no active line at all, inetd does not start.

Service state right after installing - example
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.

File to edit
sudo vi /etc/inetd.conf
Line 19 of /etc/inetd.conf (after the change)
daytime		stream	tcp6	nowait	root	internal
Enabling it - example
kazulog@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.

Connecting from another server - example
kazulog@sv2:~$ timeout 5 nc 192.168.100.10 13 < /dev/null
Sat Sep 12 09:47:04 2026

Adding 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).

File to create
sudo vi /etc/inetd.d/echo
/etc/inetd.d/echo (new file)
# echo サービス(inetd の内蔵サービス)
echo	stream	tcp	nowait	root	internal
After adding it - example
kazulog@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))             
Connecting from another server - example
kazulog@sv2:~$ printf 'hello inetd\n' | timeout 5 nc 192.168.100.10 7

Running 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.

Files to create
sudo vi /etc/inetd.d/hello
sudo vi /usr/local/bin/hello.sh
sudo vi /etc/services
/etc/inetd.d/hello (new file)
# 自作サービス: 10013 番につながると /usr/local/bin/hello.sh を実行する
hello	stream	tcp	nowait	nobody	/usr/sbin/tcpd	/usr/local/bin/hello.sh
/usr/local/bin/hello.sh (new file, make it executable)
#!/bin/sh
echo "hello from inetd on $(hostname)"
Appended to /etc/services
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.

After adding it - example
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))             
Connecting from another server - example
kazulog@sv2:~$ timeout 5 nc 192.168.100.10 10013 < /dev/null
hello from inetd on sv1

Restricting 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.

Writing 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.
Files to edit
sudo vi /etc/hosts.allow
sudo vi /etc/hosts.deny
Appended to /etc/hosts.allow
# SSH は必ず許可する(締め出し防止)
sshd: ALL
Appended to /etc/hosts.deny
# 自作サービスは 192.168.100.20 からの接続を拒否する
hello.sh: 192.168.100.20

The 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.

Both files and tcpdmatch - example
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:   denied

The refused side connects but receives nothing.

On the refused side - example
kazulog@sv2:~$ timeout 5 nc 192.168.100.10 10013 < /dev/null; echo "nc rc=$?"
nc rc=0
The refusal in the log - example
kazulog@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.

Files to create
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.socket
The two units and the listener - example
kazulog@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.

Connecting from another server - example
kazulog@sv2:~$ timeout 5 nc 192.168.100.10 10014 < /dev/null; echo "nc rc=$?"
hello from inetd on sv1
nc rc=0
Iteminetdsystemd socket activation
What listensinetutils-inetdsystemd (pid=1)
Configuration/etc/inetd.conf / /etc/inetd.d/.socket and .service units
TCP WrappersWorks if server_path is tcpdNot applied (use IPAddressDeny= and similar instead)

Final state of the configuration files

This is the state with everything above applied. Six files were touched.

inetd configuration files
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 を実行する
TCP Wrappers and service state
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))              
FileChange
/etc/inetd.confRemove the # from line 19 (daytime)
/etc/inetd.d/echoNew file (built-in service echo)
/etc/inetd.d/helloNew file (custom service, via tcpd)
/usr/local/bin/hello.shNew file (the custom service itself)
/etc/servicesAppend hello 10013/tcp
/etc/hosts.allow / /etc/hosts.denysshd: ALL and hello.sh: 192.168.100.20
/etc/systemd/system/hello-socket.socket / hello-socket@.serviceNew files (for the comparison)

Removing

Removal command
sudo apt purge -y inetutils-inetd

If 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.

Removal - example
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=1

Test 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.

Test topology
  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 ------------+
Stepsv1sv2
Initial state (just wiped)show / conf / log
Installing inetutils-inetdshow / conf / log
Enabling daytimeshow / conf / log
Connecting to daytimeshow / conf / log
Adding echo and connectingshow / conf / logshow / conf / log
Adding the custom service and connectingshow / conf / logshow / conf / log
Restricting sourcesshow / conf / logshow / conf / log
The refusal in the logshow / conf / log
systemd socket activationshow / conf / logshow / conf / log
Final state of the filesshow / conf / log
Removingshow / conf / log

Reference

GNU Inetutils manual

Related articles

Ubuntu official pages