# WEBONICTECH — SERVER HANDBOOK — PART 2
## Sections 5 through 8

---

# SECTION 5 — NODE.JS SERVER DEPLOYMENT

---

## 5.1 Understanding Node.js on a Server

Node.js is a **JavaScript runtime** that lets you run JavaScript code on the server (not just in the browser). It is built on Chrome's V8 engine and is known for being:
- **Non-blocking / asynchronous** — handles many requests without waiting
- **Fast** for I/O-heavy operations (APIs, real-time apps)
- **Popular** for REST APIs, real-time apps, Next.js, Express, Socket.io

**Important:** Node.js is NOT a web server like Apache or Nginx. It is an application runtime. You run your Node.js app on a port (e.g., 3000), and then Nginx acts as a reverse proxy in front of it.

```
Internet → Nginx (port 80/443) → Node.js App (port 3000)
```

---

## 5.2 Installing Node.js on Ubuntu

**NEVER use the default Ubuntu apt package for Node.js** — it's usually very outdated.

### Method 1: Using NodeSource (Recommended for Production)
```bash
# Install Node.js 20 LTS
curl -fsSL https://deb.nodesource.com/setup_20.x | sudo -E bash -
sudo apt install nodejs

# Verify installation
node -v
npm -v
```

### Method 2: Using NVM (Recommended for Development Flexibility)
```bash
# Install NVM (Node Version Manager)
curl -o- https://raw.githubusercontent.com/nvm-sh/nvm/v0.39.0/install.sh | bash

# Load NVM
source ~/.bashrc

# Install specific Node version
nvm install 20
nvm install 18
nvm use 20

# Set default version
nvm alias default 20

# List installed versions
nvm list
```

NVM is useful when you have multiple projects requiring different Node.js versions.

---

## 5.3 Understanding package.json

Every Node.js project has a `package.json` file. Think of it as the project's ID card.

```json
{
  "name": "my-node-app",
  "version": "1.0.0",
  "description": "My Express API",
  "main": "server.js",
  "scripts": {
    "start": "node server.js",
    "dev": "nodemon server.js",
    "build": "next build",
    "test": "jest"
  },
  "dependencies": {
    "express": "^4.18.2",
    "dotenv": "^16.0.3",
    "mongoose": "^7.0.0"
  },
  "devDependencies": {
    "nodemon": "^3.0.0"
  }
}
```

```bash
# Install all dependencies from package.json
npm install

# Install a specific package
npm install express

# Install a dev dependency (not needed in production)
npm install --save-dev nodemon

# Install packages for production (skips devDependencies)
npm install --production
# or
NODE_ENV=production npm install

# Run a script from package.json
npm start
npm run dev
npm run build
```

---

## 5.4 Environment Variables and .env Files

**NEVER hardcode database passwords or API keys in your code.**

```bash
# Install dotenv package
npm install dotenv

# Create .env file (in project root)
nano .env
```

**.env file example:**
```
NODE_ENV=production
PORT=3000
DB_HOST=localhost
DB_NAME=myapp_db
DB_USER=myapp_user
DB_PASS=supersecretpassword
JWT_SECRET=myjwtsecretkey123
REDIS_URL=redis://localhost:6379
```

**In your Node.js code:**
```javascript
// At the very top of your entry file
require('dotenv').config();

const port = process.env.PORT || 3000;
const dbHost = process.env.DB_HOST;
```

> ⚠️ **NEVER commit `.env` to Git.** Add it to `.gitignore`:
```bash
echo ".env" >> .gitignore
echo ".env.local" >> .gitignore
```

---

## 5.5 PM2 — The Production Process Manager

When you run `node server.js` directly, the process dies if:
- You close the terminal
- There's an uncaught exception
- The server reboots

**PM2** (Process Manager 2) solves all of these:
- Keeps your app running forever
- Restarts automatically on crashes
- Starts on server reboot
- Shows logs and monitoring
- Zero-downtime restarts

### Installing and Using PM2

```bash
# Install PM2 globally
sudo npm install -g pm2

# Start your application
pm2 start server.js --name "my-api"
pm2 start server.js --name "my-api" -i max   # Cluster mode (use all CPU cores)

# For Next.js
pm2 start npm --name "nextjs-app" -- start

# Start with specific environment
NODE_ENV=production pm2 start server.js --name "my-api"

# List all PM2 processes
pm2 list
pm2 status

# View logs
pm2 logs
pm2 logs my-api
pm2 logs my-api --lines 100

# Monitor in real-time
pm2 monit

# Restart an app
pm2 restart my-api

# Stop an app
pm2 stop my-api

# Delete from PM2
pm2 delete my-api

# Reload with zero downtime (graceful restart)
pm2 reload my-api
```

### Make PM2 Survive Server Reboots

```bash
# Generate startup script
pm2 startup
# It will print a command — RUN THAT COMMAND (it contains your username/path)
# Example: sudo env PATH=$PATH:/usr/bin /usr/lib/node_modules/pm2/bin/pm2 startup systemd -u ubuntu --hp /home/ubuntu

# After running the startup command, save current PM2 process list
pm2 save

# Now your apps will restart automatically after server reboot
```

### PM2 Ecosystem File (For Multiple Apps)

Create `ecosystem.config.js` in your project:

```javascript
module.exports = {
  apps: [
    {
      name: 'api-server',
      script: 'server.js',
      instances: 'max',
      exec_mode: 'cluster',
      env: {
        NODE_ENV: 'development',
        PORT: 3000
      },
      env_production: {
        NODE_ENV: 'production',
        PORT: 3000
      },
      error_file: '/var/log/pm2/api-error.log',
      out_file: '/var/log/pm2/api-out.log',
      log_date_format: 'YYYY-MM-DD HH:mm Z',
      watch: false,         // Don't watch for file changes in production
      max_memory_restart: '500M',  // Restart if memory exceeds 500MB
    },
    {
      name: 'worker',
      script: 'worker.js',
      instances: 1,
      env_production: {
        NODE_ENV: 'production'
      }
    }
  ]
};
```

