Cara Deploy Laravel dari GitHub ke VPS (Manual + GitHub Actions)

pindipin
23 September 2026
19 min read
Cara Deploy Laravel dari GitHub ke VPS (Manual + GitHub Actions)

Di lokal, aplikasi Laravel cukup dijalankan dengan php artisan serve dan hampir semua terlihat beres. Situasi berubah begitu repository sudah ada di GitHub dan VPS baru menyala: deploy Laravel dari GitHub ke VPS bukan soal menyalin folder proyek ke server. Rantai langkah tambahan inilah yang sering membuat developer pemula bingung.

Beberapa bagian aplikasi memang sengaja tidak ikut ke git. Folder vendor/ harus diinstall ulang lewat Composer di server, file .env berisi kredensial lingkungan yang berbeda tiap server, dan aset hasil build Vite ada di public/build yang juga masuk .gitignore. Di sisi server, document root Nginx harus menunjuk ke folder public/, direktori storage dan bootstrap/cache butuh permission tulis, database harus dimigrasi, dan queue worker adalah proses panjang yang tidak otomatis melihat kode baru.

Panduan ini membedah dua jalur sekaligus:

  1. Jalur manual — git pull di server plus checklist post-deploy, supaya kalian paham apa yang sebenarnya terjadi di setiap rilis.
  2. Jalur GitHub Actions — tes dan build aset berjalan di CI, hasil build dikirim ke server, lalu deploy via SSH otomatis setiap push ke main.

Alurnya kira-kira begini:


developer yang sudah bisa membuat aplikasi Laravel dan push ke GitHub, tapi baru pertama kali menyentuh server Linux. Setelah selesai, aplikasi bisa diakses lewat domain, dan rilis berikutnya — kalau memakai Actions — cukup dengan git push.

Semua contoh ditulis untuk Laravel 13, PHP 8.3–8.4, Node 22, dan Ubuntu 24.04 per September 2026. Panduan ini valid untuk Laravel 12 dan 13; bedanya hanya versi PHP minimum (Laravel 12 minimal PHP 8.2, Laravel 13 minimal PHP 8.3).

Prasyarat dan Rencana Arsitektur

Apa saja yang dibutuhkan

  • Repository GitHub berisi aplikasi Laravel. Pastikan composer.lock dan package-lock.json ikut di-commit — tanpa keduanya, versi dependency bisa berbeda tiap deploy.
  • VPS Ubuntu 24.04 dari penyedia mana pun (DigitalOcean, Vultr, Linode, atau provider lokal). Ubuntu 24.04 menyediakan PHP 8.3 langsung dari repositori bawaan, jadi tidak perlu menambah PPA untuk Laravel 13. Di Ubuntu 22.04 PHP bawaannya masih 8.1, sehingga butuh PPA ppa:ondrej/php.
  • Domain dengan DNS A record menuju IP VPS. Opsional, tapi disarankan supaya SSL dan APP_URL lebih rapi; lewat IP pun tetap jalan.
  • Istilah yang akan sering muncul: LEMP (Linux + Nginx + MySQL + PHP), PHP-FPM (proses manager PHP — Nginx tidak mengeksekusi PHP, ia meneruskan request lewat socket FastCGI), Composer, queue worker, GitHub Actions, dan deploy key (SSH key khusus untuk membaca repository).

Dua jalur yang akan dibahas

Jalur manual adalah pijakan pemahaman: kalian SSH ke server, git pull, lalu menjalankan daftar perintah post-deploy dengan urutan yang benar. Sederhana, tanpa biaya CI, tapi bergantung pada disiplin orang yang menjalankannya.

Jalur kedua memakai GitHub Actions: setiap push ke main memicu pipeline yang menginstall dependency, membangun aset, menjalankan tes, lalu mengirim aset hasil build dan menjalankan script deploy yang sama seperti jalur manual di VPS lewat SSH. Ini jalur yang direkomendasikan untuk rilis rutin.


Menyiapkan Server (Ubuntu 24.04 + LEMP)

Ubuntu 24.04 menyediakan Nginx 1.24 dan PHP 8.3 dari repositori bawaan — keduanya persis yang dibutuhkan Laravel 13. Mulai dari SSH ke VPS sebagai root.

Buat user deploy (non-root)

