# WEBONICTECH — SERVER HANDBOOK — PART 4
## Sections 13 through 16 + Checklists + Learning Roadmap + Labs

---

# SECTION 13 — BEST PRACTICES

---

## 13.1 Server Hygiene

Think of your server like a car. If you never change the oil and ignore warning lights, it eventually breaks down at the worst possible time — when a client's site is under heavy traffic.

### Daily Habits

```bash
# Check disk space every morning
df -h

# Check if services are running
sudo systemctl status nginx php8.2-fpm mysql

# Scan for errors in logs
sudo tail -n 50 /var/log/nginx/error.log
```

### Weekly Tasks

```bash
# Update packages
sudo apt update
sudo apt upgrade -y

# Check SSL certificate expiry
sudo certbot certificates

# Check backup files are being created
ls -lht /backup/mysql/ | head -10

# Review Fail2ban status
sudo fail2ban-client status

# Check disk usage trend
du -sh /var/www/*
du -sh /var/log/*
```

### Monthly Tasks

```bash
# Review and remove unused packages
sudo apt autoremove

# Review user accounts (remove old/unused ones)
cat /etc/passwd | grep -v "nologin\|false" | awk -F: '{print $1}'

# Review cron jobs
sudo crontab -l
crontab -l

# Test backup restoration (CRITICAL)
# Restore a backup to a test location and verify it works

# Review firewall rules
sudo ufw status verbose
```

---

## 13.2 Backup Policies

**Rule: No backup = No recovery.** This is not optional.

### 3-2-1 Backup Rule
- **3** copies of data
- **2** different storage media/services
- **1** off-site backup

```bash
# Example backup strategy:
# Copy 1: /backup on the same server (fast restore)
# Copy 2: S3 bucket or external storage (off-site)
# Copy 3: Weekly full backup downloaded locally

# Script to backup to S3 (using AWS CLI)
sudo apt install awscli
aws configure   # Enter your AWS credentials

# Backup and upload to S3
DB_BACKUP="/backup/mysql/mydb_$(date +%Y%m%d).sql.gz"
mysqldump -u root -p mydb | gzip > $DB_BACKUP
aws s3 cp $DB_BACKUP s3://my-backup-bucket/mysql/

# Backup website files to S3
tar -czvf /backup/website_$(date +%Y%m%d).tar.gz /var/www/mysite
aws s3 cp /backup/website_$(date +%Y%m%d).tar.gz s3://my-backup-bucket/websites/
```

### What to Back Up

| Item | Frequency | Method |
|------|-----------|--------|
| Database | Daily | mysqldump + compress |
| Website files | Daily (or after deployment) | tar + S3 |
| Nginx/Apache config | After changes | Git or backup |
| .env files | After changes | Encrypted backup |
| SSL certificates | Monthly | Let's Encrypt auto-renews |
| Cron jobs | After changes | crontab -l > backup |

---

## 13.3 Naming Conventions

Consistent naming makes the server easy to navigate for everyone.

```bash
# Web directories
/var/www/clientname-project/      # clientname-projecttype
/var/www/webonictech-main/        # company internal
/var/www/raju-electricals-website/
/var/www/abc-corp-api/

# Nginx config files
/etc/nginx/sites-available/clientname-project.conf

# Backup files
/backup/mysql/dbname_YYYYMMDD_HHMMSS.sql.gz

# Log files (custom)
/var/log/nginx/clientname-project-error.log
/var/log/nginx/clientname-project-access.log

# PM2 app names
pm2 start server.js --name "clientname-api"
pm2 start npm --name "clientname-nextjs" -- start
```

---

## 13.4 Folder Organization for Web Projects

```
/var/www/
├── html/                    ← Default Apache page (don't put real sites here)
├── raju-electricals/        ← Plain HTML/PHP site
│   ├── public/              ← DocumentRoot
│   │   ├── index.php
│   │   ├── css/
│   │   └── images/
│   └── .htaccess
├── abc-corp-laravel/        ← Laravel app
│   ├── app/
│   ├── public/              ← DocumentRoot → app/public
│   ├── storage/
│   ├── .env
│   └── artisan
├── xyz-react-app/           ← React static build
│   ├── build/               ← DocumentRoot → build/
│   └── src/                 ← Source (may not be here in production)
└── techstartup-api/         ← Node.js API
    ├── server.js
    ├── package.json
    └── .env
```

---

## 13.5 Documentation Habits

Good teams document everything. A senior developer should not need to be called at midnight because nobody knows the database password.

### Server Documentation Template

Create a private (never public) document for each server:

```
# SERVER DOCUMENTATION — CLIENT NAME

## Server Details
- Provider: DigitalOcean / Hetzner / etc.
- IP Address: 203.0.113.42
- Region: Mumbai / Frankfurt / NYC
- OS: Ubuntu 22.04 LTS
- RAM: 4GB, CPU: 2 vCPU, Disk: 80GB SSD
- Created: 2026-01-01

## Access
- SSH User: ubuntu
- SSH Port: 22
- Key Location: /keys/clientname-server.pem
- cPanel (if applicable): https://...

## Domains & Sites
| Domain | Project | Root | Tech Stack |
|--------|---------|------|-----------|
| mysite.com | Main Website | /var/www/mysite | WordPress |
| api.mysite.com | API Server | - | Node.js (PM2, port 3000) |

## Database
- MySQL User: mysite_user
- MySQL DB: mysite_db
- Password: [stored in password manager - use Bitwarden/LastPass]

## Important Notes
- SSL: Let's Encrypt via Certbot (auto-renews)
- Backups: Daily to S3 bucket: my-backup-bucket
- Cron: Laravel scheduler runs every minute
- Queue Worker: Supervisor - check: sudo supervisorctl status

## Deployment Notes
- Deployment script: /usr/local/bin/deploy-mysite.sh
- Git repo: git@github.com:webonictech/mysite.git
- Branch: main

## History
- 2026-01-01: Server created
- 2026-01-15: WordPress installed
- 2026-02-01: SSL configured
```

