Linux Networking for Sysadmins and DevOps Engineers
Practical Linux networking: diagnostics, interface management, firewall rules, SSH power-user tips, and DNS troubleshooting workflows for operations use.
Networking problems are responsible for a disproportionate share of outages and escalations. In over two decades of systems work I have found that most incidents boil down to one of three root causes: a routing or firewall rule that is wrong, a DNS entry that is stale or missing, or a service that is not actually listening on the expected address and port. The tools and workflows on this page address all three — methodically, without guessing.
The legacy ifconfig / route / netstat tools from net-tools are obsolete. Use ip and ss from the iproute2 suite on any modern Linux system.
ip addr show # all interfacesip addr show eth0 # specific interfaceip addr show dev eth0 # same, explicitip -4 addr show # IPv4 onlyip -6 addr show # IPv6 onlyip -br addr show # brief one-line-per-interface format# Add/remove addresssudo ip addr add 192.168.1.10/24 dev eth0sudo ip addr del 192.168.1.10/24 dev eth0
# Statusfirewall-cmd --statefirewall-cmd --list-all # active zone rulesfirewall-cmd --list-all-zones # all zones# Allow a service (permanent = survives reboot)firewall-cmd --permanent --add-service=httpfirewall-cmd --permanent --add-service=httpsfirewall-cmd --reload # apply permanent changes# Allow a specific portfirewall-cmd --permanent --add-port=8080/tcpfirewall-cmd --permanent --remove-port=8080/tcp# Allow a source IPfirewall-cmd --permanent --add-rich-rule='rule family="ipv4" source address="10.0.0.0/8" accept'# Port forwardingfirewall-cmd --permanent --add-forward-port=port=80:proto=tcp:toport=8080# Reload and verifyfirewall-cmd --reloadfirewall-cmd --list-all
# Statusufw status verboseufw status numbered # show rule numbers for deletion# Enable / disableufw enableufw disable# Allow / deny rulesufw allow 22/tcp # SSHufw allow 80/tcp # HTTPufw allow 443 # HTTPS (tcp implied)ufw allow from 10.0.0.0/8 to any port 5432 # PostgreSQL from internalufw deny 23/tcp # block telnetufw delete 3 # delete rule by number# Reset all rulesufw reset
# List all rules with line numbersiptables -L -n -v --line-numbersiptables -L INPUT -n -v --line-numbers# Allow established connections (stateful)iptables -A INPUT -m state --state ESTABLISHED,RELATED -j ACCEPT# Allow SSHiptables -A INPUT -p tcp --dport 22 -j ACCEPT# Allow HTTP/Siptables -A INPUT -p tcp -m multiport --dports 80,443 -j ACCEPT# Block an IPiptables -A INPUT -s 203.0.113.45 -j DROP# Drop everything else (default deny — put this LAST)iptables -A INPUT -j DROP# Delete a rule by numberiptables -L INPUT --line-numbersiptables -D INPUT 5# Save rules (persist across reboots)# RHEL/CentOS:service iptables save# Debian/Ubuntu:iptables-save > /etc/iptables/rules.v4
# List rulesnft list ruleset# Add a simple rule (allow SSH)nft add rule inet filter input tcp dport 22 accept# Example minimal ruleset in /etc/nftables.confcat > /etc/nftables.conf <<'EOF'#!/usr/sbin/nft -fflush rulesettable inet filter { chain input { type filter hook input priority 0; policy drop; iifname lo accept ct state established,related accept tcp dport {22, 80, 443} accept icmp type echo-request accept } chain forward { type filter hook forward priority 0; policy drop; } chain output { type filter hook output priority 0; policy accept; }}EOFsystemctl enable --now nftables
# Generate ED25519 key (preferred over RSA since ~2014)ssh-keygen -t ed25519 -C "valeriy@hostname-$(date +%Y%m%d)" -f ~/.ssh/id_ed25519# Copy public key to remote serverssh-copy-id -i ~/.ssh/id_ed25519.pub user@remote-host# Manual alternative (when ssh-copy-id is unavailable)cat ~/.ssh/id_ed25519.pub | ssh user@remote-host \ "mkdir -p ~/.ssh && cat >> ~/.ssh/authorized_keys && chmod 600 ~/.ssh/authorized_keys"# Start ssh-agent and add keyeval "$(ssh-agent -s)"ssh-add ~/.ssh/id_ed25519ssh-add -l # list loaded keys
Add ControlMaster auto, ControlPath ~/.ssh/cm-%r@%h:%p, and ControlPersist 10m to your ~/.ssh/config to multiplex SSH connections. After the first login, subsequent connections to the same host are near-instant with no re-authentication.
cat /etc/resolv.conf# Look for: nameserver, search, domain directivesresolvectl status # systemd-resolved detailsresolvectl query google.com # query through systemd-resolved
2
Verify basic name resolution
# Does the host resolve at all?dig +short google.com# If that fails, try a public resolver directlydig @8.8.8.8 +short google.comdig @1.1.1.1 +short google.com# If direct resolvers work but /etc/resolv.conf does not,# the problem is local resolver configuration or caching
3
Check for DNS caching issues
# Flush systemd-resolved cacheresolvectl flush-cachessystemd-resolve --flush-caches# Flush nscd cache (if running)nscd -i hosts# Verify TTL on the record (low TTL = propagating change)dig +nocmd +noall +answer +ttl google.com
4
Trace the full delegation chain
# Follow the resolution from root servers downdig +trace example.com# Check all authoritative nameservers agreefor ns in $(dig +short NS example.com); do echo "=== ${ns} ===" dig @"${ns}" example.com A +shortdone
5
Diagnose split-horizon / internal DNS
# Compare internal vs external resolutiondig @10.0.0.53 internal-app.company.com # internal DNSdig @8.8.8.8 internal-app.company.com # external DNS# Check search domain is set correctlycat /etc/resolv.conf | grep search# Test with FQDN (trailing dot forces full lookup)dig internal-app.company.com. # FQDN
6
Inspect /etc/hosts for overrides
grep -v '^#' /etc/hosts | grep -v '^$'# Entries here override DNS — a common source of surprisesgetent hosts hostname # shows effective resolution order
# 1. Check interface is up and has an IPip -br addr show# 2. Check default route existsip route show default# 3. Ping the default gatewayGATEWAY=$(ip route show default | awk '/default/{print $3}')ping -c 3 "${GATEWAY}"# 4. Ping a public IP (bypasses DNS)ping -c 3 8.8.8.8# 5. Test DNS resolutiondig +short google.com# 6. Test HTTP connectivitycurl -sv --max-time 5 https://google.com 2>&1 | head -30# 7. Check local firewalliptables -L OUTPUT -n -vfirewall-cmd --list-all # if using firewalld
Service port not accessible — checklist
# 1. Is the service listening locally?ss -tlnp | grep ':8080'# 2. Is it bound to 0.0.0.0 or only 127.0.0.1?ss -tlnp | grep ':8080'# 127.0.0.1 means not reachable from outside# 3. Test local loopbackcurl -v http://127.0.0.1:8080/health# 4. Test from the server's external IPcurl -v http://$(curl -s checkip.amazonaws.com):8080/health# 5. Check firewalliptables -L INPUT -n -v | grep 8080# 6. Test from a remote hostnc -zv remote-host 8080telnet remote-host 8080