```bash
# Start using ecosystem file
pm2 start ecosystem.config.js --env production

# Deploy updates
pm2 reload ecosystem.config.js --env production
```

---

## 5.6 Deploying an Express.js Application

### Step-by-step Express Deployment

```bash
# 1. SSH into the server
ssh ubuntu@your-server-ip

# 2. Create project directory
sudo mkdir -p /var/www/my-express-api
sudo chown ubuntu:ubuntu /var/www/my-express-api
cd /var/www/my-express-api

# 3. Clone from Git
git clone https://github.com/yourorg/my-express-api.git .

# 4. Install dependencies (production only)
npm install --production

# 5. Create .env file
nano .env
# (Paste your production environment variables)

# 6. Start with PM2
pm2 start server.js --name "my-express-api"

# 7. Save PM2 process list
pm2 save

# 8. Create Nginx reverse proxy config
sudo nano /etc/nginx/sites-available/my-express-api.conf
```

**Nginx config for Express:**
```nginx
server {
    listen 80;
    server_name api.mysite.com;

    location / {
        proxy_pass http://127.0.0.1:3000;
        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;
    }
}
```

```bash
# Enable site and add SSL
sudo ln -s /etc/nginx/sites-available/my-express-api.conf /etc/nginx/sites-enabled/
sudo nginx -t
sudo systemctl reload nginx
sudo certbot --nginx -d api.mysite.com
```

---

## 5.7 Deploying a Next.js Application

Next.js can be deployed in two ways:
1. **Static export** (`next export`) — Pure static HTML/CSS/JS → Serve with Nginx directly
2. **SSR/SSG mode** (`next start`) — Requires Node.js to run → Use PM2 + Nginx proxy

### Next.js SSR Deployment (Most Common)

```bash
# 1. Clone your project
cd /var/www
git clone https://github.com/yourorg/nextjs-app.git
cd nextjs-app

# 2. Install dependencies
npm install

# 3. Create .env.local (or .env.production)
nano .env.local
# NEXT_PUBLIC_API_URL=https://api.mysite.com
# DATABASE_URL=...

# 4. Build the application
npm run build

# 5. Start with PM2
pm2 start npm --name "nextjs-app" -- start
# This runs 'npm start' which runs 'next start'

# 6. Save PM2 config
pm2 save

# 7. Configure Nginx
sudo nano /etc/nginx/sites-available/nextjs-app.conf
```

**Nginx config for Next.js:**
```nginx
server {
    listen 80;
    server_name mysite.com www.mysite.com;

    location / {
        proxy_pass http://127.0.0.1:3000;
        proxy_http_version 1.1;
        proxy_set_header Upgrade $http_upgrade;
        proxy_set_header Connection 'upgrade';
        proxy_set_header Host $host;
        proxy_cache_bypass $http_upgrade;
        proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
        proxy_set_header X-Forwarded-Proto $scheme;
    }

    # Serve Next.js public folder directly
    location /public/ {
        alias /var/www/nextjs-app/public/;
    }
}
```

---

## 5.8 React Build Hosting

React is a **frontend framework** — it compiles down to static HTML/CSS/JavaScript. No server-side processing needed.

```bash
# Local build (on your machine or CI)
npm run build
# Creates a 'build/' folder with all static files

# On the server
sudo mkdir -p /var/www/react-app
# Upload build folder via scp or rsync
scp -r ./build/* ubuntu@your-server-ip:/var/www/react-app/

# Or deploy directly on server
cd /var/www
git clone https://github.com/yourorg/react-app.git
cd react-app
npm install
npm run build
# Files are now in /var/www/react-app/build/
```

**Nginx config for React:**
```nginx
server {
    listen 80;
    server_name myreactapp.com www.myreactapp.com;

    root /var/www/react-app/build;
    index index.html;

    # CRITICAL: Without this, React Router pages return 404 on refresh
    location / {
        try_files $uri $uri/ /index.html;
    }

    # Cache static assets aggressively
    location ~* \.(js|css|png|jpg|gif|ico|svg|woff2)$ {
        expires 1y;
        add_header Cache-Control "public, immutable";
    }
}
```

---

## 5.9 Node.js Application Logs

```bash
# View PM2 logs
pm2 logs
pm2 logs app-name --lines 200

# Clear PM2 logs
pm2 flush

# View log files directly
ls /root/.pm2/logs/
cat /root/.pm2/logs/my-app-out.log
tail -f /root/.pm2/logs/my-app-error.log

# In your Node.js code, use a proper logger
# Install Winston logger
npm install winston

# logger.js
const winston = require('winston');
const logger = winston.createLogger({
    level: 'info',
    format: winston.format.combine(
        winston.format.timestamp(),
        winston.format.json()
    ),
    transports: [
        new winston.transports.File({ filename: 'error.log', level: 'error' }),
        new winston.transports.File({ filename: 'combined.log' })
    ]
});
```

---

## 5.10 Common Node.js Deployment Issues

| Issue | Cause | Fix |
|-------|-------|-----|
| App exits after closing terminal | Not using PM2 | `pm2 start server.js` |
| App doesn't restart after reboot | PM2 startup not configured | `pm2 startup && pm2 save` |
| 502 from Nginx | Node.js app not running | `pm2 status`, `pm2 restart app` |
| Port already in use | Previous process still running | `pm2 delete app`, `pkill node` |
| Memory keeps growing | Memory leak in code | `pm2 start app --max-memory-restart 500M` |
| App crashes on null error | Uncaught exception | Add proper error handling, PM2 auto-restarts |