> **Store server credentials in a team password manager (Bitwarden, 1Password, Keeper) — NEVER in Slack, WhatsApp, or plain text files.**

---

## 13.6 Security Best Practices Summary

```bash
# 1. Keep software updated
sudo apt update && sudo apt upgrade -y

# 2. Use strong, unique passwords for everything
# Database: "J!8xK@2mNp#5qR$9" — not "password123"

# 3. SSH key authentication only
# Disable password login: PasswordAuthentication no in sshd_config

# 4. Principle of least privilege
# Give minimum required permissions
# DB user for app: SELECT, INSERT, UPDATE, DELETE only
# Not: GRANT ALL

# 5. No sensitive data in code
# Use .env files, never commit secrets to Git

# 6. APP_DEBUG=false in production
# NEVER true on live server — exposes file paths, DB info, etc.

# 7. Restrict database access
# bind-address = 127.0.0.1 in MySQL config (no remote connections)
# If remote needed: allow only specific IPs via firewall

# 8. Regularly scan for malware
# Install ClamAV or use professional scanner

# 9. Monitor access logs
# Watch for unusual patterns: many 404s, POST floods, login attempts

# 10. HTTPS everywhere
# No HTTP-only sites in 2026. SSL is free. There are no excuses.
```

---

## 13.7 Monitoring Basics

```bash
# Install basic monitoring tools
sudo apt install htop iotop iftop

# CPU/Memory: htop
htop

# Disk I/O: iotop
sudo iotop

# Network traffic: iftop
sudo iftop

# Simple uptime monitoring: UptimeRobot (free)
# Sign up at uptimerobot.com
# Add your domain — it pings every 5 minutes
# Sends email/SMS when site goes down

# More advanced: Netdata (open source, free)
# Installs in one command:
bash <(curl -Ss https://my-netdata.io/kickstart.sh)
# Access at: http://server-ip:19999 (restrict to your IP with firewall)

# Nginx status page
# Add to server block:
location /nginx-status {
    stub_status;
    allow 127.0.0.1;
    deny all;
}
curl http://127.0.0.1/nginx-status
```

---

## 13.8 Handling Client Servers Safely

When working on a live client server, follow these rules:

```
BEFORE touching anything:
□ Back up the database
□ Back up the website files
□ Note the current working state
□ Do it during off-peak hours (2 AM - 5 AM if possible)
□ Inform the client of maintenance window

WHILE working:
□ Enable maintenance mode (Laravel: php artisan down)
□ Work on one thing at a time
□ Test every change before moving on
□ Keep a list of everything you changed

IF something breaks:
□ Don't panic
□ Check logs immediately
□ Rollback if needed (git reset, restore DB)
□ Fix calmly and methodically

AFTER work:
□ Verify the site is working (visit in browser)
□ Test all critical functionality (forms, payments, login)
□ Disable maintenance mode
□ Document what was done and why
```

---

---

# SECTION 14 — REAL-WORLD SCENARIOS

---

## 14.1 Scenario: Laravel Website Not Loading After Deployment

**Symptoms:** Site was working, you deployed new code, now showing 500 error.

**Debugging Flow:**

```bash
# Step 1: Check the application log
tail -n 50 /var/www/mylaravelapp/storage/logs/laravel.log

# Common findings:
# "No such file or directory" → Missing file, wrong path
# "Class not found" → Composer autoload not updated
# "SQLSTATE" → Database error
# "Permission denied" → File ownership/permission issue
# "APP_KEY not set" → Missing APP_KEY in .env

# Step 2: Clear and rebuild caches
cd /var/www/mylaravelapp
php artisan config:clear
php artisan cache:clear
php artisan route:clear
php artisan view:clear

# Step 3: Rebuild caches
composer dump-autoload
php artisan config:cache

# Step 4: Check migrations
php artisan migrate:status
php artisan migrate --force

# Step 5: Check permissions
sudo chown -R www-data:www-data storage bootstrap/cache
sudo chmod -R 775 storage bootstrap/cache

# Step 6: Check .env file
cat .env | grep "APP_" | grep -v PASSWORD

# Most likely cause:
# New environment variable added in .env.example but not in production .env
# Fix: Add the missing variable to production .env
```

---

## 14.2 Scenario: React App Shows Blank Page

**Symptoms:** React app deployed, but browser shows blank white page.

**Debugging Flow:**

```bash
# Step 1: Open browser developer tools (F12)
# Check Console tab for JavaScript errors
# Common: "Failed to load resource: 404" for JS/CSS files

# Step 2: Check Nginx configuration
# Is the root pointing to the build/ folder?
grep -r "root" /etc/nginx/sites-enabled/myreactapp.conf

# Step 3: Check if build files exist
ls -la /var/www/myreactapp/
# You need: index.html, static/ folder

# Step 4: Check file permissions
ls -la /var/www/myreactapp/
# Must be readable by www-data

# Step 5: Check if homepage is configured in package.json
# If React Router is used with hash routing:
# In package.json: "homepage": "/"

# Common causes:
# 1. Build folder not deployed (only source code uploaded)
#    Fix: Run npm run build and deploy the build/ folder
# 2. React Router without try_files /index.html
#    Fix: Add to Nginx: try_files $uri $uri/ /index.html;
# 3. Wrong base URL in build
#    Fix: Set PUBLIC_URL or homepage in package.json
```

---

## 14.3 Scenario: WordPress Plugin Crash

**Symptoms:** WordPress admin says "There has been a critical error" or WSOD after activating a plugin.

**Debugging Flow:**

```bash
# Step 1: Check PHP error log
tail -n 50 /var/log/php/php-error.log
# Or:
tail -n 50 /var/www/mywordpresssite/wp-content/debug.log

# Step 2: If you can't access admin, deactivate plugin via database
mysql -u root -p wordpress_db
SELECT option_value FROM wp_options WHERE option_name = 'active_plugins';
# You'll see a serialized list of plugins

# Deactivate all plugins:
UPDATE wp_options SET option_value = 'a:0:{}' WHERE option_name = 'active_plugins';

# Step 3: Or deactivate via WP-CLI
sudo -u www-data wp plugin deactivate problematic-plugin-name

# Step 4: If memory issue, increase limit
# In wp-config.php:
define('WP_MEMORY_LIMIT', '512M');
# In php.ini:
memory_limit = 512M

# Step 5: Check plugin compatibility
# Visit plugin page on wordpress.org
# Check "Tested up to" version
```