Jangan menjalankan deploy sebagai root. Composer menolak (atau minimal memperingatkan) berjalan sebagai root, dan file Laravel harus bisa ditulis oleh user tempat PHP-FPM berjalan — default Ubuntu: www-data.

# 1) SSH ke server, buat user deploy + grup www-data + grup sudo
sudo adduser deploy
sudo usermod -aG www-data deploy
sudo usermod -aG sudo deploy

Bagian setup server ini tetap dijalankan sebagai root; pindah ke user deploy nanti di bagian Memindahkan Kode. deploy sekalian dimasukkan ke grup sudo karena beberapa perintah sesudahnya — reload Nginx, supervisorctl, chown di /var/www — tetap butuh hak admin.

Install Nginx, PHP 8.3-FPM, MySQL, dan Composer

# 2) Update & install stack
sudo apt update
sudo apt install -y nginx php8.3-fpm php8.3-cli php8.3-mysql \
  php8.3-mbstring php8.3-xml php8.3-curl php8.3-zip php8.3-gd \
  php8.3-intl php8.3-bcmath composer git unzip mysql-server supervisor

# catatan: ekstensi tambahan sesuaikan kebutuhan proyek; cek dengan
# composer check-platform-reqs

# 4) Verifikasi
php -v        # >= 8.3
nginx -v
mysql --version
composer --version

Daftar ekstensi di atas adalah pola umum proyek Laravel; sesuaikan dengan composer.json masing-masing. composer check-platform-reqs memeriksa PHP dan ekstensi yang terpasang terhadap kebutuhan package yang terpasang.

(Opsional) Node.js untuk build aset di server

Kalau deploy kalian nantinya memakai GitHub Actions, server tidak perlu Node — aset dibangun di CI. Tapi untuk jalur manual 100%, install Node 22 lewat NodeSource:

# 3) (Opsional) Node.js via NodeSource untuk build aset di server
curl -fsSL https://deb.nodesource.com/setup_22.x | sudo -E bash -
sudo apt install -y nodejs

Firewall

# 5) Firewall (UFW) — jangan sampai SSH terkunci
sudo ufw allow OpenSSH
sudo ufw allow 'Nginx Full'
sudo ufw enable

Izinkan OpenSSH sebelum mengaktifkan UFW. Kalau urutannya kebalik dan SSH belum diizinkan, kalian terkunci dari server.

Membuat Database

Masuk ke MySQL lalu buat database dan user khusus aplikasi:

sudo mysql
CREATE DATABASE laravel_app CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci;
CREATE USER 'laravel_user'@'localhost' IDENTIFIED BY 'PASSWORD_KUAT';
GRANT ALL PRIVILEGES ON laravel_app.* TO 'laravel_user'@'localhost';
FLUSH PRIVILEGES;

Laravel juga mendukung PostgreSQL, MariaDB, SQLite, dan SQL Server — untuk PostgreSQL polanya serupa dengan DB_CONNECTION=pgsql. Artikel ini fokus MySQL karena paling umum di tutorial LEMP.

Memindahkan Kode dari GitHub ke VPS

GitHub tetap jadi source of truth; VPS hanya tempat menjalankan. Ada dua cara memberi server akses ke repository: HTTPS + Personal Access Token (PAT) atau SSH + deploy key. Untuk repository privat, cara praktisnya: clone sekali memakai PAT, lalu selanjutnya cukup git pull — token tidak perlu disimpan ulang.

Sebelum lanjut, pastikan sesi SSH sudah memakai user deploy, bukan root:

whoami          # harus mencetak: deploy
# kalau masih root, pindah user dulu:
su - deploy     # masukkan password user deploy
# alternatif: tutup SSH, lalu masuk lagi dengan ssh deploy@IP

Seluruh perintah di bagian ini dan bagian Post-Deploy dijalankan sebagai deploy. sudo dipakai hanya untuk perintah admin (membuat folder di /var/www, dan seterusnya) — dan karena deploy sudah masuk grup sudo, perintah itu tetap jalan.

Klon repository

# SSH-key untuk akses git (bukan untuk CI)
ssh-keygen -t ed25519 -a 200 -C "deploy@appku"
cat ~/.ssh/id_ed25519.pub
# → tambahkan ke GitHub: Settings > Deploy keys (read-only) atau SSH keys akun