---

---

# SECTION 6 — PHP ECOSYSTEM

---

## 6.1 PHP on the Server

PHP is the backbone of most web applications — WordPress, Laravel, CodeIgniter are all PHP-based. On a production server, PHP runs in one of two ways:

1. **mod_php (Apache module):** PHP runs inside Apache. Simpler but less efficient.
2. **PHP-FPM (FastCGI Process Manager):** PHP runs as a separate service. Used with Nginx and modern Apache setups. Much more efficient.

```bash
# Install PHP 8.2 and common extensions
sudo apt update
sudo apt install php8.2 php8.2-fpm php8.2-mysql php8.2-curl php8.2-gd \
    php8.2-mbstring php8.2-xml php8.2-zip php8.2-bcmath php8.2-intl \
    php8.2-imagick php8.2-redis php8.2-opcache

# Check PHP version
php -v

# Check FPM status
sudo systemctl status php8.2-fpm

# View PHP configuration
php -i
php --ini   # Shows which php.ini file is loaded
```

---

## 6.2 PHP Versions and Multiple PHP Versions

Many client servers have mixed projects. You may need PHP 7.4 for an old project and PHP 8.2 for a new one.

```bash
# Add repository for multiple PHP versions
sudo add-apt-repository ppa:ondrej/php
sudo apt update

# Install multiple versions
sudo apt install php7.4 php7.4-fpm php7.4-mysql
sudo apt install php8.0 php8.0-fpm
sudo apt install php8.2 php8.2-fpm

# Switch CLI default PHP version
sudo update-alternatives --config php

# For Apache: enable specific PHP module
sudo a2dismod php7.4
sudo a2enmod php8.2
sudo systemctl restart apache2

# For Nginx: use different PHP-FPM sockets per site
# In Nginx server block:
# fastcgi_pass unix:/run/php/php8.2-fpm.sock;  (for a PHP 8.2 site)
# fastcgi_pass unix:/run/php/php7.4-fpm.sock;  (for a PHP 7.4 site)
```

---

## 6.3 PHP Configuration — php.ini

`php.ini` is the main PHP configuration file. Location:

```bash
# Find php.ini location
php -i | grep "Loaded Configuration File"
# Output: /etc/php/8.2/fpm/php.ini (for FPM)
# Output: /etc/php/8.2/cli/php.ini (for CLI/terminal)
# Output: /etc/php/8.2/apache2/php.ini (for Apache module)

sudo nano /etc/php/8.2/fpm/php.ini
```

**Critical settings to adjust:**
```ini
; Maximum file upload size
upload_max_filesize = 64M

; Maximum POST data size (must be >= upload_max_filesize)
post_max_size = 64M

; Maximum memory a single PHP process can use
memory_limit = 256M

; Maximum script execution time (seconds)
max_execution_time = 300

; Maximum time for input processing
max_input_time = 300

; Maximum number of input variables (for large forms)
max_input_vars = 3000

; Error display (MUST be off on production)
display_errors = Off
display_startup_errors = Off

; Error logging (should be On on production)
log_errors = On
error_log = /var/log/php/php-error.log

; PHP timezone
date.timezone = "Asia/Kolkata"
```

After editing php.ini:
```bash
sudo systemctl restart php8.2-fpm
sudo systemctl reload apache2  # if using Apache
```

---

## 6.4 PHP-FPM Configuration

PHP-FPM is a separate service that processes PHP scripts for Nginx (and modern Apache).

```bash
# Main FPM config
sudo nano /etc/php/8.2/fpm/php-fpm.conf

# Pool configuration (the important part)
sudo nano /etc/php/8.2/fpm/pool.d/www.conf
```

**Key pool settings:**
```ini
; The FPM pool name
[www]

; Run as www-data user
user = www-data
group = www-data

; FPM socket location (Nginx uses this)
listen = /run/php/php8.2-fpm.sock

; Process management type
; dynamic: Adjusts number of processes based on load
pm = dynamic

; Maximum number of PHP processes
pm.max_children = 50

; Number of processes kept running at startup
pm.start_servers = 5

; Minimum idle processes
pm.min_spare_servers = 5

; Maximum idle processes
pm.max_spare_servers = 35

; Slow request logging
slowlog = /var/log/php/php8.2-fpm-slow.log
request_slowlog_timeout = 5s
```

---

## 6.5 Composer — PHP Dependency Manager

Composer is to PHP what npm is to Node.js. Laravel, Symfony, and modern PHP apps use it.

```bash
# Install Composer
cd ~
curl -sS https://getcomposer.org/installer -o composer-setup.php
sudo php composer-setup.php --install-dir=/usr/local/bin --filename=composer

# Verify installation
composer --version

# Install dependencies from composer.json
composer install

# Production install (no dev dependencies, faster)
composer install --no-dev --optimize-autoloader

# Add a package
composer require guzzlehttp/guzzle

# Update packages
composer update

# Dump autoloader (regenerate autoload files after changes)
composer dump-autoload --optimize
```

---

## 6.6 Laravel Production Deployment

### Prerequisites
- PHP 8.1+ with required extensions
- Composer
- MySQL or PostgreSQL
- Nginx or Apache2

### Complete Laravel Deployment Steps

```bash
# 1. Create project directory
sudo mkdir -p /var/www/mylaravelapp
sudo chown ubuntu:ubuntu /var/www/mylaravelapp
cd /var/www/mylaravelapp

# 2. Clone repository
git clone https://github.com/yourorg/laravel-app.git .

# 3. Install PHP dependencies (no dev packages in production)
composer install --no-dev --optimize-autoloader

# 4. Create .env from example
cp .env.example .env

# 5. Edit .env with production values
nano .env
```