---

## 14.4 Scenario: Node.js App Crashing After Server Reboot

**Symptoms:** API was working yesterday. Server was rebooted. Now 502 from Nginx.

**Root Cause:** PM2 startup script not configured.

**Diagnosis:**
```bash
pm2 status
# Shows no processes or empty list

sudo systemctl status nginx
# Nginx is running — the issue is the Node app isn't

# Check if PM2 startup was configured
systemctl list-units | grep pm2
# If empty: PM2 startup was never set up
```

**Fix:**
```bash
# Start the application again
pm2 start ecosystem.config.js --env production
# or
pm2 start server.js --name "my-api"

# Set up startup
pm2 startup
# Run the command it shows you (it includes your username and path)
# Example: sudo env PATH=$PATH:/usr/bin /usr/lib/node_modules/pm2/bin/pm2 startup systemd -u ubuntu --hp /home/ubuntu

# Save current processes
pm2 save

# Verify
pm2 status
# Now test reboot:
sudo reboot
# After reboot, SSH back in
pm2 status
# App should be running
```

---

## 14.5 Scenario: SSL Certificate Expired

**Symptoms:** Browser shows "Your connection is not private" or NET::ERR_CERT_DATE_INVALID

**Diagnosis:**
```bash
# Check certificate expiry
sudo certbot certificates
# Shows: VALID: 0 days (or shows it expired)

echo | openssl s_client -servername mysite.com -connect mysite.com:443 2>/dev/null | openssl x509 -noout -dates
```

**Fix:**
```bash
# Force renewal
sudo certbot renew --force-renewal

# Or renew specific certificate
sudo certbot renew --cert-name mysite.com --force-renewal

# Reload web server
sudo systemctl reload nginx

# Verify
sudo certbot certificates
# Should now show valid for 89 days
```

**Prevention:**
```bash
# Check auto-renewal timer is active
sudo systemctl status certbot.timer

# If not enabled:
sudo systemctl enable certbot.timer
sudo systemctl start certbot.timer

# Test renewal script
sudo certbot renew --dry-run
```

---

## 14.6 Scenario: Disk Full Issue

**Symptoms:** PHP errors, database writes failing, log files stop updating, strange application errors everywhere.

**Diagnosis:**
```bash
df -h
# Filesystem      Size  Used Avail Use% Mounted on
# /dev/sda1        30G   30G     0  100% /   ← PROBLEM!
```

**Fix (in order of safety):**
```bash
# Step 1: Find the biggest offenders
du -sh /var/log/*      2>/dev/null | sort -rh | head -10
du -sh /var/www/*      2>/dev/null | sort -rh | head -10
du -sh /tmp/*          2>/dev/null | sort -rh | head -10
du -sh /home/*         2>/dev/null | sort -rh | head -10
du -sh /backup/*       2>/dev/null | sort -rh | head -10

# Step 2: Safe cleanups
# Clear apt cache
sudo apt clean                  # Usually frees 500MB-1GB

# Truncate large log files (NOT delete — nginx needs them to exist)
sudo truncate -s 0 /var/log/nginx/access.log
sudo truncate -s 0 /var/log/apache2/access.log
sudo truncate -s 0 /var/log/mysql/mysql.log

# Remove old database backups
ls -lht /backup/mysql/
# Keep last 7, remove older:
find /backup/mysql -name "*.sql.gz" -mtime +7 -delete

# Clear PHP sessions
sudo rm -rf /var/lib/php/sessions/*

# Clear PM2 logs
pm2 flush

# Step 3: Find large files
find / -type f -size +100M 2>/dev/null | head -20

# Step 4: Remove old kernel packages
sudo apt autoremove

# Prevention: Set up log rotation and backup retention policies
```

---

## 14.7 Scenario: Database Connection Refused

**Symptoms:** "SQLSTATE[HY000] [2002] Connection refused" or "Can't connect to MySQL server on '127.0.0.1'"

**Debugging Flow:**
```bash
# Step 1: Is MySQL running?
sudo systemctl status mysql
# If "failed" or "inactive":
sudo systemctl start mysql
sudo systemctl status mysql
# If it fails to start:
sudo journalctl -u mysql -n 50   # Read the error!

# Step 2: Can you connect manually?
mysql -u myapp_user -p myapp_db
# If "Access denied" → Wrong credentials
# If "Connection refused" → MySQL not listening

# Step 3: Check MySQL bind address
sudo grep -i "bind-address" /etc/mysql/mysql.conf.d/mysqld.cnf
# bind-address = 127.0.0.1 is correct for local connections

# Step 4: Check .env has correct DB details
cat /var/www/myapp/.env | grep "^DB_"
# DB_HOST=127.0.0.1 (NOT 'localhost' for some setups)
# DB_PORT=3306
# DB_DATABASE=myapp_db
# DB_USERNAME=myapp_user
# DB_PASSWORD=correctpassword

# Step 5: Check MySQL logs
sudo tail -n 50 /var/log/mysql/error.log

# Common MySQL startup failures:
# "Table './mysql/host' is marked as crashed" → Need repair
# "Disk full" → Free up disk space
# "Can't read dir '/var/lib/mysql/'" → Permission issue
# sudo chown -R mysql:mysql /var/lib/mysql && sudo systemctl restart mysql
```

---

## 14.8 Scenario: Nginx 502 After New Node.js Deployment

**Symptoms:** Was working before deployment. Now 502.

