Loading
Please wait...

Laravel E-Learning Rebuild

1

Part 1: Introduction & Why I'm Upgrading

2

Part 2: Setting Up Laravel 11 + Filament

3

Part 3: Database Schema Design

4

Part 4: Authentication & User Roles

5

Part 5: Building the Exam System

6

Part 6: Real-Time Features

7

Part 7: Testing Strategy

11 Januari 2026

Laravel E-Learning Part 8: Production Deployment

Complete guide to deploying our Laravel e-learning platform to production using Ubuntu VPS, NGINX, MySQL, and automated deployment with GitHub Actions.

5 min read


Deployment Overview

We're deploying to a Ubuntu 22.04 VPS with:

  • NGINX as reverse proxy
  • PHP 8.3 with PHP-FPM
  • MySQL 8.0
  • Let's Encrypt SSL
  • Supervisor for queue workers
  • GitHub Actions for CI/CD

Step 1: Server Initial Setup

SSH into your server:

ssh root@your-server-ip

Update and install essential packages:

apt update && apt upgrade -y
apt install -y nginx mysql-server curl git unzip supervisor ufw

Configure Firewall

ufw allow OpenSSH
ufw allow 'Nginx Full'
ufw enable

Step 2: Install PHP 8.3

apt install -y software-properties-common
add-apt-repository ppa:ondrej/php -y
apt update

apt install -y php8.3 php8.3-fpm php8.3-cli php8.3-common \
    php8.3-mysql php8.3-zip php8.3-gd php8.3-mbstring \
    php8.3-curl php8.3-xml php8.3-bcmath php8.3-intl \
    php8.3-redis

Verify installation:

php -v
# PHP 8.3.x

Step 3: Install Composer

curl -sS https://getcomposer.org/installer | php
mv composer.phar /usr/local/bin/composer
chmod +x /usr/local/bin/composer

Step 4: Configure MySQL

Secure MySQL installation:

mysql_secure_installation

Create database and user:

mysql -u root -p
CREATE DATABASE elearning_production;
CREATE USER 'elearning'@'localhost' IDENTIFIED BY 'your_strong_password';
GRANT ALL PRIVILEGES ON elearning_production.* TO 'elearning'@'localhost';
FLUSH PRIVILEGES;
EXIT;

Step 5: Configure NGINX

Create site configuration /etc/nginx/sites-available/elearning:

server {
    listen 80;
    listen [::]:80;
    server_name elearning.yourdomain.com;
    root /var/www/elearning/public;

    add_header X-Frame-Options "SAMEORIGIN";
    add_header X-Content-Type-Options "nosniff";
    add_header X-XSS-Protection "1; mode=block";

    index index.php;
    charset utf-8;

    location / {
        try_files $uri $uri/ /index.php?$query_string;
    }

    location = /favicon.ico { access_log off; log_not_found off; }
    location = /robots.txt  { access_log off; log_not_found off; }

    error_page 404 /index.php;

    location ~ \.php$ {
        fastcgi_pass unix:/var/run/php/php8.3-fpm.sock;
        fastcgi_param SCRIPT_FILENAME $realpath_root$fastcgi_script_name;
        include fastcgi_params;
        fastcgi_hide_header X-Powered-By;
    }

    location ~ /\.(?!well-known).* {
        deny all;
    }

    # Gzip compression
    gzip on;
    gzip_vary on;
    gzip_min_length 1024;
    gzip_types text/plain text/css application/json application/javascript text/xml application/xml;
}

Enable site:

ln -s /etc/nginx/sites-available/elearning /etc/nginx/sites-enabled/
nginx -t
systemctl reload nginx

Step 6: Deploy Application

Create deploy user:

adduser deploy
usermod -aG www-data deploy

Create application directory:

mkdir -p /var/www/elearning
chown -R deploy:www-data /var/www/elearning

Switch to deploy user and clone:

su - deploy
cd /var/www/elearning
git clone https://github.com/yourusername/elearning-app.git .

Install dependencies:

composer install --optimize-autoloader --no-dev
npm install && npm run build

Configure environment:

cp .env.example .env
php artisan key:generate

Edit .env with production values:

APP_NAME="E-Learning"
APP_ENV=production
APP_DEBUG=false
APP_URL=https://elearning.yourdomain.com

DB_CONNECTION=mysql
DB_HOST=127.0.0.1
DB_PORT=3306
DB_DATABASE=elearning_production
DB_USERNAME=elearning
DB_PASSWORD=your_strong_password

CACHE_DRIVER=redis
SESSION_DRIVER=redis
QUEUE_CONNECTION=redis

Run migrations and seeders:

php artisan migrate --force
php artisan db:seed --class=RolePermissionSeeder --force

Set permissions:

sudo chown -R deploy:www-data /var/www/elearning
sudo chmod -R 755 /var/www/elearning
sudo chmod -R 775 /var/www/elearning/storage
sudo chmod -R 775 /var/www/elearning/bootstrap/cache

Step 7: SSL with Certbot

apt install certbot python3-certbot-nginx -y
certbot --nginx -d elearning.yourdomain.com

Test auto-renewal:

certbot renew --dry-run

Step 8: Configure Queue Worker

Create Supervisor config /etc/supervisor/conf.d/elearning-worker.conf:

