DEV Community

Alex Chen
Alex Chen

Posted on

Linux Server Essentials: What Every Developer Should Know (2026)

Linux Server Essentials: What Every Developer Should Know (2026)

You deployed your app — now what? Here's everything you need to know to keep a Linux server running smoothly.

First Things to Do on a New Server

# 1. Update everything
sudo apt update && sudo apt upgrade -y

# 2. Create a non-root user (never run as root!)
sudo adduser deploy
sudo usermod -aG sudo deploy

# 3. SSH hardening (CRITICAL!)
sudo nano /etc/ssh/sshd_config
# Change these:
PermitRootLogin no
PasswordAuthentication no   # Key-only auth!
PubkeyAuthentication yes
Port 2222                   # Non-standard port (optional security)
MaxAuthTries 3

sudo systemctl restart sshd
# Before closing this session: test SSH from another terminal!

# 4. Firewall setup
sudo ufw allow 2222/tcp     # Your SSH port
sudo ufw allow 80/tcp       # HTTP
sudo ufw allow 443/tcp      # HTTPS
sudo ufw enable             # Enable firewall

# 5. Automatic security updates
sudo apt install unattended-upgrades
sudo dpkg-reconfigure unattended-upgrades  # Select "Yes"

# 6. Time synchronization (important for logs/certificates!)
sudo timedatectl set-timezone UTC
sudo apt install chrony && sudo systemctl enable chrony

# 7. Fail2Ban (auto-block brute force attackers)
sudo apt install fail2ban
sudo systemctl enable fail2ban
Enter fullscreen mode Exit fullscreen mode

Monitoring: What's Happening on Your Server?

# Quick system overview:
htop                          # Interactive process viewer (q to quit)
free -h                       # Memory usage
df -h                         # Disk space
uptime                        # Load average + uptime
nethogs                       # Network bandwidth by process
iotop                         # Disk I/O by process

# Real-time log monitoring:
tail -f /var/log/syslog       # System logs
tail -f /var/log/nginx/access.log   # Nginx access logs
tail -f /var/log/nginx/error.log    # Nginx error logs
journalctl -u nginx -f        # Systemd service logs (follow mode)

# Resource usage deep dive:
vmstat 1                      # CPU, memory, IO every second
iostat -xz 1                  # Disk I/O stats per device
sar -u 1 10                   # CPU history (10 samples, 1s apart)
mpstat -P ALL 1               # Per-CPU core usage

# Network diagnostics:
ss -tlnp                      # Listening ports + PIDs
netstat -tulpn                # Alternative (older)
ip addr show                  # Network interfaces
curl -I https://localhost     # Test local HTTP response
dig example.com               # DNS lookup
traceroute google.com         # Route tracing
tcpdump -i eth0 port 80       # Capture HTTP traffic (debug only!)