**Debugging Flow:**
```bash
# Step 1: Check Nginx error log
sudo tail -n 20 /var/log/nginx/myapp-error.log
# Likely: "connect() failed (111: Connection refused) while connecting to upstream"

# Step 2: Check if Node app is running
pm2 status
# If it shows "stopped" or "errored":

# Step 3: Check PM2 logs for the error
pm2 logs my-api --lines 50
# Common errors:
# "Error: Cannot find module 'xxx'" → npm install needed
# "port 3000 already in use" → Previous process still running
# "SyntaxError" → Bug in your code
# "Error: ENOENT: .env not found" → .env file missing

# Step 4: Restart with verbose output
pm2 stop my-api
pm2 start server.js --name "my-api" --no-daemon
# This runs in foreground — you see all output

# Step 5: Common fixes
cd /var/www/myapp
npm install                   # Install any new dependencies
cp .env.example .env          # Create .env if missing
nano .env                     # Fill in values
pm2 restart my-api
```

---

## 14.9 Scenario: Apache .htaccess Rewrite Not Working

**Symptoms:** WordPress shows posts as 404. Laravel routes not working on Apache.

**Debugging Flow:**
```bash
# Step 1: Is mod_rewrite enabled?
apache2ctl -M | grep rewrite
# If not listed:
sudo a2enmod rewrite
sudo systemctl reload apache2

# Step 2: Is AllowOverride set to All?
# Check your VirtualHost config:
sudo cat /etc/apache2/sites-enabled/mysite.conf | grep -A5 "<Directory"
# Should show: AllowOverride All
# If it shows: AllowOverride None → That's the problem

# Fix: Edit your VirtualHost config
sudo nano /etc/apache2/sites-enabled/mysite.conf
# Change AllowOverride None to AllowOverride All
sudo systemctl reload apache2

# Step 3: Does .htaccess file exist?
ls -la /var/www/mysite/.htaccess

# Step 4: Test Apache config
sudo apache2ctl configtest
# Should say: Syntax OK
```

---

---

# SECTION 15 — FINAL CHECKLISTS

---

## 15.1 New Server Setup Checklist

```markdown
## New Server Setup Checklist

### Initial Setup
□ Update system: sudo apt update && sudo apt upgrade -y
□ Set server timezone: sudo timedatectl set-timezone Asia/Kolkata
□ Create a non-root user: sudo adduser developer && sudo usermod -aG sudo developer
□ Set up SSH keys for the new user
□ Disable root SSH login (PermitRootLogin no)
□ Disable password authentication (PasswordAuthentication no)
□ Change SSH port (optional, but recommended)
□ Test SSH with new user before logging out as root

### Firewall
□ Install UFW: sudo apt install ufw
□ Allow SSH: sudo ufw allow ssh (or custom port)
□ Allow HTTP/HTTPS: sudo ufw allow 'Nginx Full'
□ Enable UFW: sudo ufw enable
□ Verify: sudo ufw status

### Software Installation
□ Install Nginx or Apache2
□ Install PHP + required extensions
□ Install MySQL or MariaDB
□ Secure MySQL: sudo mysql_secure_installation
□ Install Composer (if PHP apps)
□ Install Node.js (if Node apps)
□ Install PM2 (if Node apps)
□ Install Git
□ Install Certbot for SSL

### Security
□ Install Fail2ban: sudo apt install fail2ban
□ Configure Fail2ban rules
□ Set up log rotation (usually default)
□ Install unattended-upgrades for security patches

### Backups
□ Create backup directory: sudo mkdir -p /backup/mysql /backup/files
□ Create backup scripts
□ Add backup cron jobs
□ Test first backup

### Documentation
□ Document server IP, credentials, installed software
□ Store credentials in team password manager
□ Add server to monitoring tool (UptimeRobot)
```

---

## 15.2 Laravel Deployment Checklist

```markdown
## Laravel Deployment Checklist

### Before Deploying
□ Run tests locally: php artisan test
□ Review .env.example for new variables
□ Take database backup
□ Check disk space on server

### Deployment Steps
□ Enable maintenance mode: php artisan down
□ Pull latest code: git pull origin main
□ Install dependencies: composer install --no-dev --optimize-autoloader
□ Update .env with new variables (if any)
□ Run migrations: php artisan migrate --force
□ Create storage symlink: php artisan storage:link
□ Set correct permissions: chown -R www-data:www-data storage bootstrap/cache
□ Clear old caches: php artisan optimize:clear
□ Rebuild caches: php artisan optimize
□ Restart queue workers: php artisan queue:restart

### After Deploying
□ Disable maintenance mode: php artisan up
□ Test the website in browser
□ Test critical features (login, forms, payments)
□ Check error log: tail -f storage/logs/laravel.log
□ Monitor for first 15 minutes
```

---

## 15.3 WordPress Deployment Checklist

```markdown
## WordPress Deployment Checklist

### Before Deploying
□ Take full database backup: mysqldump
□ Take files backup: tar the wp-content folder
□ Note current WordPress version, PHP version
□ Test changes on staging first

### Fresh WordPress Install
□ Create MySQL database and user
□ Download and extract WordPress
□ Configure wp-config.php
□ Set file permissions (755 directories, 644 files)
□ Run WordPress installation wizard
□ Set permalink structure

### After Installation
□ Install SSL certificate: sudo certbot --nginx
□ Install necessary plugins
□ Configure caching plugin
□ Disable XML-RPC if not needed
□ Set file editor to disabled (DISALLOW_FILE_EDIT)
□ Configure regular backups (UpdraftPlus plugin)
□ Set up security plugin (Wordfence)
□ Test login, forms, uploads

### Ongoing
□ Keep WordPress core updated
□ Keep plugins and themes updated
□ Monitor access logs for suspicious activity
□ Verify backups are being created
```

---

## 15.4 Node.js Deployment Checklist

```markdown
## Node.js Deployment Checklist

### Preparation
□ Ensure Node.js version matches local (check .nvmrc or package.json engines)
□ All dependencies in package.json (not just installed locally)
□ .env.example updated with all required variables
□ Tests passing

### Deployment
□ Pull latest code
□ npm install --production
□ Create/update .env with production values
□ pm2 restart app-name (or pm2 start for first time)
□ Verify app is running: pm2 status

### Post Deployment
□ Check logs: pm2 logs app-name
□ Verify Nginx is routing correctly
□ Test API endpoints
□ Ensure pm2 startup is configured: pm2 startup && pm2 save
```