**Production .env for Laravel:**
```
APP_NAME="My Laravel App"
APP_ENV=production
APP_KEY=         # Will be generated in next step
APP_DEBUG=false  # CRITICAL: Never true in production
APP_URL=https://mylaravelapp.com

DB_CONNECTION=mysql
DB_HOST=127.0.0.1
DB_PORT=3306
DB_DATABASE=mylaravelapp
DB_USERNAME=mylaravel_user
DB_PASSWORD=strongpassword

CACHE_DRIVER=file
QUEUE_CONNECTION=database
SESSION_DRIVER=file

MAIL_MAILER=smtp
MAIL_HOST=smtp.mailtrap.io
MAIL_PORT=587
```

```bash
# 6. Generate application key
php artisan key:generate

# 7. Run database migrations
php artisan migrate --force

# 8. Seed database (if needed)
php artisan db:seed --force

# 9. Set correct permissions
sudo chown -R www-data:www-data /var/www/mylaravelapp
sudo chmod -R 755 /var/www/mylaravelapp
sudo chmod -R 775 /var/www/mylaravelapp/storage
sudo chmod -R 775 /var/www/mylaravelapp/bootstrap/cache

# 10. Create storage symlink
php artisan storage:link

# 11. Cache for performance
php artisan config:cache
php artisan route:cache
php artisan view:cache

# 12. Configure web server (see Section 3 or 4 for VirtualHost/server block)
```

### Laravel artisan Commands for Daily Use

```bash
# Clear all caches
php artisan cache:clear
php artisan config:clear
php artisan route:clear
php artisan view:clear

# Optimize for production
php artisan optimize

# Run database migrations
php artisan migrate
php artisan migrate --force  # Use --force on production

# Rollback last migration
php artisan migrate:rollback

# Check routes
php artisan route:list

# Create a controller
php artisan make:controller UserController

# Create a model + migration
php artisan make:model Product -m

# Maintenance mode (shows maintenance page to visitors)
php artisan down
php artisan up

# Run queue workers (process background jobs)
php artisan queue:work
php artisan queue:work --daemon
```

### Laravel Queue Worker with Supervisor

For processing queued jobs (emails, notifications), you need a queue worker running in the background. Use **Supervisor** to manage it.

```bash
# Install Supervisor
sudo apt install supervisor

# Create worker config
sudo nano /etc/supervisor/conf.d/laravel-worker.conf
```

```ini
[program:laravel-worker]
process_name=%(program_name)s_%(process_num)02d
command=php /var/www/mylaravelapp/artisan queue:work database --sleep=3 --tries=3 --max-time=3600
autostart=true
autorestart=true
stopasgroup=true
killasgroup=true
user=www-data
numprocs=2
redirect_stderr=true
stdout_logfile=/var/www/mylaravelapp/storage/logs/worker.log
stopwaitsecs=3600
```

```bash
# Reload Supervisor
sudo supervisorctl reread
sudo supervisorctl update
sudo supervisorctl start laravel-worker:*

# Check worker status
sudo supervisorctl status
```

### Laravel Cron Setup

```bash
# Edit root crontab
sudo crontab -e

# Add Laravel scheduler (runs every minute)
* * * * * cd /var/www/mylaravelapp && php artisan schedule:run >> /dev/null 2>&1
```

---

## 6.7 WordPress Server Setup

### Complete WordPress Deployment

```bash
# 1. Create database
mysql -u root -p
```
```sql
CREATE DATABASE wordpress_db CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci;
CREATE USER 'wp_user'@'localhost' IDENTIFIED BY 'strongpassword';
GRANT ALL PRIVILEGES ON wordpress_db.* TO 'wp_user'@'localhost';
FLUSH PRIVILEGES;
EXIT;
```

```bash
# 2. Download WordPress
cd /tmp
wget https://wordpress.org/latest.tar.gz
tar -xzvf latest.tar.gz

# 3. Move to web root
sudo mv wordpress /var/www/mywordpresssite

# 4. Configure wp-config.php
cd /var/www/mywordpresssite
cp wp-config-sample.php wp-config.php
nano wp-config.php
```

**Key wp-config.php settings:**
```php
define('DB_NAME', 'wordpress_db');
define('DB_USER', 'wp_user');
define('DB_PASSWORD', 'strongpassword');
define('DB_HOST', 'localhost');
define('DB_CHARSET', 'utf8mb4');

// Security keys (generate at https://api.wordpress.org/secret-key/1.1/salt/)
define('AUTH_KEY', 'your-unique-phrase-here');
define('SECURE_AUTH_KEY', 'your-unique-phrase-here');
// ... etc

// If behind SSL proxy
define('FORCE_SSL_ADMIN', true);
if (strpos($_SERVER['HTTP_X_FORWARDED_PROTO'], 'https') !== false) {
    $_SERVER['HTTPS'] = 'on';
}

// Increase PHP memory for WordPress
define('WP_MEMORY_LIMIT', '256M');

// Disable file editing from admin panel (security)
define('DISALLOW_FILE_EDIT', true);

// Disable auto-updates (manage manually in production)
define('AUTOMATIC_UPDATER_DISABLED', true);
```

```bash
# 5. Set permissions
sudo chown -R www-data:www-data /var/www/mywordpresssite
sudo chmod -R 755 /var/www/mywordpresssite
sudo chmod 644 /var/www/mywordpresssite/wp-config.php
```

---

## 6.8 CodeIgniter Deployment