# Clone
sudo mkdir -p /var/www
sudo chown -R $USER:www-data /var/www
cd /var/www
git clone git@github.com:username/repo.git appku

Deploy key di GitHub bersifat read-only per repository — cocok untuk server yang hanya perlu menarik kode. Kepemilikan /var/www memakai grup www-data supaya PHP-FPM tetap bisa membaca dan menulis di sana. $USER pada chown di atas adalah deploy — sesi sudah berpindah dari root — dan di sinilah keanggotaan grup sudo tadi dipakai.

Buat .env production

cd /var/www/appku
cp .env.example .env

Lalu isi .env sesuai server (contoh minimal):

APP_NAME=AppKu
APP_ENV=production
APP_KEY=base64:...   # hasil key:generate
APP_DEBUG=false
APP_URL=https://appku.com

LOG_CHANNEL=stack
LOG_LEVEL=error

DB_CONNECTION=mysql
DB_HOST=127.0.0.1
DB_PORT=3306
DB_DATABASE=laravel_app
DB_USERNAME=laravel_user
DB_PASSWORD=PASSWORD_KUAT

QUEUE_CONNECTION=database
CACHE_STORE=database
SESSION_DRIVER=database

Dua hal yang tidak bisa ditawar: APP_DEBUG=false (kalau true, detail konfigurasi sensitif bocor ke pengguna saat error) dan APP_KEY yang valid — tanpanya sesi dan encryption gagal dengan pesan "No application encryption key has been specified". APP_KEY cukup digenerate sekali di server, dan perintahnya dijalankan di bagian Post-Deploy — setelah composer install. Sebelum itu jangan menjalankan artisan apa pun: pada clone baru vendor/ belum ada, sedangkan artisan mem-bootstrap dari vendor/autoload.php, jadi perintahnya akan fatal error. .env tidak boleh masuk git; file .gitignore bawaan Laravel sudah menanganinya.

Post-Deploy: Perintah Artisan yang Wajib Dijalankan

Setiap kali kode baru masuk ke /var/www/appku, jalankan rangkaian ini:

composer install --no-dev --no-interaction --optimize-autoloader
php artisan key:generate       # deploy pertama; .env sudah disiapkan sebelumnya
npm ci && npm run build        # bila build aset di server (jalur manual)
php artisan migrate --force    # atau --isolated untuk multi-server
php artisan storage:link --force  # idempoten; aman dijalankan tiap rilis
php artisan optimize           # = config:cache + event:cache + route:cache + view:cache
chmod -R 775 storage bootstrap/cache   # bila perlu; untuk user non-www-data

Kenapa begitu banyak:

  • composer install --no-dev --no-interaction --optimize-autoloader menginstall dependency persis sesuai composer.lock, tanpa package require-dev (PHPUnit dsb. tidak perlu di production), sekaligus membangun classmap autoloader yang lebih cepat. Di tutorial lama sering muncul flag --prefer-dist; di Composer 2 mengunduh dist sudah jadi default sehingga flag itu tidak diperlukan lagi. Jalankan sebagai user deploy, bukan root.
  • php artisan key:generate mengisi APP_KEY di .env. Letaknya di urutan ini — setelah composer install — karena artisan mem-bootstrap dari vendor/autoload.php; pada clone baru vendor/ belum ada. Cukup dijalankan sekali di server; --force hanya kalau memang sengaja regenerate.
  • npm ci && npm run build menghasilkan aset Vite ber-hash di public/build. npm ci dipilih daripada npm install karena memasang dependency persis dari package-lock.json. Baris ini hanya untuk jalur manual — lewat GitHub Actions, aset dibangun di CI lalu dikirim ke server oleh step transfer.
  • php artisan migrate --force — di production, migrasi menolak jalan tanpa konfirmasi, jadi --force wajib. Untuk setup multi-server, tambahkan --isolated agar dua server tidak migrasi bersamaan.
  • php artisan storage:link membuat symlink public/storage → storage/app/public untuk file pada disk public (upload gambar dsb.). Idempoten, apalagi dengan --force.
  • php artisan optimize adalah gabungan config:cache, event:cache, route:cache, dan view:cache dalam satu perintah; jalankan ulang setiap deploy.
  • chmod -R 775 storage bootstrap/cache hanya bila muncul error permission; pola umumnya chown -R deploy:www-data storage bootstrap/cache && chmod -R 775 storage bootstrap/cache agar user deploy dan PHP-FPM sama-sama bisa menulis.

