Back to Blog
LinuxDevOpsNginxSecurityServer Administration

Linux Server Administration for Full-Stack Developers

Umut Korkmaz2025-01-0511 min read

As a full-stack developer who frequently deploys applications on Linux servers, I've compiled the essential knowledge every developer should have. This isn't meant to replace dedicated DevOps—it's about being self-sufficient when you need to be.

Initial Server Setup

When you first get a fresh Ubuntu/Debian server:

bash
# Update packages
sudo apt update && sudo apt upgrade -y

# Create a non-root user
sudo adduser deploy
sudo usermod -aG sudo deploy

# Set up SSH key authentication
mkdir -p ~/.ssh
chmod 700 ~/.ssh
# Add your public key to ~/.ssh/authorized_keys

# Disable password authentication
sudo nano /etc/ssh/sshd_config
# Set: PasswordAuthentication no
sudo systemctl restart sshd

Nginx as Reverse Proxy

For Node.js/Next.js applications, Nginx handles SSL and load balancing:

nginx
# /etc/nginx/sites-available/myapp.conf

upstream nodejs_app {
    server 127.0.0.1:3000;
    server 127.0.0.1:3001;
    keepalive 64;
}

server {
    listen 80;
    server_name example.com www.example.com;
    return 301 https://$server_name$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;
    ssl_protocols TLSv1.2 TLSv1.3;
    ssl_ciphers ECDHE-ECDSA-AES128-GCM-SHA256:ECDHE-RSA-AES128-GCM-SHA256;
    ssl_prefer_server_ciphers off;

    # Security headers
    add_header X-Frame-Options "SAMEORIGIN" 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_vary on;
    gzip_proxied any;
    gzip_comp_level 6;
    gzip_types text/plain text/css text/xml application/json application/javascript application/xml;

    location / {
        proxy_pass http://nodejs_app;
        proxy_http_version 1.1;
        proxy_set_header Upgrade $http_upgrade;
        proxy_set_header Connection 'upgrade';
        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;
        proxy_cache_bypass $http_upgrade;
        proxy_read_timeout 86400s;
        proxy_send_timeout 86400s;
    }

    # Static files with caching
    location /_next/static/ {
        alias /var/www/myapp/.next/static/;
        expires 1y;
        add_header Cache-Control "public, immutable";
    }

    location /public/ {
        alias /var/www/myapp/public/;
        expires 30d;
        add_header Cache-Control "public";
    }
}

Process Management with PM2

Keep your Node.js apps running reliably:

bash
# Install PM2 globally
npm install -g pm2

# Start application with cluster mode
pm2 start npm --name "myapp" -i max -- start

# Save process list for restart on reboot
pm2 save
pm2 startup

# Useful PM2 commands
pm2 logs myapp          # View logs
pm2 monit               # Real-time monitoring
pm2 reload myapp        # Zero-downtime reload

PM2 ecosystem file for complex setups:

javascript
// ecosystem.config.js
module.exports = {
  apps: [{
    name: 'myapp',
    script: 'npm',
    args: 'start',
    instances: 'max',
    exec_mode: 'cluster',
    env: {
      NODE_ENV: 'production',
      PORT: 3000
    },
    error_file: '/var/log/myapp/error.log',
    out_file: '/var/log/myapp/out.log',
    merge_logs: true,
    max_memory_restart: '500M'
  }]
};

Firewall Configuration with UFW

bash
# Enable UFW
sudo ufw default deny incoming
sudo ufw default allow outgoing

# Allow SSH (important - don't lock yourself out!)
sudo ufw allow ssh

# Allow HTTP and HTTPS
sudo ufw allow 'Nginx Full'

# Allow specific ports if needed
sudo ufw allow 27017/tcp  # MongoDB (only if needed externally)

# Enable firewall
sudo ufw enable

# Check status
sudo ufw status verbose

SSL with Let's Encrypt

Free SSL certificates with automatic renewal:

bash
# Install Certbot
sudo apt install certbot python3-certbot-nginx

# Obtain certificate
sudo certbot --nginx -d example.com -d www.example.com

# Test automatic renewal
sudo certbot renew --dry-run

# Certificate auto-renews via systemd timer
sudo systemctl status certbot.timer

Log Management

bash
# View application logs
tail -f /var/log/myapp/out.log

# Nginx access logs
tail -f /var/log/nginx/access.log

# System logs
journalctl -u nginx -f

# Log rotation (already configured but customize if needed)
sudo nano /etc/logrotate.d/myapp

Example logrotate configuration:

/var/log/myapp/*.log {
    daily
    missingok
    rotate 14
    compress
    delaycompress
    notifempty
    create 0640 www-data www-data
    sharedscripts
    postrotate
        pm2 reloadLogs
    endscript
}

Monitoring and Alerts

Basic monitoring with shell scripts:

bash
#!/bin/bash
# /opt/scripts/health-check.sh

APP_URL="https://example.com/api/health"
SLACK_WEBHOOK="your-webhook-url"

response=$(curl -s -o /dev/null -w "%{http_code}" $APP_URL)

if [ $response != "200" ]; then
    curl -X POST -H 'Content-type: application/json' \
        --data "{\"text\":\"⚠️ Health check failed: $APP_URL returned $response\"}" \
        $SLACK_WEBHOOK
fi

Add to crontab:

bash
# Check every 5 minutes
*/5 * * * * /opt/scripts/health-check.sh

Security Hardening Checklist

  1. Keep system updated: sudo apt update && sudo apt upgrade
  2. Disable root login via SSH
  3. Use SSH key authentication only
  4. Configure firewall (UFW)
  5. Install and configure Fail2ban
  6. Regular backups
  7. Monitor logs for suspicious activity
bash
# Install and configure Fail2ban
sudo apt install fail2ban
sudo cp /etc/fail2ban/jail.conf /etc/fail2ban/jail.local
sudo nano /etc/fail2ban/jail.local

# Enable for SSH and Nginx
# [sshd]
# enabled = true
# [nginx-http-auth]
# enabled = true

sudo systemctl enable fail2ban
sudo systemctl start fail2ban

These skills have proven invaluable across projects like Kapsül Mobilya and E-Export City, where I manage the full deployment pipeline from development to production.