```bash
# 1. Upload/clone project
cd /var/www
git clone https://github.com/yourorg/ci-project.git

# 2. For CodeIgniter 3
# Edit application/config/config.php
nano application/config/config.php
# Set base_url
$config['base_url'] = 'https://mycisite.com/';

# Edit application/config/database.php
nano application/config/database.php

# For CodeIgniter 4
nano app/Config/App.php
# Set $baseURL = 'https://mycisite.com/';
nano app/Config/Database.php

# 3. Set permissions
sudo chown -R www-data:www-data /var/www/ci-project
sudo chmod -R 755 /var/www/ci-project
sudo chmod -R 775 /var/www/ci-project/application/cache
sudo chmod -R 775 /var/www/ci-project/application/logs
```

**Apache VirtualHost for CodeIgniter 4:**
```apache
<VirtualHost *:80>
    ServerName mycisite.com
    DocumentRoot /var/www/ci-project/public

    <Directory /var/www/ci-project/public>
        AllowOverride All
        Require all granted
    </Directory>
</VirtualHost>
```

---

## 6.9 PHP OPcache

OPcache speeds up PHP by caching compiled bytecode in memory — your PHP scripts don't need to be recompiled on every request.

```bash
# Check if OPcache is enabled
php -r "echo (function_exists('opcache_get_status') ? 'OPcache enabled' : 'OPcache disabled');"

# Edit PHP configuration
sudo nano /etc/php/8.2/fpm/php.ini
```

```ini
[opcache]
opcache.enable=1
opcache.enable_cli=0
opcache.memory_consumption=256
opcache.interned_strings_buffer=64
opcache.max_accelerated_files=20000
opcache.revalidate_freq=0     ; 0 = don't check for file changes (production)
opcache.validate_timestamps=0  ; 0 = always use cached version (production)
opcache.fast_shutdown=1
opcache.huge_code_pages=1
```

> **Important:** With `validate_timestamps=0`, changes to PHP files won't be reflected until OPcache is cleared. After deploying, run:
```bash
# Clear OPcache via Laravel artisan
php artisan opcache:clear

# Or via PHP script
php -r "opcache_reset();"

# Or restart PHP-FPM
sudo systemctl reload php8.2-fpm
```

---

---

# SECTION 7 — DATABASE MANAGEMENT

---

## 7.1 MySQL — The Standard for PHP Apps

MySQL is the world's most popular open-source relational database. MariaDB is a drop-in replacement (slightly improved fork).

```bash
# Install MySQL
sudo apt install mysql-server

# Install MariaDB (alternative)
sudo apt install mariadb-server

# Secure the installation (run immediately after install)
sudo mysql_secure_installation
# Follow prompts:
# - Set root password: YES
# - Remove anonymous users: YES
# - Disallow remote root login: YES
# - Remove test database: YES
# - Reload privilege tables: YES

# Login to MySQL
sudo mysql               # As system root (uses auth_socket)
mysql -u root -p         # With password

# Check version
mysql --version
```

---

## 7.2 MySQL — Essential Commands

```sql
-- Show all databases
SHOW DATABASES;

-- Create a database
CREATE DATABASE myapp_db CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci;

-- Select a database to use
USE myapp_db;

-- Show tables
SHOW TABLES;

-- Describe a table structure
DESCRIBE users;

-- Create a user
CREATE USER 'myapp_user'@'localhost' IDENTIFIED BY 'strongpassword123';

-- Grant permissions
GRANT ALL PRIVILEGES ON myapp_db.* TO 'myapp_user'@'localhost';
GRANT SELECT, INSERT, UPDATE, DELETE ON myapp_db.* TO 'myapp_user'@'localhost';
FLUSH PRIVILEGES;

-- Show user permissions
SHOW GRANTS FOR 'myapp_user'@'localhost';

-- Delete a user
DROP USER 'myapp_user'@'localhost';

-- Delete a database (CAREFUL!)
DROP DATABASE myapp_db;

-- Change user password
ALTER USER 'myapp_user'@'localhost' IDENTIFIED BY 'newpassword';
```

---

## 7.3 Database Backup and Restore

**This is critical.** Always backup before any deployment or database change.

### Backup (mysqldump)
```bash
# Backup a single database
mysqldump -u root -p myapp_db > /backup/myapp_db_$(date +%Y%m%d_%H%M%S).sql

# Backup with compression
mysqldump -u root -p myapp_db | gzip > /backup/myapp_db_$(date +%Y%m%d).sql.gz

# Backup all databases
mysqldump -u root -p --all-databases > /backup/all_databases_$(date +%Y%m%d).sql

# Backup specific tables
mysqldump -u root -p myapp_db users orders > /backup/users_orders.sql

# Backup from remote server
mysqldump -h remote-server-ip -u myapp_user -p myapp_db > backup.sql
```

### Restore
```bash
# Restore from SQL file
mysql -u root -p myapp_db < /backup/myapp_db_20260101.sql

# Restore from compressed file
gunzip < /backup/myapp_db_20260101.sql.gz | mysql -u root -p myapp_db

# Restore to a fresh database
mysql -u root -p -e "CREATE DATABASE myapp_db_restore;"
mysql -u root -p myapp_db_restore < /backup/myapp_db_20260101.sql
```

### Automated Daily Backup Script
```bash
# Create backup script
sudo nano /usr/local/bin/backup_databases.sh
```

