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)

Sooner or later, you'll need to manage a server. Whether it's a VPS, cloud instance, or CI runner — here's what you actually need to know.

First Things First: Connect & Secure

# === SSH Connection ===
# Basic:
ssh user@your-server-ip

# With key (recommended):
ssh -i ~/.ssh/mykey.pem user@your-server-ip

# SSH config (~/.ssh/config) — save typing every time:
Host myserver
    HostName 43.135.172.134
    User root
    IdentityFile ~/.ssh/id_ed25519
    Port 22
    ServerAliveInterval 60   # Keep connection alive
# Now just: ssh myserver

# === IMMEDIATE Security Steps (do this on EVERY new server!) ===
# 1. Change root password:
passwd

# 2. Create non-root user (never run as root!):
adduser deploy
usermod -aG sudo deploy      # Add to sudo group

# 3. Set up SSH key auth (disable password login):
# On your LOCAL machine:
ssh-copy-id deploy@myserver   # Copies your public key to server

# On the server, edit SSH config:
sudo nano /etc/ssh/sshd_config
# Change these:
PermitRootLogin no           # No direct root login
PasswordAuthentication no    # Only key-based auth
PubkeyAuthentication yes
Port 2222                   # Optional: change from default 22

# Restart SSH:
sudo systemctl restart sshd
# ⚠️ Don't close your current session until you've tested the new one!

# 4. Enable firewall:
sudo ufw allow 2222/tcp       # Your custom SSH port
sudo ufw allow 80/tcp         # HTTP
sudo ufw allow 443/tcp        # HTTPS
sudo ufw enable              # Activate firewall
sudo ufw status verbose      # Check rules

# 5. Automatic security updates:
sudo apt install unattended-upgrades
sudo dpkg-reconfigure --priority=low unattended-upgrades
Enter fullscreen mode Exit fullscreen mode

Monitoring Your Server

# === Resource Usage (first commands to run!) ===
# Quick overview:
htop                          # Interactive process viewer (install first)
# Or built-in alternative:
top                           # Press '1' to see per-CPU usage

# Memory:
free -h                       # RAM usage (human-readable)
vmstat 1 5                    # Memory stats every 1 second, 5 times
cat /proc/meminfo            # Detailed memory info

