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 how to keep it running, secure, and fast.

First Things After SSH In

# 1. Who am I and where am I?
whoami          # Current user (should NOT be root for daily work)
hostname        # Server name
uname -a        # Kernel info
cat /etc/os-release  # OS version

# 2. What's running on this machine?
free -h         # Memory usage
df -h           # Disk space
nproc           # CPU cores
uptime          # Uptime + load average
top             # Resource-hungry processes (q to quit)

# 3. What's listening? (Security check!)
ss -tlnp        # TCP ports + PIDs
ss -ulnp        # UDP ports
# If you see unexpected ports → investigate immediately!

# 4. Who else is here?
w               # Logged-in users + what they're doing
last            # Recent login history
lastb           # Failed login attempts (if enabled)
Enter fullscreen mode Exit fullscreen mode

User & Security Setup

# NEVER use root for daily operations!
# Create a user with sudo access:

sudo adduser deploy                    # Create new user
sudo usermod -aG sudo deploy           # Add to sudo group

# Set up SSH key auth (disable password login!):
# On YOUR local machine:
ssh-keygen -t ed25519                  # Generate key pair
ssh-copy-id deploy@your-server-ip      # Copy public key to server

# On server: disable password authentication:
sudo nano /etc/ssh/sshd_config
# Change: PasswordAuthentication no
# Change: PubkeyAuthentication yes
# Change: PermitRootLogin no
sudo systemctl restart sshd

# Firewall basics (ufw):
sudo ufw default deny incoming
sudo ufw default allow outgoing
sudo ufw allow ssh              # Don't lock yourself out!
sudo ufw allow 80/tcp           # HTTP
sudo ufw allow 443/tcp          # HTTPS
sudo ufw allow 3000/tcp         # Your app port (or use reverse proxy)
sudo ufw enable                # Activate firewall
sudo ufw status verbose        # Check rules

# Fail2Ban (auto-ban brute force attackers):
sudo apt install fail2ban
sudo cp /etc/fail2ban/jail.conf /etc/fail2ban/jail.local
sudo nano /etc/fail2ban/jail.local
# [sshd]
# enabled = true
# maxretry = 3
# bantime = 3600
sudo systemctl enable fail2ban
sudo systemctl start fail2ban
Enter fullscreen mode Exit fullscreen mode

Process Management: Keep Your App Running

# Option 1: systemd service (recommended for production)
sudo nano /etc/systemd/system/myapp.service

[Unit]
Description=My Node.js Application
After=network.target

[Service]
Type=simple
User=deploy
WorkingDirectory=/home/deploy/app
ExecStart=/home/deploy/.nvm/v22/bin/node /home/deploy/app/server.js
Restart=always
RestartSec=10
Environment=NODE_ENV=production
Environment=PORT=3000
StandardOutput=journal
StandardError=journal
SyslogIdentifier=myapp

[Install]
WantedBy=multi-user.target

# Manage your service:
sudo systemctl daemon-reload       # After editing .service file
sudo systemctl start myapp         # Start
sudo systemctl stop myapp          # Stop
sudo systemctl status myapp        # Check status
sudo systemctl restart myapp       # Restart
sudo journalctl -u myapp -f        # Follow logs live!

# Enable auto-start on boot:
sudo systemctl enable myapp

# Option 2: PM2 (easier for Node.js, good for development)
npm install -g pm2
pm2 start server.js --name "myapp"
pm2 start server.js --name "myapp" -i max    # Cluster mode (all CPUs)
pm2 list                                      # List apps
pm2 logs myapp                                # View logs
pm2 monit                                     # Interactive monitor
pm2 save                                      # Save process list (survives reboot)
pm2 startup                                   # Generate startup script (run output!)

# Option 3: screen/tmux (quick and dirty)
tmux new -s myapp                            # Create named session
node server.js                               # Run inside session
Ctrl+B D                                     # Detach (keeps running!)
tmux attach -t myapp                         # Reattach later
Enter fullscreen mode Exit fullscreen mode

Nginx Reverse Proxy

# /etc/nginx/sites-available/myapp
server {
    listen 80;
    server_name example.com www.example.com;

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

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

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

    # Security headers
    add_header X-Frame-Options DENY always;
    add_header X-Content-Type-Options nosniff always;
    add_header X-XSS-Protection "1; mode=block" always;
    add_header Strict-Transport-Security "max-age=31536000; includeSubDomains" always;

    # Gzip compression
    gzip on;
    gzip_types text/plain text/css application/json application/javascript text/xml;

    location / {
        proxy_pass http://127.0.0.1:3000;  # Your Node.js app
        proxy_http_version 1.1;

        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;

        # WebSocket support (if needed)
        proxy_set_header Upgrade $http_upgrade;
        proxy_set_header Connection "upgrade";

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

    # Static files cache
    location /static/ {
        alias /home/deploy/app/public/;
        expires 30d;
        add_header Cache-Control "public, immutable";
    }

    # Rate limiting zone (defined in nginx.conf)
    limit_req zone=api burst=20 nodelay;
}
Enter fullscreen mode Exit fullscreen mode
# SSL certificate (Let's Encrypt — free!):
sudo apt install certbot python3-certbot-nginx
sudo certbot --nginx -d example.com -d www.example.com
# Auto-renews! Check with: sudo certbot renew --dry-run

# Test and reload Nginx:
sudo nginx -t                          # Test config syntax
sudo systemctl reload nginx            # Reload without dropping connections
sudo systemctl restart nginx           # Full restart
Enter fullscreen mode Exit fullscreen mode

Monitoring & Log Management

# System logs:
journalctl -f                        # All system logs (follow)
journalctl -u myapp -f              # Your app's logs
journalctl -u myapp --since "1 hour ago"

# Application logs (if you write your own):
tail -f /var/log/myapp/access.log
tail -f /var/log/myapp/error.log

# Log rotation (prevent disk filling up!):
sudo nano /etc/logrotate.d/myapp
# /var/log/myapp/*.log {
#   daily
#   missingok
#   rotate 14
#   compress
#   delaycompress
#   notifempty
#   copytruncate
# }

# Quick health check script:
#!/bin/bash
# health-check.sh — run via cron every 5 minutes
APP_URL="https://example.com/health"
RESPONSE=$(curl -sf -o /dev/null -w "%{http_code}" "$APP_URL")
if [ "$RESPONSE" != "200" ]; then
  echo "$(date): DOWN (HTTP $RESPONSE)" >> /var/log/health-check.log
  # Optional: send alert (email, webhook, etc.)
fi
Enter fullscreen mode Exit fullscreen mode

Automated Backups

#!/bin/bash
# backup.sh — Daily database backup with retention
BACKUP_DIR="/backups"
DATE=$(date +%Y%m%d_%H%M%S)
RETENTION_DAYS=30

# PostgreSQL backup:
pg_dump -U app mydb | gzip > "$BACKUP_DIR/db_$DATE.sql.gz"

# Or SQLite backup:
cp /path/to/data.db "$BACKUP_DIR/db_$DATE.db"

# Delete backups older than RETENTION_DAYS:
find "$BACKUP_DIR" -mtime +$RETENTION_DAYS -delete

echo "Backup complete: $BACKUP_DIR/db_$DATE.sql.gz"

# Add to crontab (runs daily at 2 AM):
# 0 2 * * * /home/deploy/backup.sh >> /var/log/backup.log 2>&1
Enter fullscreen mode Exit fullscreen mode

What's your go-to server setup command? What did you wish you knew when you started?

Follow @armorbreak for more practical developer guides.

Top comments (0)