---

## 15.5 SSL Checklist

```markdown
## SSL Checklist

□ Domain DNS is pointing to this server (check with dig)
□ Port 80 and 443 are open in firewall
□ Web server is running and serving the domain on port 80
□ Install Certbot: sudo apt install certbot python3-certbot-nginx
□ Get certificate: sudo certbot --nginx -d domain.com -d www.domain.com
□ Verify HTTPS works in browser
□ Verify HTTP redirects to HTTPS
□ Check certificate details: sudo certbot certificates
□ Test auto-renewal: sudo certbot renew --dry-run
□ Check auto-renewal timer: sudo systemctl status certbot.timer
□ Test SSL quality: https://www.ssllabs.com/ssltest/
□ Check for mixed content (HTTP resources on HTTPS page)
□ Add HSTS header if appropriate
```

---

## 15.6 Security Checklist

```markdown
## Security Checklist

### Server
□ SSH key authentication only (no password login)
□ Root SSH disabled
□ Firewall enabled (UFW)
□ Only required ports open
□ Fail2ban installed and configured
□ System packages up to date
□ Unused services disabled and removed

### Web Server
□ Server version hidden (server_tokens off in Nginx)
□ Directory listing disabled
□ Security headers configured (X-Frame-Options, CSP, etc.)
□ .env files not accessible from web
□ Upload directories block PHP execution

### Application
□ APP_DEBUG=false in production
□ Strong database passwords
□ Database user has minimum required privileges
□ .env not in Git repository
□ Sensitive files in .gitignore
□ No hardcoded credentials in code

### Database
□ MySQL bound to 127.0.0.1 (no external access)
□ Root login disabled remotely
□ Strong root password set
□ phpMyAdmin access restricted by IP

### Backups
□ Daily automated backups
□ Backups stored off-server
□ Backup restoration tested
□ Backup retention policy (e.g., keep 30 days)
```

---

---

# SECTION 16 — GLOSSARY

---

**Apache2** — Open-source web server software. Processes HTTP requests and serves web content. Can run PHP directly through mod_php.

**A Record** — A DNS record that maps a domain name to an IPv4 address.

**Artisan** — The command-line interface for Laravel. Runs migrations, clears caches, and executes custom tasks.

**apt** — Advanced Package Tool. Package manager for Debian-based Linux systems (Ubuntu).

**Bash** — The default command-line shell on most Linux systems.

**Cache** — A temporary storage of frequently accessed data to speed up future requests. Example: Storing a database query result in Redis instead of running it every time.

**CDN (Content Delivery Network)** — A global network of servers that delivers static content (images, CSS, JS) from a server closest to the user. Examples: Cloudflare, AWS CloudFront.

**Certbot** — A free, open-source tool that automates obtaining and renewing Let's Encrypt SSL certificates.

**chmod** — Linux command to change file/directory permissions.

**chown** — Linux command to change file/directory ownership.

**CNAME** — DNS record that maps one domain to another domain name (alias).

**Composer** — Dependency manager for PHP. Like npm but for PHP projects.

**cron / crontab** — A Linux utility for scheduling commands to run automatically at specific times.

**Daemon** — A background process that runs continuously without direct user interaction. Apache, Nginx, MySQL are all daemons.

**DNS (Domain Name System)** — The system that translates human-readable domain names (example.com) to IP addresses.

**DocumentRoot** — Apache configuration directive that specifies which directory contains the website files.

**Dotenv (.env)** — A file containing environment-specific configuration variables. Should never be committed to version control.

**Environment Variable** — A key-value pair available to running processes. Used to store configuration without hardcoding.

**FastCGI** — A protocol for interfacing external applications (like PHP) with web servers. PHP-FPM uses FastCGI.

**Firewall** — Software or hardware that controls incoming and outgoing network traffic based on rules. UFW is Ubuntu's firewall tool.

**FTP (File Transfer Protocol)** — Protocol for transferring files. Insecure — use SFTP or SCP instead on production servers.

**Git** — A distributed version control system. Used to track code changes and collaborate.

**Gzip** — A compression algorithm used to reduce file sizes for faster transfer over the network.

**htop** — An interactive system process viewer. Better version of `top`.

**HTTP (Hypertext Transfer Protocol)** — The protocol for transmitting web content. Unencrypted, uses port 80.

**HTTPS (HTTP Secure)** — HTTP with encryption via SSL/TLS. Uses port 443. Mandatory for all production websites.

**IP Address** — Unique numerical address assigned to each device on a network (e.g., 203.0.113.42).

**journalctl** — Command to read logs from systemd's logging service.

**Laravel** — A popular PHP web framework known for elegant syntax and rich features.

**Let's Encrypt** — A free, automated Certificate Authority that provides SSL certificates.

**Load Balancer** — A system that distributes incoming traffic across multiple servers.

**MX Record** — Mail Exchanger DNS record. Specifies which server handles email for a domain.

**Middleware** — Software that acts between different applications/layers. In web apps, middleware processes requests before they reach your code.

**MySQL** — The world's most popular open-source relational database management system.

**Nginx** — High-performance web server and reverse proxy known for handling massive concurrency efficiently.

**npm** — Node Package Manager. Tool for managing JavaScript dependencies.

**NS Record** — Nameserver DNS record. Indicates which DNS servers are authoritative for a domain.

**OPcache** — PHP extension that caches compiled PHP bytecode in memory, significantly improving performance.

**PHP-FPM (FastCGI Process Manager)** — PHP's process manager for handling PHP requests. Used with Nginx for better performance than mod_php.

**PM2** — Production process manager for Node.js applications. Keeps apps running and restarts on crash.

**Port** — A numbered communication endpoint on a server. Different services listen on different ports (80=HTTP, 443=HTTPS, 3306=MySQL).

**Process** — A running instance of a program.

**Proxy (Reverse Proxy)** — A server that sits between clients and backend servers, forwarding requests. Nginx commonly acts as a reverse proxy for Node.js apps.

**Redis** — In-memory data structure store used as database, cache, and message broker.

