Bash Networking
Use curl, wget, nc, ssh, scp, rsync, and port checks in Bash scripts for automation and DevOps tasks.
curl — HTTP Requests
curl is the standard tool for making HTTP requests in Bash scripts. It is available on every Linux server and macOS, supports all common protocols, and has flags for every scenario you will encounter in automation — authentication, custom headers, timeouts, retries, and response code checking. Always use -s (silent) in scripts to suppress the progress bar.
# Basic GET request
curl https://api.example.com/users
# Silent mode — no progress bar (essential in scripts)
curl -s https://api.example.com/users
# Save response to a file
curl -so output.json https://api.example.com/data
# Follow redirects automatically
curl -sL https://example.com
# With an Authorization header
curl -sH "Authorization: Bearer $TOKEN" https://api.example.com/me
# POST with a JSON body
curl -s -X POST \
-H "Content-Type: application/json" \
-d '{"name":"Alice","role":"admin"}' \
https://api.example.com/users
# POST with a JSON body built safely from variables (use jq — no manual escaping)
payload=$(jq -n --arg name "$USERNAME" --arg role "admin" \
'{name: $name, role: $role}')
curl -s -X POST -H "Content-Type: application/json" \
-d "$payload" https://api.example.com/users
# Get only the HTTP status code
status=$(curl -so /dev/null -w "%{http_code}" https://example.com)
echo "Status: $status"
# Fail and return non-zero exit code on HTTP 4xx/5xx errors
curl -sf https://api.example.com/health || echo "Health check failed"
# Set connection and total timeouts
curl -s --connect-timeout 5 --max-time 30 https://api.example.com/data
# Basic auth
curl -su "user:password" https://api.example.com/protected
Robust curl wrapper for scripts
A reusable wrapper that handles errors uniformly and extracts both the response body and the status code:
api_request() {
local method="$1"
local url="$2"
local data="${3:-}"
local response
local status
# -w appends the HTTP status code on a new line after the body
response=$(curl -s -w "\n%{http_code}" \
-X "$method" \
-H "Authorization: Bearer $API_TOKEN" \
-H "Content-Type: application/json" \
--connect-timeout 5 \
--max-time 30 \
${data:+-d "$data"} \
"$url")
status=$(tail -1 <<< "$response")
body=$(head -n -1 <<< "$response")
if [[ "$status" -ge 400 ]]; then
echo "ERROR: HTTP $status from $url" >&2
echo "$body" >&2
return 1
fi
echo "$body"
}
users=$(api_request GET "https://api.example.com/users")
result=$(api_request POST "https://api.example.com/users" '{"name":"Bob"}')
wget — File Downloads
wget excels at downloading files, especially when you need retry logic or recursive site mirroring. For API calls and pipeline use, prefer curl; for downloading release archives and following mirrors, wget is often simpler.
# Download a file to the current directory
wget https://example.com/file.tar.gz
# Download quietly (no progress output)
wget -q https://example.com/file.tar.gz
# Download to a specific filename
wget -qO /tmp/app.tar.gz https://example.com/app-latest.tar.gz
# Download with automatic retry on connection failure
wget -q --retry-connrefused --tries=5 https://example.com/file.tar.gz
# Check if a URL is reachable without downloading
wget -q --spider https://example.com && echo "Reachable"
nc (netcat) — Port Checks and Raw TCP
nc (netcat) is the Swiss army knife of TCP/UDP networking. In scripts, its most common use is checking whether a port is open — useful for waiting until a service is ready before proceeding with a deploy or test.
# Check if a TCP port is open (3-second timeout)
nc -zw3 localhost 5432 && echo "PostgreSQL is reachable"
# Check multiple ports on the same host
for port in 80 443 8080; do
nc -zw2 example.com "$port" \
&& echo "Port $port: OPEN" \
|| echo "Port $port: CLOSED"
done
# Send a one-shot message to a TCP listener
echo "Hello" | nc -w1 192.168.1.10 9000
# Grab a service banner (useful for verifying SSH or SMTP)
nc -w3 example.com 22 # shows the SSH version banner
Port check without nc (using /dev/tcp)
Bash’s /dev/tcp pseudo-device lets you check TCP connectivity even when nc is not installed:
check_port() {
local host="$1"
local port="$2"
# Attempt to open a TCP connection — success means the port is open
if (echo > /dev/tcp/"$host"/"$port") 2>/dev/null; then
return 0 # port is open
else
return 1 # port is closed or unreachable
fi
}
# Wait until a service is ready — useful in deploy scripts and Docker entrypoints
wait_for_port() {
local host="$1"
local port="$2"
local max_wait="${3:-60}"
local waited=0
echo "Waiting for $host:$port..."
until check_port "$host" "$port"; do
if (( waited >= max_wait )); then
echo "Timeout waiting for $host:$port" >&2
return 1
fi
sleep 2
(( waited += 2 ))
done
echo "$host:$port is ready (waited ${waited}s)"
}
wait_for_port localhost 5432 60 # wait up to 60s for PostgreSQL
SSH — Remote Command Execution
SSH is the standard way to execute commands on remote servers from scripts. Using SSH keys instead of passwords is essential for automation — password-based SSH requires interactive input that scripts cannot provide.
# Run a single command on a remote server
ssh user@server "uptime"
# Run with a specific SSH key
ssh -i ~/.ssh/deploy_key user@server "systemctl restart myapp"
# Run multiple commands — use a quoted heredoc to prevent local variable expansion
ssh user@server <<'EOF'
cd /var/www/myapp
git pull origin main
npm install --production
pm2 restart myapp
EOF
# Skip host key checking in CI environments — use carefully
ssh -o StrictHostKeyChecking=no user@server "echo connected"
# SSH port forwarding — tunnel local port 8080 to remote port 80
ssh -L 8080:localhost:80 user@server -N -f
# Test SSH connectivity in a script
if ssh -o BatchMode=yes -o ConnectTimeout=5 user@server "exit" 2>/dev/null; then
echo "SSH to server is working"
else
echo "SSH to server failed" >&2
fi
Deploy script using SSH
deploy_to_server() {
local server="$1"
local app_dir="/var/www/myapp"
echo "Deploying to $server..."
# Note: variables from the local shell expand here; use \$ for remote variables
ssh -o StrictHostKeyChecking=no "deploy@${server}" bash <<EOF
set -euo pipefail
cd $app_dir
git fetch origin main
git reset --hard origin/main
npm ci --production
systemctl restart myapp
echo "Deploy complete on \$(hostname)"
EOF
}
scp — Secure File Copy
scp copies files between local and remote hosts over SSH. For simple one-off transfers it is convenient; for large or frequent transfers, prefer rsync which can resume interrupted transfers and skip unchanged files.
# Copy a local file to a remote host
scp app.tar.gz user@server:/tmp/
# Copy a remote file to the local machine
scp user@server:/var/log/app.log ./
# Copy a directory recursively
scp -r ./dist/ user@server:/var/www/html/
# Specify a non-standard SSH port
scp -P 2222 file.txt user@server:/tmp/
rsync — Efficient File Sync
rsync is the right tool for syncing directories, especially over slow or unreliable connections. It only transfers files that have changed (based on size and modification time), compresses data in transit, and can resume interrupted transfers. It is the standard tool for deployment and backup.
# Sync a local directory to a remote host (archive mode: preserve perms, times, symlinks)
rsync -avz ./dist/ user@server:/var/www/html/
# Dry run — see exactly what would change without making any changes
rsync -avzn ./dist/ user@server:/var/www/html/
# Delete files on the destination that no longer exist locally
rsync -avz --delete ./dist/ user@server:/var/www/html/
# Exclude files and directories
rsync -avz --exclude='*.log' --exclude='.git' ./ user@server:/app/
# Backup with a per-day archive of changed files
rsync -avz --backup --backup-dir="/backup/$(date +%Y%m%d)" \
/var/data/ user@backup-server:/backups/current/
# Sync using a specific SSH key
rsync -avz -e "ssh -i ~/.ssh/deploy_key" ./dist/ user@server:/var/www/
DNS Lookups
DNS lookups are useful for verifying infrastructure, debugging connectivity issues, and checking that DNS records are propagating correctly after a change.
# Forward lookup — IP address for a hostname
dig +short example.com
host example.com
# Reverse lookup — hostname for an IP address
dig +short -x 93.184.216.34
# Check specific record types
dig +short example.com MX # mail exchange records
dig +short example.com TXT # TXT records (SPF, DMARC, verification)
dig +short example.com NS # authoritative nameservers
# Quick lookup using a specific DNS server
nslookup example.com 8.8.8.8 # query Google's DNS directly
What’s Next
The next tutorial covers system information commands — uname, df, du, ps, lsof, netstat, and systemctl.