# Disk:
df -h                         # Filesystem usage (human-readable)
du -sh /var/log/*             # Directory sizes
ncdu /                        # Interactive disk usage browser (great for finding big files)

# Network:
ss -tlnp                     # Listening ports + PIDs (modern netstat)
nethogs                       # Real-time bandwidth per process
iftop                         # Network interface traffic

# System load:
uptime                        # Load averages (1min, 5min, 15min)
# Rule of thumb: keep under # of CPU cores on each average
# 4 cores → 4.0 is max sustainable; >4.0 means overloaded

# Logging:
journalctl -u nginx -f        # Follow nginx service logs live
tail -f /var/log/syslog       # Follow system log
dmesg | tail -20              # Recent kernel messages
Enter fullscreen mode Exit fullscreen mode

Process Management

# === Finding processes ===
ps aux | grep node            # Find all Node.js processes
pgrep -a node                 # Same, cleaner output
pidof node                    # Just PIDs

# === Managing processes ===
kill PID                      # Graceful stop (SIGTERM)
kill -9 PID                  # Force kill (SIGKILL) — last resort!
pkill -f "node server"       # Kill by name pattern
killall node                 # Kill ALL processes named node

# Process states (from ps):
# R = Running (on CPU)
# S = Sleeping (waiting for I/O/event)
# D = Uninterruptible sleep (usually I/O wait)
# Z = Zombie (dead but parent hasn't reaped it!)
# T = Stopped (by job control signal)

# Zombie processes (should be rare, fix if many):
ps aux | awk '$8 ~ /Z/'     # List zombies
# Fix: kill the parent process (PPID column)

# Background jobs:
npm start &                   # Run in background
jobs                          # List background jobs
fg %1                         # Bring job 1 to foreground
Ctrl+Z                        # Suspend current job
bg %1                         # Resume suspended in background
nohup npm start &             # Survives terminal disconnection
Enter fullscreen mode Exit fullscreen mode

Service Management (systemd)

# === Essential Commands ===
systemctl status nginx        # Check service status
systemctl start nginx         # Start service
systemctl stop nginx          # Stop service
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

# View logs:
journalctl -u nginx           # All logs for this service
journalctl -u nginx -f        # Follow logs live
journalctl -u nginx --since "2026-06-01"  # Logs since date
journalctl -u nginx -p err    // Only error-level logs

# Failed services:
systemctl --failed            # List services that failed

# === Creating Your Own systemd Service ===
# /etc/systemd/system/myapp.service:
[Unit]
Description=My Node.js Application
After=network.target postgresql.service

[Service]
Type=simple
User=deploy
WorkingDirectory=/home/deploy/app
ExecStart=/home/deploy/.nvm/v22.0.0/bin/node server.js
Restart=always               # Auto-restart on crash
RestartSec=10                # Wait 10s between restarts
Environment=NODE_ENV=production
Environment=PORT=3000
Environment=DATABASE_URL=postgresql://...
StandardOutput=journal
StandardError=journal
SyslogIdentifier=myapp

[Install]
WantedBy=multi-user.target

# After creating:
sudo systemctl daemon-reload   # Reload systemd configs
sudo systemctl enable myapp     # Enable auto-start
sudo systemctl start myapp      # Start it!
sudo journalctl -u myapp -f     # Watch logs
Enter fullscreen mode Exit fullscreen mode

Nginx: The Web Server You Need

# Install:
sudo apt install nginx

# Key directories:
/etc/nginx/nginx.conf          # Main config
/etc/nginx/sites-available/    # Site configs (stored here)
/etc/nginx/sites-enabled/      # Active sites (symlinks to available)
/var/log/nginx/                # Access + error logs
/usr/share/nginx/html/         # Default static files

# === Basic Site Configuration ===
# /etc/nginx/sites-available/myapp.conf:
server {
    listen 80;
    server_name example.com www.example.com;

    # Redirect HTTP → HTTPS:
    return 301 https://$server_name$request_uri;
}

server {
    listen 443 ssl http2;
    server_name example.com;

    ssl_certificate     /etc/letsencrypt/live/example.com/fullchain.pem;
    ssl_certificate_key /etc/letsencrypt/live/example.com/privkey.pem;

    # Reverse proxy to your app:
    location / {
        proxy_pass http://127.0.0.1:3000;
        proxy_http_version 1.1;

        # Required headers for WebSocket and proper forwarding:
        proxy_set_header Host $host;
        proxy_set_header X-Real-IP $remote_addr;
        proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
        proxy_set_header X-Forwarded-Proto $scheme;

        # Timeouts:
        proxy_connect_timeout 60s;
        proxy_send_timeout 60s;
        proxy_read_timeout 60s;
    }

    # Static files (serve directly, faster than through Node):
    location /static/ {
        alias /home/deploy/app/public/;
        expires 30d;           # Cache for 30 days
        add_header Cache-Control "public, immutable";
    }

    # Rate limiting:
    limit_req_zone $binary_remote_addr zone=api_limit:10m rate=10r/s;

    location /api/ {
        limit_req zone=api_limit burst=20 nodelay;
        proxy_pass http://127.0.0.1:3000;
        # ... same headers as above ...
    }
}

# Activate site:
sudo ln -s /etc/nginx/sites-available/myapp.conf /etc/nginx/sites-enabled/
sudo nginx -t                    # Test configuration (always do before restart!)
sudo systemctl reload nginx      # Apply changes without dropping connections!

# SSL with Let's Encrypt (free HTTPS):
sudo apt install certbot python3-certbot-nginx
sudo certbot --nginx -d example.com -d www.example.com
# Auto-renews automatically!
Enter fullscreen mode Exit fullscreen mode

Automation & Maintenance

# === Cron Jobs (Scheduled Tasks) ===
crontab -e                     # Edit your cron jobs

# Format: minute hour day-of-month month day-of-week command
# Examples:
0 3 * * * /usr/bin/docker system prune -af >> /var/log/cleanup.log 2>&1  # Daily 3 AM cleanup
*/15 * * * * /home/deploy/scripts/healthcheck.sh                            # Every 15 minutes
0 6 * * 1 /usr/bin/pg_dump -U app mydb > /backups/db_$(date +\%Y\%m\%d).sql  # Weekly Monday backup

# === Useful One-Liners ===
# Find large files (>100MB):
find / -type f -size +100M 2>/dev/null | head -20

# Find recently modified files (last 7 days):
find /var/www -type f -mtime -7

# Monitor real-time file access:
inotifywait -m -r /var/www/html

# Port check (is something listening?):
ss -tlnp | grep :3000

# Connection count per IP (detect attacks):
netstat -an | grep ':80' | awk '{print $5}' | cut -d: -f1 | sort | uniq -c | sort -rn | head -10

# Top CPU consumers:
ps aux --sort=-%cpu | head -11

# Free memory aggressively (sync before!):
sync && echo 3 > /proc/sys/vm/drop_caches

# System info summary:
echo "=== OS ===" && cat /etc/os-release && echo "=== CPU ===" && nproc && \
echo "=== RAM ===" && free -h | grep Mem && echo "=== Disk ===" && df -h / && \
echo "=== Uptime ===" && uptime

# === Backup Strategy ===
#!/bin/bash
# Simple backup script (save as /home/deploy/scripts/backup.sh)
BACKUP_DIR="/backups"
DATE=$(date +%Y%m%d_%H%M%S)

# Database backup
pg_dump -U app mydb > "$BACKUP_DIR/db_$DATE.sql"

# Application files
tar -czf "$BACKUP_DIR/app_$DATE.tar.gz" -C /home/deploy app/

# Clean backups older than 30 days
find "$BACKUP_DIR" -mtime +30 -delete

echo "Backup complete: $DATE"
Enter fullscreen mode Exit fullscreen mode

What's your go-to server command? What server nightmare have you survived?

Follow @armorbreak for more practical developer guides.

Top comments (0)