[program:elearning-worker]
process_name=%(program_name)s_%(process_num)02d
command=php /var/www/elearning/artisan queue:work redis --sleep=3 --tries=3 --max-time=3600
autostart=true
autorestart=true
stopasgroup=true
killasgroup=true
user=deploy
numprocs=2
redirect_stderr=true
stdout_logfile=/var/www/elearning/storage/logs/worker.log
stopwaitsecs=3600

Start workers:

supervisorctl reread
supervisorctl update
supervisorctl start elearning-worker:*

Step 9: Optimize for Production

php artisan config:cache
php artisan route:cache
php artisan view:cache
php artisan icons:cache
php artisan filament:cache-components

Step 10: Automated Deployment

Create deployment script deploy.sh:

#!/bin/bash
set -e

cd /var/www/elearning

echo "๐Ÿš€ Pulling latest changes..."
git pull origin main

echo "๐Ÿ“ฆ Installing dependencies..."
composer install --optimize-autoloader --no-dev

echo "๐ŸŽจ Building assets..."
npm ci && npm run build

echo "๐Ÿ”„ Running migrations..."
php artisan migrate --force

echo "๐Ÿงน Clearing caches..."
php artisan config:clear
php artisan route:clear
php artisan view:clear

echo "โœจ Rebuilding caches..."
php artisan config:cache
php artisan route:cache
php artisan view:cache
php artisan filament:cache-components

echo "๐Ÿ”„ Restarting queue workers..."
php artisan queue:restart

echo "โœ… Deployment complete!"

Make executable:

chmod +x deploy.sh

GitHub Actions Deployment

.github/workflows/deploy.yml:

name: Deploy to Production

on:
  push:
    branches: [main]

jobs:
  deploy:
    runs-on: ubuntu-latest
    if: github.ref == 'refs/heads/main'

    steps:
      - name: Deploy to server
        uses: appleboy/ssh-action@master
        with:
          host: ${{ secrets.SERVER_HOST }}
          username: ${{ secrets.SERVER_USER }}
          key: ${{ secrets.SERVER_SSH_KEY }}
          script: |
            cd /var/www/elearning
            ./deploy.sh

Step 11: Migrate Data from Old System

Create migration script database/scripts/migrate_legacy.php:

<?php

// Import old students
$oldStudents = json_decode(file_get_contents('legacy_data/students.json'), true);

foreach ($oldStudents as $old) {
    $user = User::create([
        'name' => $old['nama_siswa'],
        'email' => $old['email'] ?: $old['nis'] . '@school.local',
        'password' => Hash::make('defaultpassword'),
    ]);
    $user->assignRole('student');
    
    // Map old ID for reference
    DB::table('legacy_mappings')->insert([
        'old_table' => 'siswa',
        'old_id' => $old['id'],
        'new_table' => 'users',
        'new_id' => $user->id,
    ]);
}

// Import old exams
$oldExams = json_decode(file_get_contents('legacy_data/exams.json'), true);

foreach ($oldExams as $old) {
    Exam::create([
        'title' => $old['nama_ujian'],
        'subject_id' => $subjectMapping[$old['mapel_id']],
        'duration_minutes' => $old['durasi'],
        'start_time' => $old['waktu_mulai'],
        'end_time' => $old['waktu_selesai'],
        'status' => 'closed', // Old exams are closed
    ]);
}

Monitoring & Maintenance

Health Check Endpoint

Add to routes/web.php:

Route::get('/health', function () {
    return response()->json([
        'status' => 'ok',
        'timestamp' => now()->toIso8601String(),
        'database' => DB::connection()->getPdo() ? 'connected' : 'error',
    ]);
});

Recommended Monitoring Tools

ToolPurpose
UptimeRobotUptime monitoring
Laravel TelescopeDebug & profiling
SentryError tracking
Laravel PulseReal-time dashboard

Summary

We've successfully deployed our e-learning platform:

  • โœ… Ubuntu VPS with NGINX + PHP-FPM
  • โœ… MySQL database with secure credentials
  • โœ… SSL certificate with auto-renewal
  • โœ… Queue workers with Supervisor
  • โœ… Automated deployment with GitHub Actions
  • โœ… Legacy data migration script
  • โœ… Health monitoring endpoint

Series Conclusion

๐ŸŽ‰ Congratulations! We've completed the entire Laravel E-Learning rebuild journey:

PartTopicStatus
1Introductionโœ…
2Setup Laravel 11 + Filamentโœ…
3Database Schemaโœ…
4Authentication & Rolesโœ…
5Exam Systemโœ…
6Real-Time Featuresโœ…
7Testingโœ…
8Deploymentโœ…

From a legacy Laravel 5.2 application to a modern, tested, production-ready platform!

What's Next?

Future enhancements planned:

  • ๐Ÿ“ฑ Mobile app with React Native
  • ๐Ÿค– AI-powered question generation
  • ๐Ÿ“Š Advanced analytics dashboard
  • ๐ŸŒ Multi-school tenancy

Have questions about this series? Get in touch or find me on GitHub.


๐Ÿ”— Resources:

Continue Reading

Previous article

โ† Previous Article

Laravel E-Learning Part 7: Testing Strategy with Pest PHP