```bash
#!/bin/bash
# Daily MySQL Backup Script

BACKUP_DIR="/backup/mysql"
DATE=$(date +%Y%m%d_%H%M%S)
MYSQL_USER="backup_user"
MYSQL_PASS="backuppassword"
KEEP_DAYS=7

# Create backup directory
mkdir -p $BACKUP_DIR

# Backup each database
for DB in $(mysql -u$MYSQL_USER -p$MYSQL_PASS -e "SHOW DATABASES;" | grep -Ev "(Database|information_schema|performance_schema|sys)"); do
    mysqldump -u$MYSQL_USER -p$MYSQL_PASS $DB | gzip > $BACKUP_DIR/${DB}_${DATE}.sql.gz
    echo "Backed up: $DB"
done

# Delete backups older than KEEP_DAYS days
find $BACKUP_DIR -name "*.sql.gz" -mtime +$KEEP_DAYS -delete
echo "Cleanup complete. Backups older than $KEEP_DAYS days deleted."
```

```bash
# Make executable
sudo chmod +x /usr/local/bin/backup_databases.sh

# Add to cron (run daily at 2 AM)
sudo crontab -e
# Add: 0 2 * * * /usr/local/bin/backup_databases.sh >> /var/log/db_backup.log 2>&1
```

---

## 7.4 phpMyAdmin Setup

phpMyAdmin is a web-based GUI for MySQL management.

```bash
# Install phpMyAdmin
sudo apt install phpmyadmin

# During installation:
# - Select apache2 or none (for Nginx)
# - Configure with dbconfig-common: Yes

# For Nginx, create a symlink
sudo ln -s /usr/share/phpmyadmin /var/www/html/phpmyadmin

# Or add a dedicated Nginx server block
```

**Nginx server block for phpMyAdmin:**
```nginx
server {
    listen 80;
    server_name db.mysite.com;

    root /usr/share/phpmyadmin;
    index index.php;

    # Restrict access by IP (security)
    allow 203.0.113.10;  # Your office IP
    deny all;

    location / {
        try_files $uri $uri/ =404;
    }

    location ~ \.php$ {
        fastcgi_pass unix:/run/php/php8.2-fpm.sock;
        fastcgi_index index.php;
        fastcgi_param SCRIPT_FILENAME $document_root$fastcgi_script_name;
        include fastcgi_params;
    }
}
```

> ⚠️ **Security:** Never expose phpMyAdmin to the public. Either restrict by IP, use a non-obvious URL, or use SSH tunneling to access it locally.

---

## 7.5 MySQL Optimization Basics

```sql
-- Find slow queries
SHOW PROCESSLIST;
SHOW STATUS LIKE 'Slow_queries';

-- Check table indexes
SHOW INDEX FROM users;

-- Add an index (critical for performance on large tables)
ALTER TABLE users ADD INDEX idx_email (email);
ALTER TABLE orders ADD INDEX idx_user_id (user_id);
ALTER TABLE orders ADD INDEX idx_created_at (created_at);

-- Analyze query performance
EXPLAIN SELECT * FROM orders WHERE user_id = 123;
-- Look for "type = ALL" (full table scan) — needs an index

-- Check table sizes
SELECT 
    table_name,
    ROUND(data_length/1024/1024, 2) AS data_mb,
    ROUND(index_length/1024/1024, 2) AS index_mb
FROM information_schema.tables
WHERE table_schema = 'myapp_db'
ORDER BY data_length DESC;
```

**MySQL Configuration (`/etc/mysql/mysql.conf.d/mysqld.cnf`):**
```ini
[mysqld]
# Buffer pool (set to 70% of available RAM for dedicated DB server)
innodb_buffer_pool_size = 1G

# Log slow queries
slow_query_log = 1
slow_query_log_file = /var/log/mysql/slow.log
long_query_time = 2  # Log queries taking more than 2 seconds

# Maximum connections
max_connections = 200

# Character set
character_set_server = utf8mb4
collation_server = utf8mb4_unicode_ci
```

---

## 7.6 Redis — In-Memory Cache/Session Store

Redis is used for:
- **Caching** (reduce database load)
- **Sessions** (fast session storage for PHP/Node.js)
- **Queue backend** (Laravel queues)
- **Real-time features** (pub/sub)

```bash
# Install Redis
sudo apt install redis-server

# Start and enable
sudo systemctl start redis-server
sudo systemctl enable redis-server

# Test Redis
redis-cli ping
# Should return: PONG

# Set a value
redis-cli set mykey "hello"
redis-cli get mykey

# Configure Redis password (recommended)
sudo nano /etc/redis/redis.conf
# Find: # requirepass foobared
# Change to: requirepass yourStrongRedisPassword

sudo systemctl restart redis-server
```

**Configure Laravel to use Redis:**
```
# In .env
CACHE_DRIVER=redis
SESSION_DRIVER=redis
QUEUE_CONNECTION=redis
REDIS_HOST=127.0.0.1
REDIS_PASSWORD=yourStrongRedisPassword
REDIS_PORT=6379
```

---

## 7.7 MongoDB — NoSQL Database

MongoDB stores data as **documents** (like JSON) instead of rows in tables.

```bash
# Install MongoDB
curl -fsSL https://www.mongodb.org/static/pgp/server-7.0.asc | sudo apt-key add -
echo "deb [ arch=amd64,arm64 ] https://repo.mongodb.org/apt/ubuntu jammy/mongodb-org/7.0 multiverse" | sudo tee /etc/apt/sources.list.d/mongodb-org-7.0.list
sudo apt update
sudo apt install mongodb-org

# Start MongoDB
sudo systemctl start mongod
sudo systemctl enable mongod

# Access MongoDB shell
mongosh

# Basic MongoDB commands
use myapp_db       # Switch to (or create) database
db.createCollection("users")
db.users.insertOne({ name: "John", email: "john@example.com" })
db.users.find()
db.users.findOne({ email: "john@example.com" })
db.users.updateOne({ email: "john@example.com" }, { $set: { age: 30 } })
db.users.deleteOne({ email: "john@example.com" })
```