**root** — The superuser account with full system access on Linux. Use `sudo` instead of logging in as root.

**Root Directory (/)** — The top-level directory in Linux's file system hierarchy.

**Server Block** — Nginx's equivalent of Apache's VirtualHost. Defines how Nginx handles requests for a particular domain.

**SFTP (Secure File Transfer Protocol)** — Secure file transfer over SSH. Use instead of FTP.

**SSH (Secure Shell)** — Cryptographic protocol for secure remote access to servers over an unsecured network.

**SSL/TLS (Secure Sockets Layer / Transport Layer Security)** — Encryption protocols that secure data in transit. SSL is the older name; TLS is the current standard.

**Staging** — An environment that mirrors production, used for testing before deploying to live.

**sudo** — Command that allows permitted users to run commands as the root user.

**Supervisor** — A process control system for Linux. Used to manage long-running processes like Laravel queue workers.

**Symlink (Symbolic Link)** — A file that is a shortcut/pointer to another file or directory. Nginx uses symlinks in sites-enabled.

**systemctl** — Command to control systemd services. Start, stop, restart, and check status of services.

**TCP/IP** — The fundamental communication protocols of the internet.

**TLS** — See SSL/TLS.

**TXT Record** — DNS record that stores text data. Used for domain verification, SPF, DKIM.

**UFW (Uncomplicated Firewall)** — Easy-to-use interface for managing iptables firewall rules on Ubuntu.

**Upstream** — In Nginx, an upstream block defines a group of backend servers to proxy to.

**VirtualHost** — Apache configuration that allows one server to host multiple websites with different domains.

**VPS (Virtual Private Server)** — A virtualized server environment that gives you root access and dedicated resources on shared hardware.

**www-data** — The user account that Apache and Nginx run as on Ubuntu/Debian. Web files should be owned by this user.

---

---

# LEARNING ROADMAP FOR FRESHERS

---

## Phase 1: Foundation (Week 1-2)

**Goal:** Understand servers and get comfortable with the terminal.

```
□ Read Section 1 (Introduction to Servers) completely
□ Learn basic Linux commands (Section 2.3-2.4)
□ Practice: SSH into a test server
□ Practice: Navigate directories, create files, view files
□ Practice: Install Nginx and visit the default page
□ Practice: Edit a configuration file with nano
□ Understand: What happens when you visit a URL
□ Understand: What is a VPS vs shared hosting
```

**Mini Lab:**
1. Set up a free VPS on DigitalOcean or Hetzner (use promo credit)
2. SSH into it
3. Install Nginx
4. Create a simple HTML page at `/var/www/html/index.html`
5. Access it from your browser

---

## Phase 2: Web Servers (Week 3-4)

**Goal:** Configure Nginx and Apache for real projects.

```
□ Set up an Apache2 VirtualHost for a PHP site
□ Set up an Nginx server block
□ Deploy a static HTML/CSS site
□ Deploy a basic PHP page
□ Configure SSL with Certbot
□ Force HTTP → HTTPS redirect
□ Understand: logs, error debugging
□ Practice: Create 3 different virtual hosts on one server
```

**Mini Lab:**
1. Create two different domains (use /etc/hosts for testing)
2. Set up virtual hosts for both
3. Add SSL to both
4. Test that each domain shows its correct site

---

## Phase 3: PHP and Frameworks (Week 5-7)

**Goal:** Deploy real PHP applications.

```
□ Install PHP 8.2 + extensions
□ Install Composer
□ Deploy a plain PHP project
□ Deploy a Laravel application from scratch
□ Set correct Laravel permissions
□ Run artisan commands
□ Set up Laravel queue worker
□ Set up Laravel cron
□ Deploy a WordPress site
□ Configure wp-config.php properly
□ Fix common WordPress errors
```

**Mini Lab:**
1. Deploy a fresh Laravel project (use laravel/laravel template)
2. Configure it with a MySQL database
3. Run migrations
4. Set up cron job

---

## Phase 4: Node.js and Modern Apps (Week 8-9)

**Goal:** Deploy Node.js, React, and Next.js apps.

```
□ Install Node.js via NVM
□ Install PM2
□ Deploy an Express.js API
□ Configure Nginx reverse proxy for Node.js
□ Deploy a React app (static build)
□ Deploy a Next.js app with PM2
□ Configure PM2 startup
□ Use .env files in Node.js
```

**Mini Lab:**
1. Deploy a simple Express.js "Hello World" API
2. Configure Nginx to proxy port 80 → port 3000
3. Add SSL
4. Verify PM2 restarts after `sudo reboot`

---

## Phase 5: Database and Security (Week 10-11)

**Goal:** Manage databases and secure the server.

```
□ Create MySQL databases and users
□ Backup and restore databases
□ Set up automated backup cron job
□ Configure UFW firewall
□ Install and configure Fail2ban
□ Set up SSH key authentication
□ Harden file permissions
□ Understand and apply security headers
```

**Mini Lab:**
1. Set up daily MySQL backup cron
2. Simulate a backup restoration
3. Configure UFW to only allow ports 22, 80, 443
4. Test Fail2ban by triggering multiple failed SSH logins

---

## Phase 6: Troubleshooting Mastery (Week 12-14)

**Goal:** Debug any issue on the server independently.

```
□ Read all of Section 11
□ Practice reading error logs
□ Debug a deliberately broken Laravel setup
□ Fix a 502 error (stop PHP-FPM, observe, fix)
□ Fix a 403 error (wrong permissions, observe, fix)
□ Fix a disk full issue
□ Understand all HTTP status codes
□ Create your own debugging checklist
```

**Mini Lab:**
1. Break your server in 5 different ways (permission wrong, FPM stopped, etc.)
2. Debug and fix each one using only logs
3. Document what you found and how you fixed it

---

---

# DAILY PRACTICE PLAN FOR INTERNS

---

## Week 1-2: Linux Basics

```
Monday:    SSH login, pwd, ls, cd, mkdir, touch
Tuesday:   nano/vim, file read/write, cat, less, tail
Wednesday: chmod, chown, user management, sudo
Thursday:  Package management (apt), service management (systemctl)
Friday:    Process management (ps, kill, htop), cron jobs, Practice quiz
```