Untuk deploy berisiko (migration dengan perubahan besar), opsional: php artisan down --secret=xyz sebelum deploy dan php artisan up sesudahnya, supaya pengguna tidak melihat aplikasi setengah jadi.

Pentingnya urutan dan cache

Urutan punya konsekuensi nyata. Setelah config:cache dijalankan, file .env tidak lagi dibaca dan pemanggilan env() di luar file config akan mengembalikan null. Artinya: edit .env dulu sampai final, baru jalankan cache ulang — bukan sebaliknya. Praktik kedua yang harus ditegakkan: hanya pakai env() di dalam file config, jangan di controller atau view.

Catatan kedua: route:cache tidak mendukung route berbasis closure. Route harus memakai controller atau first-class callable. Kalau route:cache error, jalankan granular tanpa route cache dulu (php artisan config:cache && php artisan view:cache && php artisan event:cache), lalu perbaiki route tersebut dan kembali ke php artisan optimize di rilis berikutnya.

Verifikasi cepat setelah deploy: Laravel punya health check bawaan di /up — curl -I http://appku.com/up harus mengembalikan HTTP 200 (500 berarti aplikasi bermasalah). Ganti ke https:// setelah SSL terpasang di bagian berikutnya.

Konfigurasi Nginx (Virtual Host)

Virtual host Nginx untuk Laravel berikut diambil dari dokumentasi resmi Laravel, disesuaikan root dan domain. Simpan sebagai /etc/nginx/sites-available/appku (butuh hak admin — sebagai deploy, tulis lewat sudo nano /etc/nginx/sites-available/appku):

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

    add_header X-Frame-Options "SAMEORIGIN";
    add_header X-Content-Type-Options "nosniff";

    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 ~ ^/index\.php(/|$) {
        fastcgi_pass unix:/var/run/php/php8.3-fpm.sock;
        fastcgi_param SCRIPT_FILENAME $realpath_root$fastcgi_script_name;
        include fastcgi_params;
        fastcgi_buffer_size 32k;
        fastcgi_buffers 8 32k;
        fastcgi_busy_buffers_size 64k;
        fastcgi_hide_header X-Powered-By;
    }

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

Empat poin yang perlu dipahami, bukan sekadar disalin:

  1. Root menunjuk ke public/, karena index.php ada di sana. Jangan pernah memindahkan index.php ke root proyek — file .env dan konfigurasi lain ikut terekspos ke publik.
  2. fastcgi_pass ke socket PHP-FPM. Nginx hanya meneruskan request PHP ke proses PHP-FPM; di Ubuntu socket-nya /var/run/php/php8.3-fpm.sock (di banyak distro /var/run adalah symlink ke /run, jadi /run/php/php8.3-fpm.sock juga valid).
  3. SCRIPT_FILENAME memakai $realpath_root agar path tetap benar walau direktori deploy memakai symlink rilis.
  4. Blok deny all untuk dotfile memblokir akses .env, .git, dan file tersembunyi lain kecuali /.well-known (dibutuhkan Certbot).

Aktifkan site-nya:

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

nginx -t mengevalidasi konfigurasi sebelum reload — kalau ada typo, Nginx menolak dan server lama tetap jalan. Pastikan site default di sites-enabled tidak menabrak konfigurasi ini. Verifikasi: curl -I http://IP-VPS/up harus 200.

SSL (opsional singkat)

SSL dengan Let's Encrypt bisa dipasang lewat Certbot:

sudo apt install certbot python3-certbot-nginx
sudo certbot --nginx -d appku.com -d www.appku.com

Setelah itu APP_URL di .env seharusnya sudah https://appku.com. Detail lain ada di certbot.eff.org.

Menjaga Queue Worker Tetap Hidup (Supervisor)

php artisan queue:work adalah proses panjang: dia bisa mati karena timeout, restart server, atau error, dan — yang sering terlupa — dia tidak melihat kode baru sampai di-restart. Karena itu worker harus dikuasai process monitor seperti Supervisor.