### SQL vs NoSQL — When to Use Which

| Use SQL (MySQL) When | Use NoSQL (MongoDB) When |
|----------------------|--------------------------|
| Data is structured and relational | Data structure varies (flexible schema) |
| Strong consistency needed | Horizontal scalability needed |
| Complex JOINs required | Document-based data (like JSON) |
| Financial/transactional data | Real-time analytics, logging |
| WordPress, Laravel, e-commerce | Chat apps, content management, IoT |
| You need ACID compliance | You need high-speed reads/writes |

---

---

# SECTION 8 — WORDPRESS SERVER MANAGEMENT

---

## 8.1 Understanding WordPress File Structure

```
/var/www/mywordpresssite/
├── wp-admin/               ← WordPress admin panel files (don't modify)
├── wp-includes/            ← WordPress core files (don't modify)
├── wp-content/             ← YOUR CONTENT LIVES HERE
│   ├── themes/             ← All themes
│   │   ├── mytheme/        ← Your custom/active theme
│   │   └── twentytwenty/   ← Default themes
│   ├── plugins/            ← All plugins
│   │   ├── contact-form-7/
│   │   └── woocommerce/
│   ├── uploads/            ← All media files (images, PDFs)
│   │   └── 2026/
│   │       └── 01/
│   │           └── myimage.jpg
│   └── mu-plugins/         ← Must-Use plugins (always active)
├── wp-config.php           ← Main configuration (database, keys)
├── .htaccess               ← URL rewrites (Apache)
└── index.php               ← Entry point
```

---

## 8.2 WordPress File Permissions

```bash
# Correct WordPress permissions
sudo chown -R www-data:www-data /var/www/mywordpresssite

# Directories: 755
find /var/www/mywordpresssite -type d -exec chmod 755 {} \;

# Files: 644
find /var/www/mywordpresssite -type f -exec chmod 644 {} \;

# wp-config.php: more restrictive
chmod 600 /var/www/mywordpresssite/wp-config.php
# OR
chmod 640 /var/www/mywordpresssite/wp-config.php

# Uploads folder needs www-data write access
chmod 755 /var/www/mywordpresssite/wp-content/uploads
```

> ⚠️ **Warning:** If you set `uploads` to 777, malicious files can be uploaded and executed. Always keep it 755 and owned by www-data.

---

## 8.3 WordPress wp-config.php Security

```php
// Prevent direct access to config
if (!defined('ABSPATH')) {
    exit;
}

// Move wp-config.php one level up (outside web root)
// WordPress will automatically find it at /var/www/wp-config.php
// Even if document root is /var/www/mysite

// Disable file editing from admin dashboard
define('DISALLOW_FILE_EDIT', true);

// Disable file modifications entirely
define('DISALLOW_FILE_MODS', true);  // Prevents plugin/theme installs too

// Force SSL for admin
define('FORCE_SSL_ADMIN', true);

// Debug mode — NEVER true on production
define('WP_DEBUG', false);
define('WP_DEBUG_LOG', false);
define('WP_DEBUG_DISPLAY', false);
```

---

## 8.4 WordPress Performance Optimization

### Enable Caching
The #1 performance improvement for WordPress.

```bash
# Install W3 Total Cache or WP Super Cache via admin
# Or use WP Rocket (paid, excellent)

# For Redis object caching
sudo apt install php8.2-redis
# Install Redis Object Cache plugin from WordPress admin
```

### Nginx FastCGI Cache for WordPress

```nginx
# Add to nginx.conf http block
fastcgi_cache_path /tmp/nginx_cache levels=1:2 keys_zone=WORDPRESS:100m inactive=60m;
fastcgi_cache_key "$scheme$request_method$host$request_uri";

server {
    listen 80;
    server_name mywordpresssite.com;
    root /var/www/mywordpresssite;
    index index.php;

    # Cache bypass conditions (logged in users, POST requests)
    set $skip_cache 0;
    if ($request_method = POST) { set $skip_cache 1; }
    if ($query_string != "") { set $skip_cache 1; }
    if ($request_uri ~* "/wp-admin/|/wp-login.php") { set $skip_cache 1; }
    if ($cookie_logged_in_* != "") { set $skip_cache 1; }
    if ($cookie_wordpressuser_* != "") { set $skip_cache 1; }

    location / {
        try_files $uri $uri/ /index.php?$args;
    }

    location ~ \.php$ {
        fastcgi_pass unix:/run/php/php8.2-fpm.sock;
        fastcgi_index index.php;
        fastcgi_param SCRIPT_FILENAME $document_root$fastcgi_script_name;
        include fastcgi_params;

        fastcgi_cache WORDPRESS;
        fastcgi_cache_valid 200 60m;
        fastcgi_cache_bypass $skip_cache;
        fastcgi_no_cache $skip_cache;
        add_header X-FastCGI-Cache $upstream_cache_status;
    }
}
```

---

## 8.5 WordPress Migration

Moving a WordPress site from one server to another (or local to production):

### Method 1: Manual Migration

```bash
# On OLD server:
# Step 1: Backup files
tar -czvf wordpress_files.tar.gz /var/www/mywordpresssite/

# Step 2: Export database
mysqldump -u root -p wordpress_db > wordpress_db.sql

# Transfer files to new server
scp wordpress_files.tar.gz ubuntu@new-server-ip:/tmp/
scp wordpress_db.sql ubuntu@new-server-ip:/tmp/
```