## Week 3-4: Web Servers

```
Monday:    Install Nginx, understand nginx.conf
Tuesday:   Create server block, deploy HTML page
Wednesday: Install Apache2, create VirtualHost, deploy PHP page  
Thursday:  SSL setup with Certbot, HTTP→HTTPS redirect
Friday:    Review logs, fix deliberately broken configs
```

## Week 5-7: PHP Deployment

```
Week 5 Mon: Install PHP 8.2, PHP-FPM, configure php.ini
Week 5 Tue: Deploy plain PHP project
Week 5 Wed: Install Composer, understand package management
Week 5 Thu: Deploy Laravel from scratch
Week 5 Fri: Laravel artisan commands, migrations, permissions

Week 6 Mon: Laravel .env configuration
Week 6 Tue: Laravel queue workers (Supervisor)
Week 6 Wed: Laravel cron setup
Week 6 Thu: WordPress installation from scratch
Week 6 Fri: WordPress security, wp-cli basics

Week 7:    Practice deploying 3 different projects, troubleshoot issues
```

## Week 8-9: Node.js Deployment

```
Monday:    Install Node.js (NVM), npm basics
Tuesday:   Deploy Express.js app, understand PM2
Wednesday: Nginx reverse proxy for Node.js
Thursday:  Deploy React static build
Friday:    Deploy Next.js app, PM2 startup config
```

## Week 10-12: Security and Troubleshooting

```
Week 10: UFW, Fail2ban, SSH hardening, file permissions deep dive
Week 11: Database management, backups, restoration practice
Week 12: Deliberate breakage and fixing exercises, code review
```

---

**Daily Log Habit:**
Every day, write 5-10 lines in a personal notes file about what you did and what you learned. This builds your personal knowledge base. Example:

```
Date: 2026-01-15

Today I learned:
- How to check which PHP version is running: php -v
- mod_rewrite needs AllowOverride All to work in .htaccess
- The www-data user needs write access to Laravel storage/ folder
- Running: sudo chown -R www-data:www-data storage fixed my 500 error

Commands I used today:
- sudo a2enmod rewrite
- sudo systemctl reload apache2
- php artisan config:cache
- tail -f /var/log/apache2/error.log

Mistakes I made:
- Forgot to reload Apache after changing VirtualHost config
- Had AllowOverride None — should be AllowOverride All
```

---

---

# INTERVIEW QUESTIONS & ANSWERS

---

## Linux Fundamentals

**Q: What is the difference between `rm` and `rm -rf`?**
A: `rm` removes a single file. `rm -rf` removes directories and all their contents recursively and forcefully (no confirmation). Use `rm -rf` with extreme caution — there is no undo.

**Q: What does `chmod 755` mean?**
A: Owner has read+write+execute (7), group has read+execute (5), others have read+execute (5). Standard for directories and executable files.

**Q: What is the purpose of `sudo`?**
A: Allows a permitted user to execute a command as the superuser (root). Safer than logging in as root directly.

**Q: Where are Apache error logs stored on Ubuntu?**
A: `/var/log/apache2/error.log`

**Q: What is the purpose of the `/etc` directory?**
A: Contains all system-wide configuration files. Apache config is in `/etc/apache2/`, Nginx in `/etc/nginx/`, PHP in `/etc/php/`, etc.

**Q: What command checks which process is listening on port 80?**
A: `sudo ss -tlnp | grep :80`

---

## Web Server Questions

**Q: What is the difference between Apache and Nginx?**
A: Apache uses a process/thread-based model and supports .htaccess per-directory config. Nginx uses an event-driven asynchronous model, handles massive concurrency better, doesn't support .htaccess but is configured centrally. Nginx is generally faster for static content and works well as a reverse proxy.

**Q: What is a reverse proxy?**
A: A server that receives client requests and forwards them to one or more backend servers. Nginx acts as a reverse proxy when it receives requests on port 80/443 and forwards them to a Node.js app on port 3000.

**Q: Why does Apache's DocumentRoot for Laravel point to `public/` and not the project root?**
A: For security. The `public/` folder is the only directory that should be publicly accessible. The `.env` file, `app/`, `database/`, and `config/` folders should never be accessible from the web. Pointing DocumentRoot to `public/` means even if someone tries to access `example.com/../.env`, they can't reach it.

**Q: What does `AllowOverride All` do in Apache?**
A: Allows `.htaccess` files in that directory to override Apache's configuration. Required for WordPress permalinks, Laravel routing, etc.

**Q: What is PHP-FPM and why use it over mod_php?**
A: PHP-FPM (FastCGI Process Manager) runs PHP as a separate service and communicates with Nginx/Apache via FastCGI. Benefits: can run as a different user per site, better process management, required for Nginx, more stable under high load.

---

## Deployment Questions

**Q: What is the purpose of `php artisan config:cache`?**
A: It compiles all configuration files into a single cached file, speeding up application boot time. Must be run after changing `.env` values.

**Q: What is PM2 and why do we use it for Node.js?**
A: PM2 is a process manager for Node.js. It keeps apps running (restarts on crash), manages logs, enables cluster mode, and persists processes across server reboots.

**Q: What is the difference between `git pull` and `git fetch`?**
A: `git fetch` downloads changes from remote but doesn't merge them. `git pull` = `git fetch` + `git merge`. In production deployments, `git fetch` + `git reset --hard origin/main` is often used for a clean, deterministic update.

**Q: Why should `APP_DEBUG=false` be set in production Laravel?**
A: When `APP_DEBUG=true`, Laravel shows detailed error pages with file paths, database structure, and stack traces — exposing sensitive system information to anyone who triggers an error.

---

## Troubleshooting Questions

**Q: A website returns 502 Bad Gateway. What do you check first?**
A: Check if the upstream service (PHP-FPM, Node.js, etc.) is running. `sudo systemctl status php8.2-fpm` or `pm2 status`. Then check Nginx error log: `sudo tail /var/log/nginx/error.log`.

