Pernah nggak sih, kamu udah bikin aplikasi Laravel yang keren banget di local, semuanya jalan mulus di php artisan serve, tapi begitu mau ditaruh ke server production malah bingung mulai dari mana? Tenang, kamu nggak sendirian. Gue juga pernah di posisi itu.
Dulu waktu pertama kali mau deploy Laravel ke VPS, gue beneran blank. Di local tinggal php artisan serve, buka browser, beres. Tapi begitu ngadepin server beneran — ada Nginx, PHP-FPM, permission, environment variable — rasanya kayak disuruh naik gunung tanpa peta.
Artikel ini gue tulis supaya kamu nggak perlu struggle kayak gue dulu. Gue bakal ceritain step-by-step cara deploy Laravel ke production pakai Nginx dan PHP-FPM, dari provisioning server sampai aplikasi kamu bisa diakses publik. Semua berdasarkan pengalaman pribadi, jadi expect ada catatan-catatan kecil yang biasanya nggak ada di dokumentasi resmi.
Kenapa Nginx & PHP-FPM, Bukan Apache?
Sebelum masuk ke teknis, gue mau bahas dulu kenapa stack ini jadi favorit buat deploy Laravel di production.
Nginx itu web server yang ringan dan super cepat dalam menangani concurrent connections. Kalau aplikasi kamu bakal diakses banyak user sekaligus, Nginx jauh lebih efisien dibanding Apache. Event-driven architecture-nya bikin dia nggak gampang ngos-ngosan walau traffic lagi naik.
PHP-FPM (FastCGI Process Manager) itu yang bertugas nge-handle eksekusi kode PHP-nya. Dia manajemen proses PHP secara terpisah dari web server, jadi lebih fleksibel soal resource management. Kamu bisa atur berapa banyak worker yang jalan, berapa lama timeout-nya, dan lain-lain.
Kombinasi Nginx + PHP-FPM ini udah jadi standar industri. Laravel sendiri bahkan nge-recommend setup ini di dokumentasi resminya. Jadi kalau kamu serius mau production-ready, ini jalur yang bener.
Oke, anggapannya kamu udah punya:
- VPS dengan Ubuntu 22.04 atau 24.04 (bisa dari DigitalOcean, Vultr, Hetzner, atau provider mana pun)
- Domain yang udah diarahkan ke IP VPS kamu
- SSH access ke server
- Aplikasi Laravel yang udah siap di-deploy (sudah jalan di local)
Kalau semua itu udah ada, mari mulai.
Persiapan Server: Install Nginx, PHP-FPM, dan Dependencies
Pertama-tama, kita perlu nyiapin servernya. SSH ke VPS kamu dulu:
ssh root@ip-vps-kamu
Setelah masuk, update dulu semua package:
sudo apt update && sudo apt upgrade -y
Install Nginx
sudo apt install nginx -y
sudo systemctl start nginx
sudo systemctl enable nginx
Buka browser, akses http://ip-vps-kamu. Kalau muncul halaman default Nginx, berarti udah bener.
Install PHP dan PHP-FPM
Di sini kita butuh PHP 8.2 atau 8.3 (tergantung versi Laravel yang kamu pakai). Gue pakai PHP 8.3 sebagai contoh:
sudo apt install software-properties-common -y
sudo add-apt-repository ppa:ondrej/php -y
sudo apt update
sudo apt install php8.3-fpm php8.3-cli php8.3-common php8.3-mysql \
php8.3-pgsql php8.3-sqlite3 php8.3-xml php8.3-curl php8.3-gd \
php8.3-imagick php8.3-mbstring php8.3-zip php8.3-bcmath \
php8.3-intl php8.3-readline php8.3-redis php8.3-opcache -y
Banyak banget ya? Iya, tapi Laravel butuh semua extension ini. Daripada nanti error pas jalan, mending install sekarang sekalian.
Cek versi PHP-nya:
php -v
Pastikan output-nya nunjukkin PHP 8.3.x.
Install Composer
Kalau kamu belum punya Composer di server:
curl -sS https://getcomposer.org/installer | php
sudo mv composer.phar /usr/local/bin/composer
composer --version
Install Database Server
Gue asumsikan kamu pakai MySQL (paling umum untuk Laravel):
sudo apt install mysql-server -y
sudo mysql_secure_installation
Ikuti wizard-nya, set root password, dan jawab Y untuk semua pertanyaan keamanan.
Buat database dan user untuk aplikasi kamu:
sudo mysql -u root -p
CREATE DATABASE nama_database_kamu;
CREATE USER 'user_kamu'@'localhost' IDENTIFIED BY 'password_yang_kuat';
GRANT ALL PRIVILEGES ON nama_database_kamu.* TO 'user_kamu'@'localhost';
FLUSH PRIVILEGES;
EXIT;
Oke, server udah siap dari sisi software. Sekarang kita deploy aplikasinya.
Deploy Kode Laravel ke Server
Ada beberapa cara buat naruh kode Laravel ke server: manual upload, Git clone, atau CI/CD pipeline. Untuk artikel ini, gue pakai cara paling straightforward — Git clone.
Siapkan Repository
Kalau kamu belum push kode ke Git, lakukan dulu:
# Di local machine
git init
git add .
git commit -m "Initial commit"
git remote add origin https://github.com/kamu/repo-kamu.git
git push -u origin main
Clone di Server
SSH ke server, lalu clone repo kamu. Gue biasanya taruh di /var/www/:
cd /var/www
sudo git clone https://github.com/kamu/repo-kamu.git nama-aplikasi
Masuk ke direktori project dan install dependencies:
cd /var/www/nama-aplikasi
composer install --optimize-autoloader --no-dev
Penting: flag --no-dev itu wajib di production. Dia nge-skip package development kayak Laravel Debugbar, Telescope, dll. yang nggak perlu dan malah bikin overhead.
Setup Environment
Copy file .env.example ke .env:
cp .env.example .env
Edit file .env pakai editor favorit kamu (nano, vim, atau apapun):
nano .env
Ubah beberapa konfigurasi penting:
APP_NAME="Nama Aplikasi Kamu"
APP_ENV=production
APP_KEY=
APP_DEBUG=false
APP_URL=https://domain-kamu.com
LOG_CHANNEL=daily
LOG_LEVEL=warning
DB_CONNECTION=mysql
DB_HOST=127.0.0.1
DB_PORT=3306
DB_DATABASE=nama_database_kamu
DB_USERNAME=user_kamu
DB_PASSWORD=password_yang_kuat
CACHE_DRIVER=file
SESSION_DRIVER=file
QUEUE_DRIVER=sync
Beberapa catatan penting:
- APP_ENV=production — ini bikin Laravel tahu ini environment production, jadi dia bakal optimasi banyak hal secara otomatis.
- APP_DEBUG=false — JANGAN PERNAH set ini ke
truedi production. Nanti error message lengkap bakal tampil ke user, dan itu bahaya banget dari sisi keamanan. - APP_KEY belum diisi? Tenang, generate sekarang:
php artisan key:generate
Setelah .env selesai, jalankan migrasi database:
php artisan migrate --force
Flag --force diperlukan karena kita di production environment. Laravel bakal nolak menjalankan migrasi tanpa flag ini.
Optimasi untuk Production
Laravel punya beberapa artisan command khusus production yang bikin aplikasi jadi lebih cepat:
# Cache config biar nggak di-parse tiap request
php artisan config:cache
# Cache routes
php artisan route:cache
# Cache views
php artisan view:cache
# Optimize autoload
php artisan optimize
Peringatan: Kalau kamu menjalankan config:cache, semua perubahan di .env nggak akan kebaca sampai kamu jalankan php artisan config:cache lagi. Jadi pastikan konfigurasi .env udah bener sebelum nge-cache.
Set Permissions
Ini salah satu step yang sering bikin orang pusing. Laravel butuh write access ke beberapa direktori:
sudo chown -R www-data:www-data /var/www/nama-aplikasi
sudo chmod -R 755 /var/www/nama-aplikasi
sudo chmod -R 775 /var/www/nama-aplikasi/storage
sudo chmod -R 775 /var/www/nama-aplikasi/bootstrap/cache
www-data itu user yang dipakai oleh Nginx dan PHP-FPM secara default di Ubuntu. Kalau ownership-nya salah, Laravel nggak bisa nulis log, cache, session, dan file upload.
Gue pernah lupa set permission ini dan dapat error 500 yang bikin gue debug 2 jam. Nggak lucu banget. Jadi pastiin step ini bener ya.
Konfigurasi Nginx untuk Laravel
Ini bagian yang paling krusial. Nginx perlu tahu gimana cara nge-serve aplikasi Laravel kamu. Laravel itu beda dari aplikasi PHP biasa — semua request harus diarahkan ke public/index.php.
Buat file konfigurasi baru:
sudo nano /etc/nginx/sites-available/nama-aplikasi
Isi dengan konfigurasi berikut:
server {
listen 80;
listen [::]:80;
server_name domain-kamu.com www.domain-kamu.com;
root /var/www/nama-aplikasi/public;
add_header X-Frame-Options "SAMEORIGIN";
add_header X-Content-Type-Options "nosniff";
index index.php;
charset utf-8;
# Ukuran max upload file
client_max_body_size 64M;
# Routing utama Laravel
location / {
try_files $uri $uri/ /index.php?$query_string;
}
# Block akses ke hidden files (.env, .git, dll.)
location ~ /\.(?!well-known).* {
deny all;
}
# Handle file PHP via PHP-FPM
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;
}
# Cache static files
location ~* \.(css|js|jpg|jpeg|png|gif|ico|svg|woff|woff2|ttf|eot)$ {
expires 30d;
add_header Cache-Control "public, immutable";
}
# Disable favicon.ico logging
location = /favicon.ico { access_log off; log_not_found off; }
location = /robots.txt { access_log off; log_not_found off; }
}
Penjelasan beberapa bagian penting:
rootdiarahkan ke/public— ini yang paling krusial. Jangan sampai root-nya ke project directory. Kalau salah, file.envdan source code kamu bisa diakses publik.try_files— ini yang bikin routing Laravel jalan. Tanpa ini, route selain/bakal 404.fastcgi_pass— ini nunjuk ke socket PHP-FPM. Pastikan versi PHP-nya sesuai.location ~ /\.(?!well-known).*— ini nge-block akses ke hidden files. Penting banget buat keamanan.
Aktifkan site dan test konfigurasi:
sudo ln -s /etc/nginx/sites-available/nama-aplikasi /etc/nginx/sites-enabled/
# Hapus default site kalau nggak dipakai
sudo rm /etc/nginx/sites-enabled/default
# Test konfigurasi
sudo nginx -t
# Restart Nginx
sudo systemctl restart nginx
Kalau nginx -t bilang syntax is ok dan test is successful, berarti konfigurasi udah bener. Coba buka http://domain-kamu.com di browser. Harusnya halaman Laravel kamu udah muncul!
Konfigurasi PHP-FPM untuk Performa Optimal
Default PHP-FPM configuration sebenernya udah cukup buat jalan, tapi kalau mau optimal, ada beberapa tweak yang bisa kamu lakukan.
Edit file pool configuration:
sudo nano /etc/php/8.3/fpm/pool.d/www.conf
Beberapa setting yang perlu diperhatikan:
; Process manager mode
; 'dynamic' = adjust workers based on load
; 'ondemand' = spawn workers only when needed (hemat RAM)
pm = dynamic
; Max workers
pm.max_children = 50
; Start workers on launch
pm.start_servers = 5
; Min idle workers
pm.min_spare_servers = 3
; Max idle workers
pm.max_spare_servers = 10
; Max requests per worker before restart (prevent memory leaks)
pm.max_requests = 500
; Timeout
request_terminate_timeout = 60
Tapi gimana cara menentukan angka yang bener? Gini cara gampangnya:
- Cek berapa rata-rata memory yang dipakai satu proses PHP-FPM:
ps aux | grep php-fpm | awk '{sum+=$6; n++} END {print sum/n/1024 " MB"}'
- Bagi total RAM yang tersisa (setelah OS dan Nginx) dengan angka di atas. Hasilnya =
pm.max_children.
Contoh: Kalau satu proses makan ~40MB dan VPS kamu punya 2GB RAM, sisakan ~500MB untuk OS dan Nginx, berarti: 1500 / 40 ≈ 37. Set pm.max_children ke angka sekitar 35-40.
Restart PHP-FPM setelah edit:
sudo systemctl restart php8.3-fpm
Optimasi PHP OPcache
Buka file php.ini untuk FPM:
sudo nano /etc/php/8.3/fpm/php.ini
Cari dan sesuaikan setting OPcache:
opcache.enable=1
opcache.memory_consumption=256
opcache.interned_strings_buffer=16
opcache.max_accelerated_files=10000
opcache.validate_timestamps=0
opcache.save_comments=1
opcache.jit=1255
opcache.jit_buffer_size=128M
opcache.validate_timestamps=0 itu bikin PHP nggak nge-check apakah file source berubah setiap request. Ini bikin performa naik signifikan, tapi konsekuensinya setiap kali kamu update kode, kamu harus restart PHP-FPM:
sudo systemctl restart php8.3-fpm
Setup SSL dengan Let’s Encrypt (HTTPS)
Di tahun 2026, HTTPS bukan lagi optional — itu wajib. Browser modern bakal nandain situs kamu sebagai “Not Secure” kalau nggak pakai HTTPS. Plus, ranking SEO juga bisa kena dampak.
Kabar baiknya, ada Let’s Encrypt yang kasih SSL certificate gratis. Kita pakai Certbot buat setup-nya:
sudo apt install certbot python3-certbot-nginx -y
Jalankan Certbot:
sudo certbot --nginx -d domain-kamu.com -d www.domain-kamu.com
Ikuti wizard-nya:
- Masukkan email untuk notifikasi renewal
- Setuju Terms of Service
- Pilih apakah mau redirect HTTP ke HTTPS (pilih Redirect)
Selesai! Certbot bakal otomatis update konfigurasi Nginx kamu dan setup auto-renewal. SSL certificate Let’s Encrypt berlaku 90 hari, tapi Certbot udah bikin cron job yang auto-renew sebelum expired.
Cek apakah auto-renewal beneran jalan:
sudo certbot renew --dry-run
Kalau nggak ada error, kamu aman.
Buka https://domain-kamu.com — harusnya ada icon gembok hijau di address bar. Selamat, situs kamu udah HTTPS!
Setup Firewall dan Keamanan Dasar
Production server tanpa firewall itu kayak rumah tanpa pintu. Kita pakai UFW (Uncomplicated Firewall):
sudo ufw allow OpenSSH
sudo ufw allow 'Nginx Full'
sudo ufw enable
Nginx Full itu allow port 80 dan 443. OpenSSH biar kamu nggak ke-lock dari server sendiri.
sudo ufw status
Harusnya output-nya seperti ini:
Install Fail2Ban
sudo apt install fail2ban -y
sudo systemctl enable fail2ban
sudo systemctl start fail2ban
Fail2Ban nge-protect server kamu dari brute force attacks. Dia otomatis nge-ban IP yang gagal login berkali-kali.
Beberapa Tips Keamanan Tambahan
- Jangan pakai root buat SSH. Buat user baru:
adduser deployer
usermod -aG sudo deployer
Lalu disable root login di /etc/ssh/sshd_config:
- Ganti SSH port default (opsional tapi recommended):
- Buat symlink storage kalau kamu pakai file uploads:
php artisan storage:link
Setup Supervisor untuk Queue Worker (Opsional)
Kalau aplikasi kamu pakai Laravel Queue (misalnya buat kirim email async, proses background job, dll.), kamu butuh Supervisor supaya worker tetap jalan:
sudo apt install supervisor -y
Buat config file:
sudo nano /etc/supervisor/conf.d/laravel-worker.conf
[program:laravel-worker]
process_name=%(program_name)s_%(process_num)02d
command=php /var/www/nama-aplikasi/artisan queue:work sqs --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/nama-aplikasi/storage/logs/worker.log
stopwaitsecs=3600
sudo supervisorctl reread
sudo supervisorctl update
sudo supervisorctl start "laravel-worker:*"
Checklist Final Sebelum Go Live
Sebelum kamu announce ke dunia, cek dulu beberapa hal ini:
-
.envsudah benar,APP_ENV=production,APP_DEBUG=false -
APP_KEYsudah di-generate - Database migration sudah dijalankan
- Permission
storagedanbootstrap/cachebenar - SSL/HTTPS aktif
- Firewall aktif
-
php artisan config:cachesudah dijalankan -
php artisan route:cachesudah dijalankan -
php artisan view:cachesudah dijalankan - Queue worker jalan (kalau pakai queue)
- Log channel berfungsi (
storage/logs/laravel.log) - Coba semua fitur utama di production URL
Troubleshooting: Error Umum Saat Deploy
Gue kumpulin beberapa error yang sering muncul dan solusinya:
1. 500 Internal Server Error
Cek log dulu:
tail -f /var/www/nama-aplikasi/storage/logs/laravel.log
sudo tail -f /var/log/nginx/error.log
Biasanya ini masalah permission atau .env yang salah.
2. 403 Forbidden
Kemungkinan besar masalah ownership/permission:
sudo chown -R www-data:www-data /var/www/nama-aplikasi
sudo chmod -R 755 /var/www/nama-aplikasi
sudo chmod -R 775 /var/www/nama-aplikasi/storage
sudo chmod -R 775 /var/www/nama-aplikasi/bootstrap/cache
3. 502 Bad Gateway
PHP-FPM nggak jalan atau socket path salah:
sudo systemctl status php8.3-fpm
ls /var/run/php/
Pastikan socket path di konfigurasi Nginx sesuai dengan yang ada di direktori.
4. File upload gagal
Cek client_max_body_size di Nginx dan upload_max_filesize / post_max_size di php.ini.
Nah, itu dia lengkap panduan deploy Laravel ke production pakai Nginx dan PHP-FPM. Memang panjang, tapi trust me — setelah kamu lakuin sekali, kedua kalinya bakal jauh lebih cepet. Dan kalau kamu mau automate, bisa lanjut explore tools kayak Laravel Forge, Ploi, atau bikin CI/CD pipeline sendiri pakai GitHub Actions.
Kalau ada pertanyaan, butuh bantuan setup, atau mau konsultasi soal infrastruktur Laravel kamu, jangan ragu buat hubungi gue di [email protected]. Gue seneng bisa bantu, apalagi kalau kamu lagi stuck di tengah malem dan nggak ada siapa-siapa buat nanya. Hehe.
FAQ (Frequently Asked Questions)
Apakah wajib pakai Nginx untuk deploy Laravel?
Nggak wajib, tapi sangat direkomendasikan. Laravel bisa jalan di Apache, LiteSpeed, atau bahkan Caddy. Tapi Nginx + PHP-FPM adalah kombinasi yang paling banyak dipakai di production karena performa dan resource efficiency-nya yang udah teruji. Laravel Forge dan Laravel Sail juga default pakai Nginx.
Bedanya php artisan serve dengan Nginx + PHP-FPM?
php artisan serve itu cuma development server built-in PHP yang single-threaded. Dia cuma bisa nangani satu request pada satu waktu dan nggak direkomendasikan untuk production sama sekali. Nginx + PHP-FPM adalah web server dan process manager yang dirancang khusus untuk menangani ribuan concurrent connections secara efisien.
Kenapa muncul error “500 Internal Server Error” padahal di local lancar?
Penyebab paling umum: (1) Permission folder storage dan bootstrap/cache belum di-set ke www-data, (2) File .env belum dibuat atau konfigurasinya salah, (3) APP_KEY belum di-generate, (4) Extension PHP yang dibutuhkan belum terinstall. Cek log Laravel di storage/logs/laravel.log untuk detail error pastinya.
Haruskah menjalankan config:cache setiap kali update kode?
Ya, wajib. Setiap kali kamu update kode di production — baik itu perubahan di .env, config, routes, atau views — kamu harus menjalankan ulang caching commands (config:cache, route:cache, view:cache) dan restart PHP-FPM (terutama kalau OPcache validate_timestamps=0). Banyak developer lupa step ini dan heran kenapa perubahannya nggak muncul.
Bagaimana cara update aplikasi Laravel di production tanpa downtime?
Cara manual yang umum dipakai: (1) git pull kode terbaru, (2) composer install --no-dev --optimize-autoloader, (3) php artisan migrate --force, (4) php artisan config:cache && php artisan route:cache && php artisan view:cache, (5) sudo systemctl restart php8.3-fpm. Untuk zero-downtime deployment yang lebih profesional, bisa explore Envoyer, Deployer, atau GitHub Actions dengan strategy yang sesuai.