Deploy aplikasi Laravel + Inertia.js ke VPS terlihat sederhana di permukaan, tetapi dalam praktiknya ada beberapa bagian yang harus selaras: proses build frontend dengan Vite, runtime PHP melalui PHP-FPM, reverse proxy/web server Nginx, queue worker, storage publik, cache Laravel, dan konfigurasi environment production. Jika salah satu tidak tepat, gejala yang muncul bisa bermacam-macam: halaman putih, asset 404, error manifest Vite, route SPA yang salah, mixed content, atau queue yang diam tanpa proses.
Artikel ini fokus pada deployment mandiri ke VPS dengan pendekatan yang praktis dan umum dipakai. Asumsinya Anda sudah memiliki aplikasi Laravel + Inertia.js yang berjalan baik di lokal, server VPS berbasis Linux, domain yang mengarah ke server, dan akses SSH. Tujuan panduan ini bukan membuat deployment paling kompleks, melainkan deployment yang stabil, mudah dipahami, dan mudah di-debug.
Gambaran arsitektur deployment
Pada setup ini, alurnya biasanya seperti berikut:
- Nginx menerima request HTTP/HTTPS dari client.
- Nginx melayani file statis jika tersedia, lalu meneruskan request PHP ke PHP-FPM.
- Laravel menangani routing, controller, dan response Inertia.
- Frontend Inertia menggunakan asset yang sudah dibangun oleh Vite ke folder build production.
- Queue worker berjalan di background untuk job asynchronous seperti email, notifikasi, atau proses berat lain.
- File upload publik diakses melalui storage:link.
Hal penting yang perlu dipahami: untuk production, Anda tidak menjalankan Vite dev server. Yang dipakai oleh browser adalah file hasil build, dan Laravel akan membaca manifest Vite untuk mengetahui nama file final yang sudah di-hash.
Persiapan server dan struktur deployment
Dependensi minimum di VPS
Pastikan server memiliki komponen berikut:
- Nginx
- PHP-FPM beserta extension yang dibutuhkan Laravel, misalnya
mbstring,xml,curl,zip,bcmath,intl,pdo_mysqlatau driver database lain - Composer
- Node.js dan npm atau pnpm/yarn untuk build asset
- Git
- Database server atau akses ke database terpisah
- Supervisor atau systemd untuk queue worker
Anda bisa menyimpan aplikasi di direktori seperti /var/www/myapp. Untuk deployment yang lebih aman, banyak tim menggunakan struktur releases dan current agar pergantian versi cukup dengan mengubah symlink. Ini juga membantu zero-downtime sederhana.
Contoh struktur direktori
/var/www/myapp/
├── releases/
│ ├── 20260327090000/
│ └── 20260327103000/
├── shared/
│ ├── .env
│ ├── storage/
│ └── bootstrap-cache/
└── current -> /var/www/myapp/releases/20260327103000Jika Anda ingin setup lebih sederhana, Anda tetap bisa deploy langsung ke satu folder. Namun trade-off-nya adalah rollback lebih sulit dan ada risiko file berubah di tengah request aktif.
Konfigurasi environment production Laravel
File .env production
Jangan menyalin mentah file .env dari lokal. Production perlu nilai yang sesuai dengan domain, HTTPS, queue, cache, dan database produksi.
APP_NAME="MyApp"
APP_ENV=production
APP_KEY=base64:xxxxx
APP_DEBUG=false
APP_URL=https://app.example.com
LOG_CHANNEL=stack
LOG_LEVEL=warning
DB_CONNECTION=mysql
DB_HOST=127.0.0.1
DB_PORT=3306
DB_DATABASE=myapp
DB_USERNAME=myapp_user
DB_PASSWORD=secret
CACHE_STORE=file
SESSION_DRIVER=file
QUEUE_CONNECTION=database
FILESYSTEM_DISK=publicBeberapa catatan penting:
- APP_DEBUG=false wajib di production agar error sensitif tidak tampil ke user.
- APP_URL harus sesuai protokol final, biasanya
https://.... Nilai yang salah sering memicu URL asset atau redirect yang tidak konsisten. - Jika aplikasi berada di belakang reverse proxy atau CDN, pastikan konfigurasi trusted proxy sesuai kebutuhan agar skema HTTPS terbaca benar.
Optimasi cache Laravel
Setelah environment siap, jalankan cache untuk production:
php artisan config:cache
php artisan route:cache
php artisan view:cacheMengapa ini penting? Laravel biasanya membaca banyak file konfigurasi dan route saat bootstrap. Dengan cache, startup menjadi lebih efisien. Namun ada trade-off: setiap perubahan pada file konfigurasi atau route membutuhkan rebuild cache lagi. Jika lupa, aplikasi bisa terlihat memakai konfigurasi lama.
Saat deployment, urutan aman yang umum adalah:
php artisan optimize:clear
php artisan config:cache
php artisan route:cache
php artisan view:cacheJangan mengedit file
.envlalu lupa menjalankan ulangphp artisan config:cache. Ini salah satu penyebab klasik perubahan environment tidak terbaca.
Build asset Vite untuk Inertia.js
Mengapa build asset harus diperhatikan
Pada Laravel + Inertia.js, sisi frontend tetap bagian penting dari deployment. Vite akan menghasilkan file JS/CSS production ke direktori build dan membuat file manifest.json yang dibaca oleh helper Laravel. Jika build gagal, atau folder build tidak ikut terdeploy, aplikasi biasanya akan error saat render halaman.
Build di server vs build di CI/local
Ada dua pendekatan umum:
- Build di server: mudah diterapkan, cukup jalankan
npm cidannpm run builddi VPS. Kekurangannya, deployment lebih lama dan server butuh dependensi Node. - Build di CI/local lalu upload artifact: lebih bersih dan konsisten, cocok untuk tim atau pipeline otomatis. Kekurangannya setup awal lebih kompleks.
Untuk deployment mandiri, build di server sering menjadi pilihan paling praktis.
Perintah build production
composer install --no-dev --optimize-autoloader
npm ci
npm run buildSetelah build selesai, pastikan direktori build tersedia, biasanya di bawah public/build, dan file manifest ada:
ls -lah public/build
cat public/build/manifest.jsonJika Anda menggunakan SSR atau setup frontend yang lebih kompleks, langkahnya bisa berbeda. Namun untuk mayoritas aplikasi Laravel + Inertia.js standar, asset production akan dibaca dari manifest tersebut.
Konfigurasi Nginx dan PHP-FPM
Root document harus ke folder public
Kesalahan paling berbahaya saat deploy Laravel adalah mengarahkan root Nginx ke folder project utama, bukan ke public. Ini bisa membocorkan file sensitif seperti .env jika salah konfigurasi.
Contoh konfigurasi server block Nginx:
server {
listen 80;
server_name app.example.com;
root /var/www/myapp/current/public;
index index.php index.html;
access_log /var/log/nginx/myapp_access.log;
error_log /var/log/nginx/myapp_error.log;
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; }
location ~* \.(js|css|png|jpg|jpeg|gif|svg|ico|woff|woff2)$ {
expires 7d;
access_log off;
add_header Cache-Control "public, no-transform";
try_files $uri =404;
}
location ~ \.php$ {
include snippets/fastcgi-php.conf;
fastcgi_pass unix:/run/php/php8.2-fpm.sock;
fastcgi_param SCRIPT_FILENAME $realpath_root$fastcgi_script_name;
include fastcgi_params;
}
location ~ /\.(?!well-known).* {
deny all;
}
}Bagian terpenting di sini:
rootmenunjuk ke folderpublic.try_filesmeneruskan request yang tidak cocok keindex.php, sehingga route Laravel dan Inertia tetap bekerja.- Ekstensi file statis dilayani langsung oleh Nginx.
- File tersembunyi diblokir untuk keamanan.
Setelah mengubah konfigurasi, selalu uji dan reload:
sudo nginx -t
sudo systemctl reload nginxMenyesuaikan PHP-FPM
Pastikan socket atau port PHP-FPM sesuai dengan versi PHP yang Anda gunakan. Jika salah, gejala umumnya adalah 502 Bad Gateway. Cek service PHP-FPM:
sudo systemctl status php8.2-fpmJika versi berbeda, sesuaikan path socket di Nginx. Jangan menyalin konfigurasi buta dari artikel lain tanpa mengecek versi yang benar di server Anda.
Storage, permission, dan file upload
Symlink storage
Jika aplikasi menyimpan file upload ke disk public, buat symlink berikut:
php artisan storage:linkPerintah ini membuat public/storage mengarah ke storage/app/public. Tanpa symlink, URL file upload biasanya menghasilkan 404 meskipun file sebenarnya ada di server.
Permission folder yang benar
Folder yang umumnya butuh hak tulis adalah:
storage/bootstrap/cache/
Contoh pengaturan sederhana:
sudo chown -R www-data:www-data /var/www/myapp
sudo find /var/www/myapp -type f -exec chmod 644 {} \;
sudo find /var/www/myapp -type d -exec chmod 755 {} \;
sudo chmod -R ug+rwx /var/www/myapp/shared/storage
sudo chmod -R ug+rwx /var/www/myapp/shared/bootstrap-cacheHindari memberi 777 ke seluruh project. Itu memang kadang “menyelesaikan” masalah permission, tetapi membuka risiko keamanan dan menyembunyikan akar masalah ownership yang salah.
Queue worker dan proses background
Jika aplikasi menggunakan queue untuk email, import data, notifikasi, atau job berat lainnya, menjalankan web server saja tidak cukup. Anda perlu worker background yang selalu hidup.
Contoh konfigurasi Supervisor:
[program:myapp-worker]
process_name=%(program_name)s_%(process_num)02d
command=php /var/www/myapp/current/artisan queue:work --sleep=3 --tries=3 --timeout=90
autostart=true
autorestart=true
stopasgroup=true
killasgroup=true
user=www-data
numprocs=1
redirect_stderr=true
stdout_logfile=/var/log/supervisor/myapp-worker.logSetelah file dibuat:
sudo supervisorctl reread
sudo supervisorctl update
sudo supervisorctl start myapp-worker:*Jika Anda deploy versi baru, worker yang lama bisa tetap memegang kode lama di memori. Karena itu, setelah deploy biasanya jalankan:
php artisan queue:restartPerintah ini meminta worker restart secara halus setelah job yang sedang diproses selesai.
Strategi deployment dan zero-downtime sederhana
Urutan deployment yang aman
Berikut alur yang cukup aman untuk deployment manual:
- Pull atau upload kode ke release baru.
- Pasang dependency Composer tanpa dev dependency.
- Pasang dependency Node dan build asset Vite.
- Hubungkan
.envdan folder shared sepertistorage. - Jalankan migration jika diperlukan.
- Buat cache config/route/view.
- Ubah symlink
currentke release baru. - Reload PHP-FPM bila perlu dan restart queue secara halus.
Contoh sangat sederhana:
cd /var/www/myapp/releases/20260327103000
composer install --no-dev --optimize-autoloader
npm ci
npm run build
ln -sfn /var/www/myapp/shared/.env .env
rm -rf storage
ln -sfn /var/www/myapp/shared/storage storage
php artisan migrate --force
php artisan optimize:clear
php artisan config:cache
php artisan route:cache
php artisan view:cache
ln -sfn /var/www/myapp/releases/20260327103000 /var/www/myapp/current
php /var/www/myapp/current/artisan queue:restartMengapa pendekatan symlink membantu? Karena request yang sedang masuk tetap dilayani oleh release lama sampai symlink berpindah. Setelah pindah, request baru akan memakai kode versi baru. Ini bukan zero-downtime sempurna untuk semua kasus, tetapi cukup baik untuk banyak aplikasi internal atau produk skala kecil-menengah.
Trade-off utamanya ada pada migration database. Jika migration bersifat destruktif atau mengubah skema yang tidak kompatibel dengan kode lama, Anda tetap bisa mengalami gangguan sesaat. Untuk itu, usahakan migration kompatibel dua arah selama masa transisi, misalnya menambah kolom baru dulu sebelum menghapus kolom lama di deploy berikutnya.
Checklist error umum dan cara debug
1. Asset manifest tidak ditemukan
Gejala umum:
- Error tentang Vite manifest not found
- Halaman tampil tanpa CSS/JS
Penyebab yang sering:
npm run buildbelum dijalankan- Folder
public/buildtidak ikut terdeploy - Build gagal tapi deploy tetap lanjut
- Path root Nginx salah sehingga file build tidak terbaca
Cek:
ls -lah public/build
cat public/build/manifest.json2. Mixed content setelah HTTPS aktif
Gejala umum:
- Halaman utama HTTPS, tetapi asset atau request AJAX masih HTTP
- Browser memblokir file JS/CSS atau gambar
Penyebab:
APP_URLmasih memakaihttp://- Reverse proxy tidak meneruskan header HTTPS dengan benar
- URL hardcoded di frontend atau database masih menggunakan HTTP
Solusi:
- Ubah
APP_URLke HTTPS - Pastikan trusted proxy benar jika memakai load balancer/CDN
- Hindari hardcode URL absolut jika tidak perlu
- Clear lalu rebuild config cache
3. Route fallback salah atau SPA tidak bekerja benar
Pada Laravel + Inertia, routing utama tetap berada di Laravel, bukan frontend router murni seperti SPA statis. Jika konfigurasi Nginx salah, route tertentu bisa menghasilkan 404 dari Nginx sebelum sempat masuk ke Laravel.
Pastikan blok berikut ada:
location / {
try_files $uri $uri/ /index.php?$query_string;
}Kesalahan umum lainnya adalah membuat fallback route Laravel terlalu agresif sehingga menimpa route API, file statis, atau halaman lain. Jika Anda memakai fallback route, pastikan ia tidak mengganggu endpoint yang memang harus 404 atau punya penanganan sendiri.
4. 502 Bad Gateway
Biasanya terkait PHP-FPM:
- Service mati
- Socket/path salah
- Permission socket bermasalah
Cek log:
sudo systemctl status php8.2-fpm
sudo journalctl -u php8.2-fpm -n 100
sudo tail -f /var/log/nginx/myapp_error.log5. Upload file gagal atau gambar 404
Cek tiga hal:
- Symlink
public/storagesudah ada - Permission
storage/benar - Disk Laravel mengarah ke lokasi yang sesuai
6. Perubahan .env tidak terbaca
Biasanya karena cache config lama masih aktif. Jalankan:
php artisan optimize:clear
php artisan config:cachePenutup
Deploy Laravel + Inertia.js ke VPS tidak hanya soal membuat aplikasi “bisa diakses”, tetapi memastikan setiap komponen production bekerja konsisten: asset Vite tersedia, Nginx meneruskan request dengan benar, PHP-FPM stabil, storage dapat diakses, queue berjalan, dan cache Laravel diperbarui saat release baru. Jika Anda memahami hubungan antarbagian ini, proses deployment akan jauh lebih mudah dipelihara dan di-debug.
Untuk tahap awal, pendekatan manual dengan struktur release + symlink sudah cukup kuat dan aman. Setelah alur ini stabil, Anda bisa melangkah ke otomatisasi menggunakan GitHub Actions, Deployer, Envoy, atau pipeline CI/CD lain. Yang terpenting, bangun proses deploy yang dapat diulang, mudah diobservasi, dan tidak bergantung pada langkah ad-hoc yang hanya diingat oleh satu orang.
Komentar
0 komentar
Masuk ke akun kamu untuk ikut berkomentar.
Belum ada komentar
Jadilah yang pertama ikut berdiskusi!