**Q: A file upload is failing with a blank error. What could be the cause?**
A: Most likely PHP's `upload_max_filesize` or `post_max_size` in `php.ini` is too low. Also check Nginx's `client_max_body_size`. After changing, restart PHP-FPM and reload Nginx.

**Q: A site was working yesterday but returns 403 today. What happened?**
A: Someone likely changed file ownership or permissions. Check: `ls -la /var/www/mysite/`. The files may now be owned by `root` instead of `www-data`. Fix: `sudo chown -R www-data:www-data /var/www/mysite`.

**Q: DNS is updated but the site still shows the old IP. What do you do?**
A: DNS propagation takes time. Clear local DNS cache, test from different locations using dnschecker.org, and wait (up to 48 hours for full propagation). Check with: `nslookup domain.com 8.8.8.8`.

---

---

# MINI LABS AND PRACTICE TASKS

---

## Lab 1: Your First Production Server

**Objective:** Set up a completely clean server with a working website.

**Tasks:**
1. Provision a Ubuntu 22.04 VPS (use any provider with free credits)
2. SSH in as root, create a new user `developer` with sudo access
3. Update all packages
4. Install Nginx
5. Configure firewall (allow 22, 80, 443)
6. Deploy a simple HTML page with your name and today's date
7. Point a domain or subdomain to the server
8. Install SSL with Certbot
9. Verify HTTPS works

**Success Criteria:**
- `https://yourdomain.com` shows your HTML page with a valid SSL lock

---

## Lab 2: Multiple Sites on One Server

**Objective:** Host 3 different sites on the same server.

**Tasks:**
1. Create three different directories: site1, site2, site3
2. Create a unique index.html for each
3. Configure Nginx server blocks for each
4. Use `/etc/hosts` on your local machine to test (or use subdomains)
5. Verify each domain shows its correct site

**Success Criteria:**
- site1.example.com → "This is Site 1"
- site2.example.com → "This is Site 2"
- site3.example.com → "This is Site 3"

---

## Lab 3: Laravel Full Deployment

**Objective:** Deploy a real Laravel project from GitHub.

**Tasks:**
1. Fork the laravel/laravel repository or use any Laravel project
2. Set up MySQL database and user
3. Clone the repository to `/var/www/laravel-demo`
4. Install Composer dependencies
5. Configure `.env`
6. Run migrations
7. Set permissions correctly
8. Configure Nginx server block
9. Test the application loads

**Success Criteria:**
- Laravel application loads at the domain
- No errors in storage/logs/laravel.log
- Database connection works

---

## Lab 4: Node.js API + Nginx Reverse Proxy

**Objective:** Deploy a Node.js API and access it through Nginx.

**Tasks:**
1. Create a simple Express.js app that returns JSON
2. Run it on port 3000
3. Configure Nginx to proxy port 80 → port 3000
4. Add SSL
5. Set up PM2 for the app
6. Test: `sudo reboot` then verify app is still running

**Success Criteria:**
- `curl https://your-api-domain.com/api/hello` returns JSON
- After server reboot, API still works without manual intervention

---

## Lab 5: The Debugging Challenge

**Objective:** Fix 5 deliberately broken configurations.

**Setup (ask your team lead to help or do yourself):**
1. Break 1: Change Nginx DocumentRoot to a non-existent path → causes 404
2. Break 2: Stop PHP-FPM → causes 502
3. Break 3: Change Laravel storage permissions to 000 → causes 500
4. Break 4: Set wrong database password in .env → causes 500
5. Break 5: Set upload_max_filesize to 2M → large upload fails

**Your Task:** Find and fix each issue using ONLY logs and diagnostic commands.

**Rules:**
- Don't look at what was changed
- Use only what you know: check logs, check status, check configs
- Document your debugging process for each

**Success Criteria:**
- All 5 issues fixed
- Written documentation showing your debugging thought process for each

---

## Lab 6: WordPress Security Hardening

**Objective:** Harden a fresh WordPress installation.

**Tasks:**
1. Install WordPress
2. Restrict access to `wp-login.php` in Nginx (allow only specific IPs)
3. Block access to `xmlrpc.php`
4. Set `DISALLOW_FILE_EDIT` in wp-config.php
5. Configure `wp-config.php` to 640 permissions
6. Block access to uploads directory from executing PHP
7. Add security headers to Nginx

**Success Criteria:**
- `https://yoursite.com/xmlrpc.php` returns 444 (no response)
- `https://yoursite.com/wp-login.php` is blocked from public IPs
- `https://securityheaders.com` scan shows good grade

---

## Lab 7: Automated Deployment Script

**Objective:** Create a deployment script for a Laravel app.

**Tasks:**
1. Write a bash script that:
   - Puts app in maintenance mode
   - Pulls latest code from Git
   - Runs composer install
   - Runs migrations
   - Clears and rebuilds caches
   - Takes the app out of maintenance mode
   - Logs completion with timestamp
2. Test it: make a change on a branch, push, run script

**Success Criteria:**
- Script runs without errors
- App is updated with new code
- No downtime beyond the maintenance window
- Deployment logged to a file

---

## Lab 8: Monitoring and Alerts

**Objective:** Set up basic server monitoring.

**Tasks:**
1. Sign up for UptimeRobot (free)
2. Add your domain for monitoring (5-minute checks)
3. Configure email alert
4. Test: Stop Nginx temporarily, see if alert arrives
5. Bonus: Install Netdata for detailed server metrics

**Success Criteria:**
- You receive an email/alert within 10 minutes of Nginx being stopped
- You can see CPU, memory, disk usage trends in Netdata

---

---

> **Congratulations — You have completed the WEBONICTECH Server & Deployment Handbook.**
>
> Remember: Every senior developer you admire was once confused by a 502 error or broken permissions.  
> The difference between junior and senior is simply: **time spent doing, debugging, and documenting.**
>
> Keep practicing. Keep reading logs. Keep asking questions.  
> The server is not your enemy — it just needs the right configuration.

---

*WEBONICTECH Internal Training Document — For Team Use Only*  
*Version 1.0 — 2026*