Install Supervisor sudah termasuk di blok apt install sebelumnya. Buat file /etc/supervisor/conf.d/laravel-worker.conf (dengan sudo, misalnya sudo nano /etc/supervisor/conf.d/laravel-worker.conf):

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

Teruskan konfigurasi ke Supervisor:

sudo supervisorctl reread
sudo supervisorctl update
sudo supervisorctl start "laravel-worker:*"
sudo supervisorctl status

Direktif yang perlu diperhatikan: numprocs menentukan berapa worker parallel (sesuaikan beban), user=deploy memastikan worker jalan sebagai user yang benar, stopwaitsecs=3600 harus lebih besar dari durasi job terpanjang (kalau tidak, worker dibunuh sebelum job selesai), dan stopasgroup/killasgroup memastikan seluruh proses grup ikut berhenti, bukan hanya proses induknya.

Saat deploy, jalankan php artisan queue:restart. Perintah ini membuat worker berhenti secara graceful — job yang sedang diproses tetap selesai, tidak ada yang hilang — lalu Supervisor menyalakan worker baru dengan kode terbaru. Sinyal restart disimpan lewat cache, jadi pastikan CACHE_STORE terisi dengan driver yang valid (database, file, atau redis). Mulai Laravel 12 ada juga php artisan reload yang sekaligus memicu queue:restart dan schedule:interrupt.

Alternatif systemd

Untuk yang tidak mau Supervisor, systemd bisa memegang worker dengan pola umum berikut (ini konvensi komunitas, bukan anjuran resmi Laravel). Simpan sebagai /etc/systemd/system/laravel-worker.service:

[Unit]
Description=Laravel Queue Worker
After=network.target

[Service]
User=deploy
Group=www-data
WorkingDirectory=/var/www/appku
ExecStart=/usr/bin/php /var/www/appku/artisan queue:work --sleep=3 --tries=3
Restart=always
RestartSec=3

[Install]
WantedBy=multi-user.target
sudo systemctl daemon-reload
sudo systemctl enable --now laravel-worker
# saat deploy: php artisan queue:restart (systemd Restart=always menyalakan ulang)

Metode Manual vs Otomatis (Perbandingan Singkat)

Jalur manual punya nilai jelas: kalian tahu persis apa yang dijalankan di server, tanpa dependensi layanan pihak ketiga. Kekurangannya terlihat di rilis ke sepuluh: ada sembilan langkah post-deploy yang harus diingat manusia, tidak ada tes yang dijalankan, dan build aset bisa berbeda antara lokal dan server kalau tidak hati-hati.

GitHub Actions menutup celah-celah itu. Test berjalan sebelum kode menyentuh server, aset dibangun di lingkungan CI yang bersih lalu dikirim ke server (Node tidak perlu dipasang di VPS), dan urutan deploy dijamin identik tiap kali. Konfigurasi awalnya memang lebih repot — sekali saja.

Untuk rilis rutin, pindahlah ke Actions. Untuk belajar atau server sekunder, jalur manual tetap berguna. Keduanya sama-sama menjalankan deploy Laravel tanpa Forge.

Automasi Deploy Laravel dari GitHub ke VPS dengan GitHub Actions

Bagian ini memandu jalur deploy Laravel dengan GitHub Actions dari nol: SSH key, secrets, lalu satu file workflow.

Siapkan SSH key untuk CI

Buat key khusus (bukan key pribadi yang dipakai sehari-hari) dan pasang public key-nya ke user deploy:

ssh-keygen -t ed25519 -a 200 -C "github-actions"
cat ~/.ssh/id_ed25519.pub | ssh deploy@IP 'cat >> ~/.ssh/authorized_keys'
# permission: ~/.ssh 700, authorized_keys 600
# lalu paste isi id_ed25519 (private) ke secret VPS_SSH_KEY

Buat GitHub Secrets

Buka Settings → Secrets and variables → Actions → New repository secret (atau via CLI: gh secret set NAMA). Tiga secret yang dibutuhkan:

VPS_HOST       # IP atau domain server, mis. 203.0.113.10
VPS_USER       # mis. deploy
VPS_SSH_KEY    # isi file private key (-----BEGIN OPENSSH PRIVATE KEY----- ... )

Jangan pernah menaruh IP atau private key langsung di file workflow — di sana kode terbuka siapa pun untuk repository publik. Secrets hanya dibaca lewat ${{ secrets.NAMA }} saat workflow berjalan.