```bash
# On NEW server:
# Step 1: Extract files
tar -xzvf /tmp/wordpress_files.tar.gz -C /var/www/

# Step 2: Create database
mysql -u root -p
```
```sql
CREATE DATABASE wordpress_db CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci;
CREATE USER 'wp_user'@'localhost' IDENTIFIED BY 'newpassword';
GRANT ALL PRIVILEGES ON wordpress_db.* TO 'wp_user'@'localhost';
FLUSH PRIVILEGES;
```
```bash
mysql -u root -p wordpress_db < /tmp/wordpress_db.sql
```

**Update URLs in database (critical when moving domains):**
```bash
# Install WP-CLI
curl -O https://raw.githubusercontent.com/wp-cli/builds/gh-pages/phar/wp-cli.phar
chmod +x wp-cli.phar
sudo mv wp-cli.phar /usr/local/bin/wp

# Search and replace old URL with new URL
cd /var/www/mywordpresssite
sudo -u www-data wp search-replace 'https://old-domain.com' 'https://new-domain.com' --all-tables

# Update wp-config.php with new database credentials
```

### Method 2: Using All-in-One WP Migration Plugin (Easiest for Freshers)
1. Install "All-in-One WP Migration" plugin on old site
2. Export site (creates a .wpress file)
3. Install same plugin on new site
4. Import the .wpress file

---

## 8.6 Common WordPress Errors and Fixes

### White Screen of Death (WSOD)
```
Symptom: Blank white screen, nothing loads
Causes: PHP fatal error, memory exhaustion, plugin conflict

Fix:
1. Enable debug mode temporarily:
   In wp-config.php, set:
   define('WP_DEBUG', true);
   define('WP_DEBUG_LOG', true);
   define('WP_DEBUG_DISPLAY', false);
   
   Then check: /var/www/mysite/wp-content/debug.log

2. Increase PHP memory:
   define('WP_MEMORY_LIMIT', '512M');
   In php.ini: memory_limit = 512M

3. Disable all plugins via CLI or database:
   # Via CLI
   sudo -u www-data wp plugin deactivate --all
   
   # Via database
   mysql -u root -p wordpress_db
   UPDATE wp_options SET option_value = '' WHERE option_name = 'active_plugins';
```

### Error Establishing Database Connection
```
Symptom: "Error establishing a database connection"
Causes: Wrong DB credentials, MySQL not running, corrupted DB

Fix:
1. Check MySQL is running: sudo systemctl status mysql
2. Verify credentials in wp-config.php
3. Test connection: mysql -u wp_user -p wordpress_db
4. Check MySQL logs: sudo tail /var/log/mysql/error.log
```

### WordPress Stuck in Maintenance Mode
```
Symptom: "Briefly unavailable for scheduled maintenance" forever
Cause: Failed update left .maintenance file

Fix:
rm /var/www/mywordpresssite/.maintenance
```

### 413 Request Entity Too Large (Uploads Fail)
```
Symptom: Cannot upload large files/images
Cause: Nginx or PHP limits too low

Fix in Nginx:
client_max_body_size 64M;  # Add to server block or http block

Fix in php.ini:
upload_max_filesize = 64M
post_max_size = 64M

Then restart:
sudo systemctl reload nginx
sudo systemctl restart php8.2-fpm
```

### WordPress Redirect Loop
```
Symptom: ERR_TOO_MANY_REDIRECTS
Causes:
1. Wrong siteurl/home in database
2. HTTP/HTTPS redirect conflict

Fix:
# Check URLs in database
mysql -u root -p wordpress_db
SELECT option_name, option_value FROM wp_options WHERE option_name IN ('siteurl','home');

# Update if wrong
UPDATE wp_options SET option_value = 'https://mysite.com' WHERE option_name = 'siteurl';
UPDATE wp_options SET option_value = 'https://mysite.com' WHERE option_name = 'home';

# If behind Nginx proxy, add to wp-config.php:
define('FORCE_SSL_ADMIN', true);
if ($_SERVER['HTTP_X_FORWARDED_PROTO'] == 'https') {
    $_SERVER['HTTPS'] = 'on';
}
```

---

## 8.7 WordPress Security Hardening

```bash
# 1. Change database table prefix (do this during installation)
# In wp-config.php: $table_prefix = 'wp_x7k2_';  (instead of wp_)

# 2. Block xmlrpc.php (common attack vector)
# In Nginx server block:
location = /xmlrpc.php {
    deny all;
    return 444;
}

# 3. Block wp-login.php from suspicious IPs
# Or use Fail2ban (see Security section)

# 4. Disable directory browsing
# In .htaccess: Options -Indexes

# 5. Protect wp-config.php
# In .htaccess:
<Files wp-config.php>
Order allow,deny
Deny from all
</Files>

# 6. Install a security plugin
# Wordfence or Sucuri Security (free tiers available)
```

---

## 8.8 WordPress CLI (WP-CLI) Cheat Sheet

```bash
# Check WordPress version
sudo -u www-data wp core version

# Update WordPress core
sudo -u www-data wp core update

# List plugins
sudo -u www-data wp plugin list

# Activate/deactivate plugin
sudo -u www-data wp plugin activate contact-form-7
sudo -u www-data wp plugin deactivate bad-plugin

# Update all plugins
sudo -u www-data wp plugin update --all

# List themes
sudo -u www-data wp theme list

# Create admin user
sudo -u www-data wp user create newadmin admin@example.com --role=administrator --user_pass=securepassword

# Reset user password
sudo -u www-data wp user update 1 --user_pass=newpassword

# Flush rewrite rules (fix permalink issues)
sudo -u www-data wp rewrite flush

# Clear all caches
sudo -u www-data wp cache flush

# Database operations
sudo -u www-data wp db check
sudo -u www-data wp db optimize
sudo -u www-data wp db export backup.sql
```

---

> **End of Part 2 — Sections 5 to 8**  
> Continue to Part 3 for Sections 9 through 12.
