Linux networking tools let you inspect interfaces, trace routes, transfer files, manage remote servers via SSH, and diagnose connectivity problems. These commands are daily tools for any sysadmin or DevOps engineer.
The ip command (from iproute2 package) replaces the older ifconfig and route commands. Use this on modern systems.
# ip addr — show IP addresses
$ ip addr show # all interfaces
$ ip addr show eth0 # specific interface
$ ip addr show up # only active interfaces
$ ip a # shorthand
# Example output:
2: eth0: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc fq_codel state UP
link/ether 52:54:00:12:34:56 brd ff:ff:ff:ff:ff:ff
inet 192.168.1.100/24 brd 192.168.1.255 scope global dynamic eth0
inet6 fe80::5054:ff:fe12:3456/64 scope link
# Add/remove IP address (as root)
$ ip addr add 192.168.1.200/24 dev eth0
$ ip addr del 192.168.1.200/24 dev eth0
# ip link — network interface control
$ ip link show # show all interfaces
$ ip link set eth0 up # bring interface up
$ ip link set eth0 down # bring interface down
$ ip link set eth0 mtu 9000 # set MTU (jumbo frames)
# ip route — routing table
$ ip route show # show routing table
$ ip route # shorthand
$ ip route show default # show default gateway
$ ip route get 8.8.8.8 # show route to specific IP
# Example routing table:
default via 192.168.1.1 dev eth0 proto dhcp metric 100
192.168.1.0/24 dev eth0 proto kernel scope link src 192.168.1.100
# Add/remove routes
$ ip route add 10.0.0.0/8 via 192.168.1.1 # add static route
$ ip route del 10.0.0.0/8 # remove route
$ ip route add default via 192.168.1.1 # add default gateway
ss is the modern replacement for netstat. It shows socket (connection/port) information.
# ss — socket statistics
$ ss -tulpen # TCP+UDP, listening, process, numeric, extended
# Flags: t=TCP, u=UDP, l=listening, p=process, e=extended, n=numeric (no DNS)
$ ss -an # all sockets, numeric
$ ss -tn # TCP only, numeric
$ ss -tnp # TCP with process names (run as root)
$ ss -tlnp # TCP listening ports only
$ ss -ulnp # UDP listening ports only
$ ss -s # summary statistics
# Example output:
Netid State Recv-Q Send-Q Local Address:Port Peer Address:Port Process
tcp LISTEN 0 128 0.0.0.0:22 0.0.0.0:* users:(("sshd",pid=1234,fd=3))
tcp LISTEN 0 511 0.0.0.0:80 0.0.0.0:* users:(("nginx",pid=5678,fd=6))
tcp ESTAB 0 0 192.168.1.100:22 203.0.113.10:54321 users:(("sshd",pid=9012,fd=4))
# Filter by port
$ ss -tnp | grep :80 # find processes on port 80
$ ss -tnp state established # only established connections
$ ss -tnp state listening # only listening sockets
$ ss -tnp src :80 # connections FROM port 80
$ ss -tnp dst :443 # connections TO port 443
$ ss dst 192.168.1.0/24 # connections to subnet
# Summary
$ ss -s
Total: 623
TCP: 45 (estab 12, closed 20, orphaned 0, timewait 20)
# timewait: connections being gracefully closed
# If ss is not available or for familiarity $ netstat -tulpen # same flags as ss, similar output $ netstat -an | grep :80 # connections on port 80 $ netstat -r # routing table (like ip route)
# Basic ping $ ping google.com # ping continuously (Ctrl+C to stop) $ ping 8.8.8.8 # ping by IP $ ping -c 4 google.com # send exactly 4 packets $ ping -c 4 -i 0.5 google.com # send 4 packets, 0.5 seconds apart $ ping -c 10 -q google.com # quiet mode (only shows summary) $ ping -s 1400 google.com # set packet size (test MTU issues) $ ping -t 64 google.com # set TTL (time to live) $ ping -W 1 google.com # 1 second timeout per packet $ ping6 ::1 # ping IPv6 # Example output: PING google.com (142.250.185.14) 56(84) bytes of data. 64 bytes from lga34s32-in-f14.1e100.net: icmp_seq=1 ttl=118 time=12.4 ms 64 bytes from lga34s32-in-f14.1e100.net: icmp_seq=2 ttl=118 time=11.8 ms --- google.com ping statistics --- 4 packets transmitted, 4 received, 0% packet loss, time 3004ms rtt min/avg/max/mdev = 11.8/12.1/12.4/0.2 ms # TTL explained: # Each router hop decrements TTL by 1 # Starting TTL reveals OS: Linux=64, Windows=128, Cisco=255 # Low TTL means many hops (or restricted TTL)
# traceroute — show each hop to destination $ traceroute google.com # trace route (uses UDP by default) $ traceroute -T google.com # use TCP (better through firewalls) $ traceroute -I google.com # use ICMP $ traceroute -n google.com # don't resolve hostnames (faster) $ traceroute -m 30 google.com # max 30 hops $ traceroute -p 443 google.com # use specific port # tracepath — simpler, no root required $ tracepath google.com $ tracepath -n google.com # no hostname resolution # Example output: 1: 192.168.1.1 1.2ms 2: 10.0.0.1 5.4ms 3: 203.0.113.1 12.3ms 4: * * * # no response (filtered ICMP) 5: 142.250.185.14 15.6ms google.com # * * * means router is not responding to probes (not necessarily broken)
curl is the Swiss Army knife of HTTP. It supports HTTP, HTTPS, FTP, SFTP, and dozens of other protocols.
# Basic usage
$ curl https://example.com # GET request, print response body
$ curl -s https://example.com # silent (no progress meter)
$ curl -o output.html https://example.com # save to file
$ curl -O https://example.com/file.zip # save with original filename
# HTTP methods
$ curl -X GET https://api.example.com/users
$ curl -X POST https://api.example.com/users
$ curl -X PUT https://api.example.com/users/1
$ curl -X DELETE https://api.example.com/users/1
$ curl -X PATCH https://api.example.com/users/1
# Headers
$ curl -I https://example.com # headers only (HEAD request)
$ curl -H "Authorization: Bearer token123" https://api.example.com
$ curl -H "Content-Type: application/json" https://api.example.com
$ curl -A "MyBot/1.0" https://example.com # custom User-Agent
# POST data
$ curl -d "name=alice&age=30" https://api.example.com/users # form data
$ curl -d '{"name":"alice","age":30}' \
-H "Content-Type: application/json" \
https://api.example.com/users # JSON POST
# Follow redirects and auth
$ curl -L https://short.url/xyz # -L: follow redirects
$ curl -u username:password https://api.example.com # basic auth
$ curl -k https://self-signed.example.com # -k: ignore SSL cert errors
# Response details
$ curl -v https://example.com # verbose (shows headers, handshake)
$ curl -w "%{http_code}" -o /dev/null https://example.com # just status code
$ curl -w "\nTime: %{time_total}s\n" -o /dev/null -s https://example.com
# Download with resume
$ curl -C - -O https://example.com/large-file.iso # resume interrupted download
# Multiple URLs
$ curl https://site1.com https://site2.com # fetch both
# Practical API testing
$ curl -s https://api.github.com/users/torvalds | python3 -m json.tool
$ curl -s ifconfig.me # get your public IP address
$ curl -s https://api.ipify.org?format=json # public IP in JSON
# wget — non-interactive file downloader $ wget https://example.com/file.tar.gz # download file $ wget -O output.tar.gz https://example.com/f # custom output filename $ wget -q https://example.com/file # quiet mode $ wget --limit-rate=1m https://example.com/f # limit download speed to 1MB/s $ wget -c https://example.com/large-file.iso # continue partial download $ wget -b https://example.com/large-file.iso # background download # Authentication $ wget --user=alice --password=secret https://protected.example.com/file # Recursive download (mirror a website) $ wget -r https://example.com/docs/ # recursive download $ wget -r -l 2 https://example.com/ # limit to 2 levels deep $ wget -r -k -p https://example.com/ # download with page assets $ wget --mirror https://example.com/ # full mirror # Headers and timeouts $ wget --timeout=30 https://example.com/file # 30 second timeout $ wget --tries=3 https://example.com/file # retry 3 times
# Basic SSH connection
$ ssh user@hostname # connect
$ ssh user@192.168.1.100 # connect by IP
$ ssh -p 2222 user@hostname # custom port
$ ssh -i ~/.ssh/mykey.pem user@hostname # use specific private key
$ ssh -v user@hostname # verbose (debug connection)
$ ssh root@hostname # connect as root (avoid this)
# Execute commands remotely
$ ssh user@hostname "ls -la /var/www" # run single command
$ ssh user@hostname "sudo systemctl status nginx"
$ ssh user@hostname "cat /var/log/nginx/error.log | tail -50"
# Port forwarding
$ ssh -L 8080:localhost:80 user@hostname # local forwarding
# Access localhost:8080 → tunnel → hostname:80
$ ssh -R 9090:localhost:3000 user@hostname # remote forwarding
# hostname:9090 → tunnel → localhost:3000
$ ssh -D 1080 user@hostname # SOCKS5 proxy on port 1080
# Configure browser to use SOCKS5 proxy at 127.0.0.1:1080
# Keep alive options
$ ssh -o ServerAliveInterval=60 user@hostname # send keepalive every 60s
$ ssh -o StrictHostKeyChecking=no user@hostname # don't verify host key
# SSH config file (~/.ssh/config)
Host myserver
HostName 192.168.1.100
User alice
Port 2222
IdentityFile ~/.ssh/myserver_key
ServerAliveInterval 60
ServerAliveCountMax 3
Host bastion
HostName bastion.example.com
User admin
IdentityFile ~/.ssh/bastion_key
Host internal
HostName 10.0.0.50
User admin
ProxyJump bastion # connect through bastion host
# With config, just type:
$ ssh myserver
$ ssh internal # automatically proxies through bastion
# Copy local file to remote $ scp file.txt user@hostname:/home/user/ $ scp file.txt user@hostname:~ # shorthand for home dir $ scp -P 2222 file.txt user@hostname:/tmp/ # custom port (uppercase P!) # Copy remote file to local $ scp user@hostname:/var/log/nginx/access.log . # copy to current dir $ scp user@hostname:~/config.txt ./config.txt # rename while copying # Copy directories $ scp -r ./myproject user@hostname:/opt/ # recursive # Use SSH config aliases $ scp file.txt myserver:/tmp/ # uses ~/.ssh/config settings # Copy between two remote servers $ scp user1@host1:/path/file user2@host2:/path/ # Multiple files $ scp file1.txt file2.txt user@hostname:/tmp/ $ scp *.log user@hostname:/var/backup/logs/
rsync only transfers changed parts of files — much faster than scp for repeated synchronization tasks.
# Basic syntax: rsync [options] source destination # Local sync $ rsync -av /source/dir/ /backup/dir/ # sync directories $ rsync -av /source/dir/ /backup/dir/ # trailing slash matters! # /source/dir/ → contents of dir # /source/dir → the dir itself # Remote sync $ rsync -avz /local/dir/ user@hostname:/remote/dir/ # push to remote $ rsync -avz user@hostname:/remote/dir/ /local/dir/ # pull from remote # Common flags # -a archive mode: recursive, preserves perms, timestamps, symlinks, owner # -v verbose # -z compress during transfer # -P show progress + allow resume (= --progress --partial) # -n dry run (show what WOULD be transferred, don't actually do it) # --delete delete files in dest that don't exist in source # --exclude exclude files/dirs matching pattern # --exclude-from read exclude patterns from file # --checksum compare by checksum instead of size+time (slower, accurate) # --bwlimit=1024 limit bandwidth to 1 MB/s # Useful combinations $ rsync -avzP --delete /local/ user@server:/backup/ # full mirror $ rsync -avz --exclude='*.log' --exclude='.git' /src/ /dst/ # exclude files $ rsync -avzn /source/ user@server:/dest/ # dry run first # Sync using SSH with custom port $ rsync -avz -e "ssh -p 2222" /local/ user@server:/remote/ # Create timestamped backups $ rsync -av --backup --backup-dir=/backup/$(date +%Y%m%d) /source/ /dest/ # Monitor progress on large transfers $ rsync -avP --stats /large_dir/ user@server:/backup/
# dig — DNS lookup (most powerful) $ dig google.com # A record (IPv4 address) $ dig google.com A # explicit A record query $ dig google.com AAAA # IPv6 address $ dig google.com MX # mail exchange records $ dig google.com NS # name servers $ dig google.com TXT # TXT records (SPF, DKIM, etc.) $ dig google.com SOA # start of authority $ dig google.com CNAME # canonical name $ dig -x 8.8.8.8 # reverse DNS lookup (PTR record) # Use specific DNS server $ dig @8.8.8.8 google.com # query Google DNS $ dig @1.1.1.1 google.com # query Cloudflare DNS $ dig @192.168.1.1 google.com # query local DNS server # dig output options $ dig +short google.com # just the IP address $ dig +noall +answer google.com # only show answer section $ dig +trace google.com # trace full resolution path # nslookup — simpler DNS tool $ nslookup google.com # basic lookup $ nslookup google.com 8.8.8.8 # use specific server $ nslookup -type=mx google.com # MX records # host — simple DNS lookup $ host google.com # forward lookup $ host 8.8.8.8 # reverse lookup $ host -t mx google.com # MX records $ host -t ns google.com # name servers # whois $ whois google.com # domain registration info $ whois 8.8.8.8 # IP ownership info
# nmap — network exploration and security auditing # Install: sudo apt install nmap # Host discovery (ping scan only) $ nmap -sn 192.168.1.0/24 # find live hosts on subnet $ nmap -sn 192.168.1.1-50 # range of IPs # Port scanning $ nmap 192.168.1.100 # scan top 1000 common ports $ nmap -p 80,443,8080 192.168.1.100 # specific ports $ nmap -p 1-1024 192.168.1.100 # port range 1-1024 $ nmap -p- 192.168.1.100 # ALL 65535 ports (slow) $ nmap --open 192.168.1.100 # only show open ports # Service and version detection $ nmap -sV 192.168.1.100 # detect service versions $ nmap -O 192.168.1.100 # OS detection (needs root) $ nmap -A 192.168.1.100 # aggressive: OS + version + scripts + traceroute # Scan types $ nmap -sT 192.168.1.100 # TCP connect scan (default, no root needed) $ nmap -sS 192.168.1.100 # TCP SYN scan (stealth, needs root) $ nmap -sU 192.168.1.100 # UDP scan (slow) # Output $ nmap -oN scan.txt 192.168.1.100 # save normal output $ nmap -oX scan.xml 192.168.1.100 # save XML output $ nmap -oG scan.grep 192.168.1.100 # grepable output
# /etc/hosts — local DNS override (checked before DNS)
$ cat /etc/hosts
127.0.0.1 localhost
127.0.1.1 myserver.local myserver
192.168.1.100 dbserver.local dbserver
10.0.0.50 redis.internal
# Add an entry
$ echo "192.168.1.200 testserver" | sudo tee -a /etc/hosts
# /etc/resolv.conf — DNS resolver configuration
$ cat /etc/resolv.conf
nameserver 8.8.8.8 # primary DNS server
nameserver 1.1.1.1 # secondary DNS server
search example.com local # domain search list
options ndots:5 # dots needed before adding search domains
# /etc/nsswitch.conf — name service switch
# Defines order: hosts, files, dns
hosts: files dns # check /etc/hosts first, then DNS
# On Debian/Ubuntu: NetworkManager or netplan
# /etc/netplan/*.yaml (Ubuntu 18.04+)
$ cat /etc/netplan/00-installer-config.yaml
network:
version: 2
ethernets:
eth0:
dhcp4: true
eth1:
addresses: [192.168.1.100/24]
gateway4: 192.168.1.1
nameservers:
addresses: [8.8.8.8, 1.1.1.1]
$ sudo netplan apply # apply changes
# On CentOS/RHEL: /etc/sysconfig/network-scripts/
$ cat /etc/sysconfig/network-scripts/ifcfg-eth0
DEVICE=eth0
BOOTPROTO=static
IPADDR=192.168.1.100
NETMASK=255.255.255.0
GATEWAY=192.168.1.1
DNS1=8.8.8.8
# Check what's running on a port
$ ss -tlnp | grep :80
$ lsof -i :80
# Test if a port is open remotely
$ nc -zv 192.168.1.100 80 # netcat: -z=scan, -v=verbose
$ nc -zv -w 3 hostname 443 # 3 second timeout
$ curl -v telnet://hostname:25 # test SMTP port
# Quick HTTP test
$ curl -I http://192.168.1.100 # check HTTP headers
$ curl -w "%{http_code}" -o /dev/null -s http://hostname # just status
# Check DNS resolution speed
$ time dig google.com A +short # measure DNS query time
# Get your public IP
$ curl ifconfig.me
$ curl -s https://api.ipify.org
$ dig +short myip.opendns.com @resolver1.opendns.com
# Test bandwidth
$ curl -o /dev/null http://speedtest.example.com/100mb-file.bin # download test
# Quick port scan of your own server
$ nmap -sT -p 22,80,443,3306,5432 localhost
# Monitor network traffic in real time
$ iftop -n # install: apt install iftop
$ nethogs # per-process bandwidth: apt install nethogs
$ watch -n 1 "ss -s" # watch socket summary every second