Workflow lengkap

Buat file .github/workflows/deploy.yml:

name: Deploy Laravel ke VPS

on:
  push:
    branches: [ "main" ]

permissions:
  contents: read

jobs:
  deploy:
    runs-on: ubuntu-latest
    steps:
      - name: Checkout kode
        uses: actions/checkout@v7

      - name: Setup PHP
        uses: shivammathur/setup-php@v2
        with:
          php-version: '8.4'
          extensions: mbstring, intl, bcmath, zip
          coverage: none

      - name: Setup Node
        uses: actions/setup-node@v7
        with:
          node-version: 22
          cache: 'npm'

      - name: Install dependency PHP
        run: composer install --no-interaction --optimize-autoloader

      - name: Siapkan .env untuk build & tes (jangan bocorkan secret!)
        run: |
          cp .env.example .env
          php artisan key:generate

      - name: Install dependency frontend
        run: npm ci

      - name: Build aset produksi
        run: npm run build

      - name: Jalankan test
        run: php artisan test
        # sesuaikan dengan setup test proyek (skeleton default: SQLite :memory:)

      - name: Kirim aset build (public/build) ke VPS
        uses: appleboy/scp-action@v1
        with:
          host: ${{ secrets.VPS_HOST }}
          username: ${{ secrets.VPS_USER }}
          key: ${{ secrets.VPS_SSH_KEY }}
          port: 22
          source: "public/build"
          target: "/var/www/appku"

      - name: Deploy lewat SSH ke VPS
        uses: appleboy/ssh-action@v1
        with:
          host: ${{ secrets.VPS_HOST }}
          username: ${{ secrets.VPS_USER }}
          key: ${{ secrets.VPS_SSH_KEY }}
          port: 22
          script: |
            set -e
            cd /var/www/appku
            git pull origin main
            composer install --no-dev --no-interaction --optimize-autoloader
            php artisan migrate --force
            php artisan config:cache
            php artisan route:cache
            php artisan view:cache
            php artisan event:cache
            php artisan storage:link --force
            php artisan queue:restart

Beberapa keputusan di workflow ini yang perlu dipahami:

  • CI memakai composer install tanpa --no-dev, karena php artisan test butuh package require-dev (PHPUnit). Di server baru --no-dev dipakai — production tidak butuh test runner.
  • Aset dibangun di CI lalu dikirim ke server dengan appleboy/scp-action@v1. Kenapa perlu step transfer ini? public/build masuk .gitignore, jadi git pull di server tidak akan pernah membawanya — tanpa transfer, halaman langsung error Vite manifest not found. Dengan pola ini server tetap tidak perlu Node, dan build selalu konsisten dengan kode yang dites.
  • set -e di awal script server menggantikan opsi script_stop yang sudah dihapus dari appleboy/ssh-action — kalau satu perintah gagal, sisa script berhenti sehingga deploy setengah jalan tidak diteruskan diam-diam.
  • Script server pada dasarnya adalah checklist manual dari bagian sebelumnya, dengan cache dieksplisitkan per perintah dan storage:link --force supaya idempoten. Step test bisa dihapus kalau proyek butuh service tambahan yang tidak tersedia di runner.

Memicu deploy dan melihat hasilnya

Push ke main, lalu buka tab Actions di repository untuk memantau run-nya. Setiap push ke branch itu memicu pipeline yang sama: checkout → setup → install → build → test → transfer aset → SSH deploy. Push ke branch lain tidak memicu apa-apa, sesuai konfigurasi on.push.branches.

(Opsional) Envoy sebagai alternatif otomatisasi sederhana

Kalau GitHub Actions terasa berat dan kalian cukup butuh "jalankan daftar task di server remote", ada Laravel Envoy — definisinya ditulis dalam Blade:

composer require laravel/envoy --dev
@servers(['web' => ['deploy@203.0.113.10']])

@story('deploy')
    pull-kode
    install-dependency
    jalankan-artisan
@endstory

@task('pull-kode', ['on' => 'web'])
    cd /var/www/appku
    git pull origin main
@endtask

@task('install-dependency', ['on' => 'web'])
    cd /var/www/appku
    composer install --no-dev --no-interaction --optimize-autoloader
@endtask