# Find what's using resources:
ps aux --sort=-%mem | head -10   # Top 10 memory consumers
ps aux --sort=-%cpu | head -10   # Top 10 CPU consumers
du -sh /* | sort -rh | head -10   # Largest directories
lsof +L1                        # Find deleted files still holding disk space
Enter fullscreen mode Exit fullscreen mode

User & Permission Management

# User management
sudo useradd -m -s /bin/bash newuser   # Create with home directory
sudo passwd newuser                     # Set password
sudo userdel -r olduser                 # Delete with home directory
sudo usermod -aG docker newuser         # Add to group

# File permissions (the most important concept!)
ls -la file.txt
# Output: -rw-r--r-- 1 user group 4096 Jun 10 date file.txt
#        │││───┘ └─┬┘ └┬┘  └────┬────
#        │││       │     │          filename
#        │││       │     └─ group owner
#        │││       └─ user owner
#        ││└──────── others permissions
#        │└───────── group permissions
#        └────────── user/owner permissions
#
# r = read (4), w = write (2), x = execute (1)
# 7 = rwx, 6 = rw-, 5 = r-x, 4 = r--, etc.

chmod 755 script.sh           # Owner=rwx, Group+Others=rx
chmod 600 secret.key          # Only owner can read/write
chmod 644 public.html         # Owner=rw, Others=ro
chown www-data:www-data /var/www/html  # Change owner+group
chgrp deployers /opt/app      # Change group only

# Special permissions:
chmod +s /usr/bin/sudo       # Setuid bit (run as owner)
chmod +t /tmp                 # Sticky bit (only owner can delete own files)
umask 022                     # Default permission mask for new files

# Sudo management
sudo visudo                   # Edit sudoers safely
# Add specific rules:
# deploy ALL=(ALL) NOPASSWD: /usr/bin/systemctl restart nginx
# deploy ALL=(ALL) NOPASSWD: /usr/bin/docker *
Enter fullscreen mode Exit fullscreen mode

Process Management

# Finding processes
pgrep -fa "node"              # Find node processes with command line
ps aux | grep node            # Filter ps output
pidof nginx                   # Get PID of named process

# Controlling processes
kill PID                      # Graceful stop (SIGTERM)
kill -9 PID                   # Force kill (SIGKILL) — last resort
kill -HUP PID                 # Reload config (SIGHUP)
pkill -f "node server"        # Kill by pattern

# Background jobs
command &                     # Run in background
nohup long-running-job &      # Survives terminal close
disown                         # Detach from shell entirely
Ctrl+Z                         # Suspend current job
bg                             # Resume suspended job in background
fg                             # Bring to foreground
jobs                           # List background jobs

# Systemd services (modern way to manage daemons)
systemctl status nginx        # Service status
systemctl start nginx         # Start
systemctl stop nginx          # Stop
systemctl restart nginx       # Restart
systemctl reload nginx        # Reload config (no downtime!)
systemctl enable nginx        # Start on boot
systemctl disable nginx       # Don't start on boot
journalctl -u nginx -n 50     # Last 50 log lines
journalctl -u nginx --since "2026-06-10"  # Logs since date

# Creating your own systemd service:
# /etc/systemd/system/myapp.service
#[Unit]
#Description=My Node.js App
#After=network.target
#
#[Service]
#Type=simple
#User=deploy
#WorkingDirectory=/home/deploy/app
#ExecStart=/usr/bin/node server.js
#Restart=always
#RestartSec=10
#Environment=NODE_ENV=production
#Environment=PORT=3000
#
#[Install]
#WantedBy=multi-user.target
#
# Then: sudo systemctl daemon-reload && sudo systemctl enable myapp
Enter fullscreen mode Exit fullscreen mode

Disk Management

# Usage overview
df -h                         # Human-readable disk usage
du -sh *                      # Directory sizes in current location
du -sh /home/deploy/* | sort -rh  # Sorted by size

# Find large files
find /var/log -type f -size +100M   # Files > 100MB
find /tmp -type f -mtime +7 -delete # Delete files older than 7 days
find . -type f -name "*.log" -exec ls -lh {} \;  # List all log files with sizes

# Clean up space
sudo journalctl --vacuum-size=500M  # Limit journal logs
sudo apt autoremove              # Remove unused packages
docker system prune -a            # Docker cleanup
npm cache clean --force           # npm cache

# Monitor disk I/O
iostat -xz 1                    # Continuous I/O stats
iotop                           # Per-process I/O

# LVM (if using logical volumes):
lvdisplay                      # Show logical volumes
lvextend -l +100%FREE /dev/mapper/vg-root  # Extend volume
resize2fs /dev/mapper/vg-root   # Resize filesystem (no data loss!)
Enter fullscreen mode Exit fullscreen mode

Networking Essentials

# Interface info
ip addr show                   # All interfaces and IPs
ip link show                   # Link status (up/down)
ip route show                  # Routing table

# DNS resolution
/etc/resolv.conf              # DNS servers
dig @8.8.8.8 example.com      # Query specific DNS server
nslookup example.com           # Simple lookup

# Port checking
ss -tlnp | grep :3000         # Is port 3000 listening?
nc -zv localhost 3000          # Test if port responds
curl -v http://localhost:3000  # Full HTTP request/response

# Firewall (ufw)
sudo ufw status verbose       # Current rules
sudo ufw allow from 192.168.1.0/24  # Allow IP range
sudo ufw deny in 22            # Block port
sudo ufw logging on           # Enable logging

# SSH tunneling (secure remote access)
ssh -L 8080:localhost:3000 user@server  # Forward local 8080 → remote 3000
ssh -R 3000:localhost:8080 user@server  # Forward remote 3000 → local 8080
ssh -D 1080 user@server        # SOCKS proxy through SSH

# SSL/TLS certificates (Let's Encrypt)
sudo certbot --nginx -d example.com -d www.example.com
sudo certbot renew --dry-run   # Test renewal
sudo certbot certificates      # List installed certs
Enter fullscreen mode Exit fullscreen mode

Automation & Backups

# Cron jobs (scheduled tasks)
crontab -e                    # Edit cron table
# Format: minute hour day-of-month month day-of-week command
# Examples:
# 0 2 * * * /usr/local/bin/backup.sh          # Daily at 2 AM
# */15 * * * * /usr/local/bin/healthcheck.sh   # Every 15 minutes
# 0 0 * * 0 find /tmp -type f -mtime +7 -delete  # Weekly Sunday midnight cleanup

# Backup strategy:
#!/bin/bash
# backup.sh — Simple incremental backup
BACKUP_DIR="/backups"
DATE=$(date +%Y%m%d_%H%M%S)
SOURCE="/home/deploy/app"

# Database backup
pg_dump $DATABASE_URL > "$BACKUP_DIR/db_$DATE.sql"

# Application files (tar.gz with compression)
tar -czf "$BACKUP_DIR/app_$DATE.tar.gz" $SOURCE

# Keep only last 7 days of backups
find "$BACKUP_DIR" -mtime +7 -delete

echo "Backup completed: $DATE"

# Make it executable: chmod +x backup.sh
# Schedule: crontab -e → 0 3 * * * /path/to/backup.sh >> /var/log/backup.log 2>&1
Enter fullscreen mode Exit fullscreen mode

What's the first thing you do when you get a new server? What did I miss?

Follow @armorbreak for more practical developer guides.

Top comments (0)