Key Takeaways
ipnotifconfig:ip addr showreplacedifconfig.ip route showreplacedroute.ip link set eth0 upreplacedifconfig eth0 up. Thenet-toolspackage (which containedifconfig,netstat) is not installed by default on Ubuntu 24.04 and is considered legacy.ssnotnetstat:ss -tlnpshows all TCP listening ports — it’s faster and more accurate thannetstat -tlnp. The-tmeans TCP,-lmeans listening,-nmeans numeric (no DNS reverse lookup),-pmeans show process.- UFW for humans: UFW translates readable rules (
ufw allow 443/tcp) into iptables rules. Always checksudo ufw status verbosebefore and after changes. The default policy matters:ufw default deny incomingblocks everything not explicitly allowed. - systemd-resolved is the DNS layer: On Ubuntu 24.04, DNS requests go through
systemd-resolved, a local stub resolver on127.0.0.53. Useresolvectl statusto inspect DNS servers, not/etc/resolv.confdirectly.
Introduction
Direct Answer: What are the essential Linux networking commands for Ubuntu 24.04 in 2026?
On Ubuntu 24.04, the five most-used networking commands are: ip addr show (list network interfaces and IP addresses), ss -tlnp (list all TCP listening ports with process names), sudo ufw status verbose (show firewall rules), resolvectl status (show DNS configuration), and ip route show (show routing table and default gateway). To test connectivity: ping -c4 8.8.8.8 (ICMP to internet), curl -sI https://example.com (HTTP layer test), and traceroute example.com (path to host). For the modern Ubuntu 24.04 network stack: ifconfig and netstat are replaced by ip and ss respectively — both are part of the iproute2 package pre-installed on every Ubuntu server. /etc/netplan/ contains network configuration files for persistent settings; edit YAML there and run sudo netplan apply to apply changes without rebooting.
“Networking problems are always one of five things: wrong IP, wrong route, blocked by firewall, DNS failure, or service not listening. Know how to check each one in under 60 seconds and you can debug any network issue.”
Part 1: Network Interfaces with ip addr
# Show all network interfaces and their IP addresses
ip addr show
Expected output (annotated):
1: lo: <LOOPBACK,UP,LOWER_UP> mtu 65536 qdisc noqueue state UNKNOWN
link/loopback 00:00:00:00:00:00 brd 00:00:00:00:00:00
inet 127.0.0.1/8 scope host lo ← Loopback (localhost)
inet6 ::1/128 scope host
2: eth0: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc fq state UP
link/ether 96:00:02:88:0a:1c brd ff:ff:ff:ff:ff:ff ← MAC address
inet 5.161.88.47/32 brd 5.161.88.47 scope global dynamic eth0
↑ Public IPv4 address ↑ CIDR prefix ↑ Scope (global = routable)
inet6 2a01:4ff:1f0:e2a8::1/64 scope global dynamic mngtmpaddr
↑ IPv6 address
Reading the key fields:
UP= interface is enabled;LOWER_UP= physical link is connectedinet 5.161.88.47/32= IPv4 address with CIDR prefix (32 = single host)scope global= externally routable;scope host= localhost only
# Show only IPv4 addresses (clean output)
ip -4 addr show | grep inet | awk '{print $2, $NF}'
Expected output:
127.0.0.1/8 lo
5.161.88.47/32 eth0
# Show one specific interface
ip addr show eth0
# Show interface statistics (bytes sent/received, errors)
ip -s link show eth0
Expected output of ip -s link show eth0 (key lines):
2: eth0: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 ...
RX: bytes packets errors dropped
184729012 234891 0 0 ← Received — errors:0 is healthy
TX: bytes packets errors dropped
28341290 189234 0 0 ← Transmitted — errors:0 is healthy
Part 2: Routing Table with ip route
# Show the routing table
ip route show
Expected output:
default via 172.31.1.1 dev eth0 proto dhcp src 5.161.88.47 metric 100
172.31.1.1 dev eth0 proto dhcp scope link src 5.161.88.47 metric 100
Reading the output:
default via 172.31.1.1= default gateway (all traffic not matching other routes goes here)dev eth0= goes via the eth0 interfaceproto dhcp= this route was set by DHCPmetric 100= route preference (lower = preferred)
# Show which route will be used for a specific destination
ip route get 8.8.8.8
Expected output:
8.8.8.8 via 172.31.1.1 dev eth0 src 5.161.88.47 uid 1000
cache
Traffic to 8.8.8.8 goes via gateway 172.31.1.1 on eth0 from source IP 5.161.88.47.
# Add a static route (temporary — lost on reboot)
sudo ip route add 192.168.10.0/24 via 10.0.0.1
# Delete a route
sudo ip route del 192.168.10.0/24
# For permanent routes, use Netplan (see Part 5)
Part 3: Open Ports with ss
ss (Socket Statistics) replaced netstat. It queries the kernel directly and is significantly faster.
# Show all TCP listening ports with process info
ss -tlnp
Expected output:
State Recv-Q Send-Q Local Address:Port Peer Address:Port Process
LISTEN 0 511 0.0.0.0:80 0.0.0.0:* users:(("nginx",pid=1234,fd=7))
LISTEN 0 511 0.0.0.0:443 0.0.0.0:* users:(("nginx",pid=1234,fd=8))
LISTEN 0 128 127.0.0.1:5432 0.0.0.0:* users:(("postgres",pid=2345,fd=5))
LISTEN 0 128 127.0.0.1:6379 0.0.0.0:* users:(("redis-server",pid=3456,fd=6))
LISTEN 0 128 127.0.0.1:11434 0.0.0.0:* users:(("ollama",pid=4567,fd=9))
LISTEN 0 128 0.0.0.0:22 0.0.0.0:* users:(("sshd",pid=567,fd=3))
Reading the output:
Local Address:Port= where the service is listening0.0.0.0:80= listening on all interfaces (exposed to network) — intentional for Nginx127.0.0.1:5432= listening only on localhost (PostgreSQL — correct, not internet-exposed)users:(("nginx",pid=1234,fd=7))= which process is listening
# Common ss filter flags
ss -tlnp # TCP listening, numeric, with process (most common)
ss -ulnp # UDP listening
ss -tlnp4 # IPv4 only
ss -tlnp6 # IPv6 only
# Show established connections to a specific port
ss -tnp state established '( dport = :443 or sport = :443 )'
# Count connections by state
ss -s
Expected output of ss -s:
Total: 45
TCP: 12 (estab 5, closed 2, orphaned 0, timewait 2)
Transport Total IP IPv6
RAW 0 0 0
UDP 4 2 2
TCP 10 6 4
INET 14 8 6
FRAG 0 0 0
# Check if a specific port is in use
ss -tlnp | grep ':3000' || echo "Port 3000 is free"
Expected output:
Port 3000 is free
Part 4: UFW Firewall
UFW is Ubuntu’s default firewall interface. It writes iptables rules based on human-readable commands.
# Check UFW status
sudo ufw status verbose
Expected output (UFW enabled with typical web server rules):
Status: active
Logging: on (low)
Default: deny (incoming), allow (outgoing), deny (routed)
New profiles: skip
To Action From
-- ------ ----
22/tcp ALLOW IN Anywhere
80/tcp ALLOW IN Anywhere
443/tcp ALLOW IN Anywhere
22/tcp (v6) ALLOW IN Anywhere (v6)
80/tcp (v6) ALLOW IN Anywhere (v6)
443/tcp (v6) ALLOW IN Anywhere (v6)
UFW command reference:
# Enable UFW (always allow SSH first!)
sudo ufw allow ssh # MUST DO THIS FIRST or you'll lock yourself out
sudo ufw enable
# Allow by service name
sudo ufw allow ssh # Port 22 TCP
sudo ufw allow http # Port 80 TCP
sudo ufw allow https # Port 443 TCP
# Allow by port number
sudo ufw allow 3000/tcp
sudo ufw allow 5432/tcp
# Allow from specific IP only (more secure)
sudo ufw allow from 203.0.113.10 to any port 22 # SSH from one IP
sudo ufw allow from 10.0.0.0/8 to any port 5432 # PostgreSQL from private network
# Deny a specific port
sudo ufw deny 8080/tcp
# Delete a rule
sudo ufw delete allow 3000/tcp
sudo ufw delete allow from 203.0.113.10 to any port 22
# Reset all rules (⚠ removes ALL rules)
sudo ufw reset
# Show rules with line numbers (useful for deletion by number)
sudo ufw status numbered
Safe UFW setup for a web server:
# Step 1: Allow SSH before enabling (critical — don't skip)
sudo ufw allow ssh
# Step 2: Allow web traffic
sudo ufw allow http
sudo ufw allow https
# Step 3: Enable with deny-by-default for incoming
sudo ufw default deny incoming
sudo ufw default allow outgoing
sudo ufw enable
# Step 4: Verify
sudo ufw status verbose
Expected output:
Status: active
Default: deny (incoming), allow (outgoing)
To Action From
-- ------ ----
22/tcp ALLOW IN Anywhere
80/tcp ALLOW IN Anywhere
443/tcp ALLOW IN Anywhere
Test firewall from outside (from a different machine):
# From a different machine — test which ports are accessible
nmap -sT -p 22,80,443,3000,5432 YOUR_SERVER_IP
Expected output:
PORT STATE SERVICE
22/tcp open ssh
80/tcp open http
443/tcp open https
3000/tcp filtered ppp ← filtered = blocked by firewall (correct)
5432/tcp filtered postgresql ← filtered = correct, not exposed
Part 5: DNS with systemd-resolved
# Show DNS configuration for all interfaces
resolvectl status
Expected output:
Global
Protocols: -LLMNR -mDNS -DNSOverTLS DNSSEC=no/unsupported
resolv.conf mode: stub
Link 2 (eth0)
Current Scopes: DNS
Protocols: +DefaultRoute +LLMNR -mDNS -DNSOverTLS DNSSEC=no/unsupported
Current DNS Server: 185.12.64.1
DNS Servers: 185.12.64.1 185.12.64.2
DNS Domain: hetzner.cloud
# Test DNS resolution
resolvectl query example.com
Expected output:
example.com: 93.184.216.34 -- link: eth0
2606:2800:220:1:248:1893:25c8:1946
-- Information acquired via protocol DNS in 12.3ms.
-- Data is authenticated: no; Data was acquired via local or encrypted transport: no
-- Data from: network
# Flush DNS cache (useful after updating /etc/hosts or DNS records)
sudo resolvectl flush-caches
echo "DNS cache flushed"
# Check /etc/resolv.conf (it's a symlink on Ubuntu 24.04)
ls -la /etc/resolv.conf
cat /etc/resolv.conf
Expected output:
lrwxrwxrwx 1 root root 39 Feb 1 10:00 /etc/resolv.conf -> ../run/systemd/resolve/stub-resolv.conf
nameserver 127.0.0.53 ← systemd-resolved stub
options edns0 trust-ad
search hetzner.cloud
127.0.0.53 is systemd-resolved’s stub listener — all DNS queries go through it first.
Override DNS for a specific domain (split DNS):
# Add to /etc/hosts for local override (highest priority, instant)
echo "192.168.1.50 api.internal.company.com" | sudo tee -a /etc/hosts
# Test that the override works
resolvectl query api.internal.company.com
Expected output:
api.internal.company.com: 192.168.1.50
-- Information acquired via protocol DNS in 0.0ms.
-- Data from: hosts
Data from: hosts confirms the /etc/hosts override was used.
Part 6: Netplan — Persistent Network Configuration
On Ubuntu 24.04 Server, network configuration lives in /etc/netplan/. Changes here persist across reboots.
# Show current Netplan config
cat /etc/netplan/*.yaml
Example output (DHCP, typical cloud server):
network:
version: 2
ethernets:
eth0:
dhcp4: true
dhcp6: true
Configure a static IP:
sudo tee /etc/netplan/10-static.yaml << 'EOF'
network:
version: 2
ethernets:
eth0:
dhcp4: false
addresses:
- 5.161.88.47/32
routes:
- to: default
via: 172.31.1.1
nameservers:
addresses:
- 9.9.9.9 # Quad9 (privacy-focused)
- 1.1.1.1 # Cloudflare
EOF
# Test config before applying (dry run)
sudo netplan try --timeout 30
# If network works after 30s, press Enter to confirm; otherwise it auto-reverts
# Apply permanently
sudo netplan apply
Configure additional IP address (multi-homed):
network:
version: 2
ethernets:
eth0:
dhcp4: true
addresses:
- 192.168.100.1/24 # Additional static IP
Part 7: Connectivity Troubleshooting Workflow
A systematic 5-step workflow that pinpoints any network issue in under 5 minutes:
#!/bin/bash
# network_debug.sh — run this first when network isn't working
TARGET="${1:-8.8.8.8}"
echo "=== NETWORK DIAGNOSTIC — Target: $TARGET ==="
echo ""
echo "STEP 1: Interface status"
ip -4 addr show | grep -E "^[0-9]|inet" | grep -v "127.0.0.1"
echo ""
echo "STEP 2: Default gateway"
ip route show default
echo ""
echo "STEP 3: Gateway reachable?"
GW=$(ip route show default | awk '/default/ {print $3}' | head -1)
if [ -n "$GW" ]; then
ping -c2 -W2 "$GW" > /dev/null 2>&1 && \
echo " ✓ Gateway $GW is reachable" || \
echo " ✗ Gateway $GW is NOT reachable"
else
echo " ✗ No default gateway configured"
fi
echo ""
echo "STEP 4: Internet connectivity (IP level)"
ping -c2 -W2 8.8.8.8 > /dev/null 2>&1 && \
echo " ✓ 8.8.8.8 reachable (internet working at IP level)" || \
echo " ✗ 8.8.8.8 NOT reachable — check firewall or gateway"
echo ""
echo "STEP 5: DNS resolution"
resolvectl query example.com > /dev/null 2>&1 && \
echo " ✓ DNS resolving correctly" || \
echo " ✗ DNS NOT resolving — check /etc/resolv.conf and systemd-resolved"
echo ""
echo "STEP 6: HTTP connectivity"
curl -sI --max-time 5 https://example.com > /dev/null 2>&1 && \
echo " ✓ HTTPS working end-to-end" || \
echo " ✗ HTTPS failing — DNS OK but TCP/TLS may be blocked"
echo ""
echo "STEP 7: Listening services"
echo " Ports currently listening:"
ss -tlnp | awk 'NR>1 {print " " $4 " ← " $NF}' | sort
chmod +x ~/devops-scripts/network_debug.sh
bash ~/devops-scripts/network_debug.sh google.com
Expected output (healthy server):
=== NETWORK DIAGNOSTIC — Target: google.com ===
STEP 1: Interface status
2: eth0: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 ...
inet 5.161.88.47/32 brd 5.161.88.47 scope global dynamic eth0
STEP 2: Default gateway
default via 172.31.1.1 dev eth0 proto dhcp src 5.161.88.47 metric 100
STEP 3: Gateway reachable?
✓ Gateway 172.31.1.1 is reachable
STEP 4: Internet connectivity (IP level)
✓ 8.8.8.8 reachable (internet working at IP level)
STEP 5: DNS resolution
✓ DNS resolving correctly
STEP 6: HTTP connectivity
✓ HTTPS working end-to-end
STEP 7: Listening services
0.0.0.0:22 ← users:(("sshd",pid=567))
0.0.0.0:80 ← users:(("nginx",pid=1234))
0.0.0.0:443 ← users:(("nginx",pid=1234))
127.0.0.1:5432 ← users:(("postgres",pid=2345))
Quick Reference Card
# ── Interfaces ────────────────────────────────────────────────────────────
ip addr show # All interfaces and IPs
ip addr show eth0 # Specific interface
ip -4 addr # IPv4 only
ip link show # Interface state (UP/DOWN)
ip link set eth0 down # Disable interface
ip link set eth0 up # Enable interface
# ── Routing ───────────────────────────────────────────────────────────────
ip route show # Full routing table
ip route get 8.8.8.8 # Route for specific destination
ip route add 10.0.0.0/8 via 192.168.1.1 # Add route
ip route del 10.0.0.0/8 # Remove route
# ── Ports and Connections ─────────────────────────────────────────────────
ss -tlnp # TCP listening ports + process
ss -ulnp # UDP listening ports
ss -tnp state established # Established TCP connections
ss -s # Summary statistics
# ── DNS ───────────────────────────────────────────────────────────────────
resolvectl status # DNS config and active servers
resolvectl query example.com # Test resolution
sudo resolvectl flush-caches # Clear DNS cache
cat /etc/hosts # Local overrides (highest priority)
# ── UFW Firewall ──────────────────────────────────────────────────────────
sudo ufw status verbose # Current rules
sudo ufw allow ssh # Allow SSH (do before enabling)
sudo ufw allow 80/tcp # Allow HTTP
sudo ufw allow from IP to any port 22 # Allow SSH from specific IP
sudo ufw deny 3000/tcp # Block a port
sudo ufw delete allow 3000/tcp # Remove a rule
sudo ufw enable # Activate firewall
sudo ufw disable # Deactivate (keep rules)
# ── Connectivity Testing ──────────────────────────────────────────────────
ping -c4 8.8.8.8 # ICMP test to internet
traceroute 8.8.8.8 # Path to host (install: apt-get install traceroute)
curl -sI https://example.com # HTTP response headers
curl -s telnet://host:port # Test TCP port connectivity
nmap -sT -p 22,80,443 SERVER # Scan specific ports (install: apt-get install nmap)
Troubleshooting
Network unreachable after Netplan change
Cause: Wrong gateway IP or incorrect subnet mask in Netplan config.
Fix: The netplan try command auto-reverts after 30 seconds if you can’t confirm. Always use sudo netplan try instead of sudo netplan apply for risky changes. If locked out: restore from VPS console access.
Port shows LISTEN but connection refused from external
Cause: Service is listening on 127.0.0.1 (localhost only), not 0.0.0.0 (all interfaces), or UFW is blocking the port.
Fix:
# Check what address the service is bound to
ss -tlnp | grep :3000
# 127.0.0.1:3000 = localhost only → change app config to bind 0.0.0.0
# 0.0.0.0:3000 = all interfaces → check UFW: sudo ufw status | grep 3000
DNS not resolving after /etc/resolv.conf edit
Cause: On Ubuntu 24.04, /etc/resolv.conf is a symlink to systemd-resolved’s stub. Direct edits are overwritten on reboot.
Fix: Configure DNS via Netplan nameservers: section, or configure systemd-resolved directly:
sudo mkdir -p /etc/systemd/resolved.conf.d/
sudo tee /etc/systemd/resolved.conf.d/dns.conf << 'EOF'
[Resolve]
DNS=9.9.9.9 1.1.1.1
FallbackDNS=8.8.8.8
EOF
sudo systemctl restart systemd-resolved
Conclusion
The modern Linux networking toolkit is clear: ip for interfaces and routes, ss for open ports, ufw for firewall rules, resolvectl for DNS, and netplan for persistent configuration. All five are pre-installed on Ubuntu 24.04. The network debugging workflow in Part 7 systematically isolates any connectivity problem in under 5 minutes by testing each network layer from physical link to application.
Connect this knowledge to Ubuntu 24.04 LTS Server Setup Checklist for the full server hardening workflow, or SSH Hardening Guide to secure the most important open port on any server.
People Also Ask
Why does Ubuntu 24.04 not have ifconfig or netstat?
ifconfig and netstat are part of the net-tools package, which has been deprecated in favour of iproute2 since 2009. Ubuntu 24.04 does not install net-tools by default. The replacements — ip (for ifconfig/route) and ss (for netstat) — provide more information, support newer kernel features, and are actively maintained. If you need legacy tools for script compatibility, install them with sudo apt-get install net-tools, but new scripts should use ip and ss.
How do I check which process is using a specific port?
Use ss -tlnp | grep :PORT. For example, ss -tlnp | grep :80 shows what’s listening on port 80. The output includes the process name and PID in the users: column. Alternatively, sudo lsof -i :80 gives a more detailed view including all processes with open connections to that port, not just the listener.
What is the difference between a subnet mask and CIDR notation?
Both express the same concept — how many bits of an IP address are the network portion. CIDR /24 means the first 24 bits are the network address, equivalent to subnet mask 255.255.255.0. A /24 network has 254 usable host addresses. /32 is a single host (all 32 bits are the host address). /16 is a class B network (65,534 hosts). CIDR is the modern notation and is used throughout ip command output. The old subnet mask notation (255.255.255.0) is still used in some tools and documentation but is equivalent.
Further Reading
- Ubuntu 24.04 LTS Server Setup Checklist — uses these networking commands for initial server hardening
- SSH Hardening Guide 2026 — securing port 22 with UFW and key authentication
- How to Install Nginx on Ubuntu 24.04 LTS — configuring UFW for a web server
- Docker Networking: How Containers Communicate — Docker’s overlay networks build on these primitives
Tested on: Ubuntu 24.04 LTS (Hetzner CX22), Ubuntu 24.04 LTS (Raspberry Pi 5 4GB). iproute2 6.1.0, UFW 0.36.2. Last verified: April 22, 2026.