@task('jalankan-artisan', ['on' => 'web'])
    cd /var/www/appku
    php artisan migrate --force
    php artisan optimize
    php artisan queue:restart
@endtask
php vendor/bin/envoy run deploy

Envoy mendukung variabel CLI (--branch=...) dan hooks @before/@after/@error. Di Windows, Envoy butuh WSL2.

Masalah yang Sering Terjadi (dan Cara Mengatasinya)

  1. PHP terlalu tua di server — composer install gagal dengan pesan "requires php ^8.3". Solusi: pakai Ubuntu 24.04 (PHP 8.3 bawaan) atau tambah PPA ppa:ondrej/php.
  2. "No application encryption key has been specified" — halaman error saat akses pertama. Jalankan php artisan key:generate setelah composer install (artisan butuh vendor/), dengan .env sudah dibuat.
  3. Halaman putih / HTTP 500 tanpa pesan — sementara setel APP_DEBUG=true, lalu cari exception di storage/logs/laravel.log; setelah diperbaiki, kembalikan APP_DEBUG=false.
  4. Permission denied saat menulis storage/log — Laravel tidak punya izin tulis ke storage/bootstrap/cache. Jalankan sudo chown -R deploy:www-data storage bootstrap/cache && sudo chmod -R 775 storage bootstrap/cache.
  5. Vite manifest not found / aset 404 — CSS/JS tidak keluar karena hasil build tidak ada di server. Pastikan step build dan transfer public/build berjalan di CI, atau jalankan npm ci && npm run build di server untuk jalur manual.
  6. env() mengembalikan null setelah config:cache — urutan salah. Perbaiki .env, lalu php artisan config:cache ulang; ingat hanya pakai env() di file config.
  7. route:cache error ("Unable to prepare route ... for serialization") — route memakai closure. Ganti ke controller atau first-class callable, atau jalankan cache lain dulu tanpa route:cache.
  8. Worker masih menjalankan kode lama — job hasilnya tidak sesuai versi baru. php artisan queue:restart dan pastikan Supervisor punya autorestart=true.
  9. composer install habis memory — fatal error out of memory. Jalankan COMPOSER_MEMORY_LIMIT=-1 composer install ....
  10. Deploy crash di tengah — kode baru sudah masuk tapi database belum dimigrasi. Untuk deploy berisiko, gunakan maintenance mode opsional: php artisan down → deploy → php artisan up; dan selalu migrate --force setelah pull.

Dua kebiasaan pencegahan yang layak dibiasakan sejak awal: jangan commit .env (pastikan .gitignore memuatnya — kalau sudah terlanjur, rotasi semua kredensial yang bocor), dan jangan menjalankan deploy sebagai root.

Kesimpulan dan Langkah Berikutnya

Rantainya begini: server siap (LEMP + user deploy + firewall) → database dibuat → kode ditarik dari GitHub beserta .env produksi → perintah post-deploy dijalankan dengan urutan benar → Nginx menunjuk ke public/ → queue worker dipegang Supervisor → seluruhnya diulang otomatis oleh GitHub Actions di setiap rilis.

Saran pendekatannya sederhana: jalani jalur manual sekali atau dua kali di VPS baru supaya paham apa yang sebenarnya terjadi di setiap rilis, lalu pindah ke GitHub Actions untuk rilis berikutnya. Metode manual tetap berguna sebagai fallback.

Langkah lanjutan yang biasanya menyusul: pantau health route /up dengan uptime monitor, backup database terjadwal (mysqldump) plus rencana rollback (migrate:rollback --step=1 --force bila perlu), pasang SSL permanen dengan HSTS, dan pertimbangkan ASSET_URL kalau aset mulai diserve lewat CDN. Kalau mengelola server sendiri mulai terasa memberatkan, Laravel Forge dan Laravel Cloud adalah alternatif terkelola dari tim Laravel.

Pernah gagal deploy Laravel ke VPS? Ceritakan gejala dan solusinya di kolom komentar — bisa jadi itu persis yang dicari pembaca berikutnya.

Referensi Resmi

Bagikan Artikel:
Diskusi & Komentar

Fitur komentar belum diaktifkan oleh administrator.

Artikel Terkait

Selesai membaca? Kembali ke beranda untuk melihat artikel menarik lainnya.

Kembali ke Beranda