Deploy aplikasi Laravel ke server VPS menggunakan Git sepintas terlihat sederhana. Cukup melakukan git pull origin main di folder server, lalu aplikasi langsung diperbarui. Namun pada praktiknya, alur sederhana ini sering memicu masalah teknis di lingkungan produksi.
Sebagian besar error terjadi akibat perbedaan lingkungan antara komputer lokal dan server VPS. Di komputer lokal, kamu mungkin menggunakan single user, web server bawaan (php artisan serve), serta APP_DEBUG=true. Sementara di server produksi Linux, Nginx atau Apache berjalan di bawah akun web server (www-data) dengan mode caching ketat dan proteksi keamanan.
Ketika kamu menjalankan git pull, Git hanya mengunduh perubahan source code yang dilacak. Git tidak mengelola hak akses direktori Linux, tidak otomatis menjalankan migrasi database, tidak mengkompilasi ulang asset frontend, dan tidak memuat ulang cache internal Laravel. Akibatnya, server rentan mengalami error 500, permission denied, hingga downtime.
Artikel ini membahas 10 masalah yang paling sering ditemui saat melakukan deployment Laravel berbasis Git di VPS beserta solusi konkretnya. Untuk otomatisasi penuh dari awal hingga CI/CD, kamu juga bisa mengikuti panduan cara deploy Laravel ke VPS serta menerapkan workflow Git Laravel agar manajemen kode lebih terstruktur.
1. Konfigurasi Environment (.env) Hilang atau Tertimpa
Akar Masalah
Secara standar keamanan framework, file .env masuk ke dalam .gitignore agar password database dan API key tidak terdorong ke repository. Saat repository di-clone pertama kali di server, file .env tidak akan ada. Sebaliknya, jika developer tak sengaja menghapus .env dari .gitignore dan melakukan commit file lokal, git pull di server akan menimpa konfigurasi database produksi dengan localhost.
Dampak
Aplikasi menampilkan 500 Internal Server Error atau exception InvalidArgumentException: No application encryption key has been specified. Jika .env produksi tertimpa file lokal, koneksi database akan putus atau kredensial bocor.
Solusi Konkret & Command
Untuk instalasi baru di server, salin .env.example menjadi .env lalu generate application key:
cp .env.example .env
php artisan key:generate
Buka .env di server dan sesuaikan variabel lingkungan produksi:
APP_NAME="Aplikasi Produksi"
APP_ENV=production
APP_DEBUG=false
APP_URL=https://example.com
DB_CONNECTION=mysql
DB_HOST=127.0.0.1
DB_PORT=3306
DB_DATABASE=nama_db_production
DB_USERNAME=user_db_production
DB_PASSWORD=password_db_rumit
Pastikan .env tetap ada di .gitignore lokal:
.env
.env.backup
.env.production
2. Error Hak Akses Directory storage/ dan bootstrap/cache/
Akar Masalah
Proses web server (Nginx/Apache) dan PHP-FPM di Linux berjalan di bawah user www-data. Sementara itu, sesi SSH tempat kamu menjalankan git pull berjalan di bawah akun SSH kamu (misalnya deployer atau ubuntu).
Saat Laravel menulis log di storage/logs/ atau menyimpan cache di bootstrap/cache/, PHP-FPM akan gagal jika direktori tersebut tidak memiliki permission tulis yang sesuai untuk user web server.
Dampak
Aplikasi crash dengan pesan error log khas Linux:
The stream or file "/var/www/my-app/storage/logs/laravel.log" could not be opened in append mode: failed to open stream: Permission denied
Solusi Konkret & Command
Ubah kepemilikan direktori storage dan bootstrap/cache ke user www-data. Gunakan perintah find untuk menetapkan permission 775 pada folder dan 664 pada file agar presisi tanpa membuat file biasa menjadi executable:
sudo chown -R www-data:www-data /var/www/my-app/storage /var/www/my-app/bootstrap/cache
sudo find /var/www/my-app/storage /var/www/my-app/bootstrap/cache -type d -exec chmod 775 {} +
sudo find /var/www/my-app/storage /var/www/my-app/bootstrap/cache -type f -exec chmod 664 {} +
Agar file baru otomatis mewarisi group www-data, tambahkan user SSH ke group www-data dan aktifkan bit SGID (g+s):
sudo usermod -a -G www-data deployer
sudo chmod -R g+s /var/www/my-app/storage /var/www/my-app/bootstrap/cache
3. Kegagalan composer install dan Ekstensi PHP Server Hilang
Akar Masalah
Tiga kendala utama pada Composer saat deploy Laravel:
- Menjalankan
composer installtanpa melepaskan paket pengembangan (dev dependencies). - Ekstensi PHP wajib Laravel (seperti
ext-mbstring,ext-xml,ext-bcmath) belum terpasang di VPS. - Server kehabisan RAM (Out of Memory / OOM) saat kalkulasi dependency graph pada VPS spesifikasi rendah.
Dampak
Deployment terhenti dengan error Memory limit exhausted, paket testing memenuhi server produksi, atau aplikasi melempar error Fatal error: Call to undefined function....
Solusi Konkret & Command
Gunakan flag --no-dev dan --optimize-autoloader saat memasang dependency di produksi:
composer install --no-dev --optimize-autoloader
Pastikan ekstensi PHP yang dibutuhkan Laravel sudah terpasang di VPS Ubuntu:
sudo apt update
sudo apt install php8.3-cli php8.3-fpm php8.3-mbstring php8.3-xml \
php8.3-bcmath php8.3-curl php8.3-mysql php8.3-zip php8.3-gd php8.3-intl -y
Jika terjadi masalah memori pada VPS RAM 1GB, lewati batas memori sementara:
COMPOSER_MEMORY_LIMIT=-1 composer install --no-dev --optimize-autoloader
Pastikan file composer.lock selalu di-commit agar Composer tidak perlu mengkalkulasi ulang dependency di server.
4. Tampilan Hancur Akibat public/build (Vite) Tidak Di-build
Akar Masalah
Secara default, Laravel menggunakan Vite untuk mengkompilasi file CSS dan JavaScript. Folder hasil build (public/build) masuk ke .gitignore untuk menghindari konflik file terkompilasi saat kolaborasi.
Ketika kode ditarik via git pull di VPS tanpa proses build frontend, browser tidak menemukan file stylesheet dan skrip aplikasi.
Dampak
Tampilan aplikasi hancur tanpa styling CSS, atau muncul exception Laravel:
Vite manifest not found at: /var/www/my-app/public/build/manifest.json
Solusi Konkret & Command
Pilih salah satu dari dua pendekatan berikut:
Pendekatan A: Build di Server VPS
Jika VPS memiliki RAM memadai, pasang Node.js dan jalankan kompilasi setelah git pull:
npm ci
npm run build
Gunakan npm ci agar modul diinstall secara pasti sesuai package-lock.json.
Pendekatan B: Build di CI/CD (Rekomendasi VPS Kecil)
Jika RAM VPS terbatas, jalankan build di GitHub Actions lalu transfer folder public/build ke VPS via SSH/SCP. Kamu bisa membaca rincian setup CI/CD ini pada artikel cara deploy Laravel ke VPS:
- name: Build Frontend Assets
run: |
npm ci
npm run build
- name: Deploy Assets to VPS
uses: appleboy/scp-action@master
with:
host: ${{ secrets.SERVER_HOST }}
username: ${{ secrets.SERVER_USER }}
key: ${{ secrets.SSH_PRIVATE_KEY }}
source: "public/build"
target: "/var/www/my-app"
5. Perubahan Kode / .env Tidak Berefek Akibat Stale Cache
Akar Masalah
Laravel membekukan konfigurasi (config:cache), rute (route:cache), dan Blade (view:cache) ke file cache tunggal di bootstrap/cache/ demi performa. Setelah git pull, PHP-FPM di server masih membaca file cache lama jika tidak diperbarui.
Selain itu, jika fungsi env() dipanggil langsung di luar file config/*.php, fungsi tersebut akan mengembalikan null begitu php artisan config:cache aktif.
Dampak
Perubahan variabel di .env atau rute baru tidak berefek di server. Pemanggilan env() langsung di Controller menghasilkan error null value.
Solusi Konkret & Command
Setiap selesai deployment, jalankan serangkaian perintah cache Artisan:
php artisan config:cache
php artisan route:cache
php artisan view:cache
php artisan event:cache
Jika terjadi error akibat cache rusak, bersihkan seluruh cache dengan perintah pemulihan:
php artisan optimize:clear
Aturan Penggunaan env() di Laravel
Jangan panggil env() langsung di Controller atau Service. Daftarkan variabel di file config/:
// config/services.php
return [
'payment' => [
'key' => env('PAYMENT_GATEWAY_KEY'),
],
];
Lalu panggil melalui helper config():
$apiKey = config('services.payment.key');
6. Migration Menolak Berjalan Tanpa Flag --force
Akar Masalah
Ketika APP_ENV=production aktif di .env, Laravel secara otomatis memblokir eksekusi migrasi skema database untuk mencegah perubahan accidental. Saat script deployment otomatis berjalan, migrasi akan terhenti karena menunggu konfirmasi interaktif (yes/no).
Dampak
Proses deployment hang atau gagal, dan skema database server tidak diperbarui. Aplikasi melempar error SQL saat mengakses tabel/kolom baru:
SQLSTATE[42S02]: Base table or view not found: 1146 Table 'my_db.new_table' doesn't exist
Solusi Konkret & Command
Bypass konfirmasi interaktif di lingkungan produksi dengan flag --force:
php artisan migrate --force
Untuk migrasi tabel berukuran besar, terapkan alur bertahap: tambahkan kolom baru bertipe nullable, deploy kode baru yang menulis ke dua kolom, migrasikan data lama, lalu jalankan DROP COLUMN secara terpisah.
7. Storage Symlink Putus atau Belum Dibuat (public/storage)
Akar Masalah
File yang diunggah pengguna disimpan di privat storage/app/public/. Agar dapat diakses publik via URL, Laravel membutuhkan symbolic link dari public/storage ke storage/app/public.
Saat proyek baru di-clone di VPS atau ketika path direktori release berubah, symlink ini belum ada atau menjadi broken symlink.
Dampak
File unggahan pengguna (gambar avatar, dokumen PDF) mengembalikan status HTTP 404 Not Found.
Solusi Konkret & Command
Hapus symlink lama jika ada, lalu buat ulang symlink storage:
rm -rf public/storage
php artisan storage:link
8. Git Pull Gagal Akibat Perubahan Manual di Server (Detached HEAD)
Akar Masalah
Masalah ini terjadi jika ada pengeditan file langsung di VPS atau jika perubahan permission file Linux terdeteksi Git sebagai filemode change. Saat git pull dijalankan, Git menolak melakukan merge karena ada konflik dengan uncommitted changes di server.
Dampak
Proses git pull dibatalkan dengan error:
error: Your local changes to the following files would be overwritten by merge:
app/Http/Controllers/OrderController.php
Please commit your changes or stash them before you merge.
Solusi Konkret & Command
Jangan pernah mengedit source code langsung di server produksi. Selalu ikuti workflow Git Laravel di lokal. Reset kerjaan lokal server secara paksa ke commit terbaru remote branch:
git fetch origin
git reset --hard origin/main
git clean -fd
Agar Git tidak melacak perubahan hak akses file Linux di VPS, nonaktifkan fileMode:
git config core.fileMode false
9. Web Server Downtime Saat Menjalankan Deployment
Akar Masalah
Melakukan git pull langsung di direktori aktif (/var/www/my-app) menyebabkan file di server berada dalam kondisi tidak lengkap selama proses pull, composer install, dan npm run build. Request pengguna yang masuk di sela waktu tersebut akan memicu error.
Dampak
Pengguna mendapati error 502 Bad Gateway, Class not found, atau tampilan aplikasi rusak sebagian saat deployment berjalan.
Solusi Konkret & Perbandingan Strategi
Strategi 1: Maintenance Mode (Sederhana)
Aktifkan maintenance mode sebelum deployment dan matikan kembali setelah selesai:
php artisan down --retry=60
git fetch origin && git reset --hard origin/main
composer install --no-dev --optimize-autoloader
php artisan migrate --force
php artisan config:cache
php artisan route:cache
php artisan view:cache
php artisan up
Flag --retry=60 mengirimkan header HTTP Retry-After: 60 untuk menjaga SEO.
Strategi 2: Atomic Symlink Deployment (Zero Downtime)
Untuk aplikasi yang membutuhkan ketersediaan tinggi, gunakan alur symlink release terpisah seperti standar Deployer.org atau Laravel Envoyer:
/var/www/my-app/
├── current -> /var/www/my-app/releases/20260924120000
├── releases/
│ └── 20260924120000/
└── shared/
├── .env
└── storage/
Jalankan persiapan release di folder timestamp baru, lalu alihkan symlink current secara atomic:
ln -sfn /var/www/my-app/releases/[timestamp] /var/www/my-app/current
Catatan sintaks ln -sfn: -s membuat symbolic link, -f memaksa penggantian link lama, dan -n memperlakukan symlink direktori target secara atomic.
10. Laravel Queue Worker Tetap Menjalankan Code Lama
Akar Masalah
Process worker Laravel (dikelola Supervisor atau Systemd) berjalan secara terus-menerus (long-running process) di memori RAM server. git pull hanya mengubah file di disk, tidak menghentikan proses PHP worker di RAM.
Dampak
Background job (email, notifikasi, pembayaran) tetap diproses menggunakan logika class lama dari RAM, menyebabkan data inconsistent atau error.
Solusi Konkret & Command
Beri instruksi ke seluruh worker untuk restart dari disk setelah deployment selesai:
php artisan queue:restart
Worker akan menyelesaikan job aktif lalu keluar (gracefully exit), kemudian Supervisor otomatis menyalakan ulang worker dengan memuat kode PHP terbaru dari disk.
Jika menggunakan Laravel Horizon (Redis Queue):
php artisan horizon:terminate
Deployment Script & Matrix Solusi Cepat
Buat file bash deploy.sh di VPS agar alur deployment berjalan konsisten dan otomatis.
Contoh Automation Script (deploy.sh)
#!/bin/bash
set -e
echo "=== Memulai Deployment Laravel ==="
# 1. Masuk ke direktori aplikasi
cd /var/www/my-app
# 2. Aktifkan Maintenance Mode
php artisan down --retry=60 || true
# 3. Fetch & reset ke commit terbaru
git fetch origin
git reset --hard origin/main
git clean -fd
# 4. Install Composer Dependencies
composer install --no-dev --optimize-autoloader
# 5. Build Frontend Assets (jika Node.js ada di VPS)
if [ -f "package.json" ]; then
npm ci
npm run build
fi
# 6. Jalankan Migrasi Database
php artisan migrate --force
# 7. Generate Cache Aplikasi
php artisan config:cache
php artisan route:cache
php artisan view:cache
php artisan event:cache
# 8. Pastikan Storage Symlink Terhubung
rm -rf public/storage
php artisan storage:link || true
# 9. Atur Hak Akses Directory (membutuhkan NOPASSWD di /etc/sudoers)
sudo chown -R www-data:www-data storage bootstrap/cache
sudo find storage bootstrap/cache -type d -exec chmod 775 {} +
sudo find storage bootstrap/cache -type f -exec chmod 664 {} +
# 10. Restart Queue Worker
php artisan queue:restart
# 11. Matikan Maintenance Mode
php artisan up
echo "=== Deployment Berhasil! ==="
Catatan: Pastikan user SSH runner memiliki hak sudo tanpa prompt password (NOPASSWD di /etc/sudoers) agar perintah sudo chown dan sudo chmod berjalan lancar dalam script.
Beri izin eksekusi script:
chmod +x deploy.sh
Ringkasan Matrix Solusi Cepat (Troubleshooting Guide)
| Gejala Error / Masalah | Penyebab Utama | Command Solusi Cepat |
|---|---|---|
500 Server Error / Key missing | .env hilang atau tidak terkonfigurasi | cp .env.example .env && php artisan key:generate |
Permission denied di storage/logs | Ownership folder bukan www-data | sudo chown -R www-data:www-data storage bootstrap/cache |
| Dev package missing / Memory OOM | Flag composer install tidak tepat | composer install --no-dev --optimize-autoloader |
Vite manifest not found / CSS hancur | Asset frontend belum di-build | npm ci && npm run build |
Perubahan .env / Route tidak berefek | Cache lama aktif | php artisan config:cache && php artisan route:cache && php artisan view:cache |
| Migrasi minta konfirmasi interaktif | APP_ENV=production memblokir migrasi | php artisan migrate --force |
Gambar upload error 404 Not Found | Symlink storage putus atau belum ada | rm -rf public/storage && php artisan storage:link |
Your local changes would be overwritten | Edit file manual / mode change di VPS | git fetch origin && git reset --hard origin/main |
| Website error 502/down saat deployment | Pull & build di direktori live | Gunakan php artisan down atau Atomic Symlink Deployment |
| Queue Worker jalankan logika lama | Worker menyimpan class lama di RAM | php artisan queue:restart |
Penutup
Deployment Laravel di VPS menggunakan Git membutuhkan perhatian khusus pada aspek lingkungan server. Mengandalkan git pull saja tanpa mengelola hak akses, caching, dan proses worker dapat menyebabkan error produksi.
Tiga kunci utama deployment Laravel yang stabil:
- Pemisahan Konfigurasi & Asset: Simpan
.envsecara aman dan kelola asset terkompilasi dengan konsisten. - Pengelolaan Hak Akses & Cache: Pastikan direktori
storagedapat ditulis olehwww-datadan segarkan cache serta queue worker pasca deployment. - Otomatisasi: Gabungkan seluruh langkah ke dalam script
deploy.shatau pipeline CI/CD.
Dengan menerapkan langkah-langkah di atas, alur deployment Laravel kamu akan berjalan aman, konsisten, dan bebas downtime. Jika kamu ingin melangkah lebih jauh menuju otomatisasi penuh, simak juga panduan cara deploy Laravel ke VPS dengan GitHub Actions serta terapkan workflow Git Laravel untuk hasil yang optimal.
Fitur komentar belum diaktifkan oleh administrator.