Logo

~/saidqb cat wiki/stack/server/deploy-laravel.md

MDdeploy-laravel.md

commit by saidqb

Deploy Laravel (PHP-FPM + Nginx)

Tiap project dapat Linux user sendiri (/home/<appuser>/app), bukan numpuk di satu folder /var/www bersama — kalau server ini dipakai buat banyak project, satu project ke-compromise tidak otomatis bisa baca file project lain.

0. Prasyarat sebelum deploy

  • Akses SSH ke server (user dengan hak sudo) — disiapkan penyedia server, bukan sesuatu yang di-install
  • Domain sudah diarahkan (DNS A record) ke IP server — diatur di panel DNS registrar, bukan di server
  • Nginx: sudo apt install nginx -y
  • PHP 8.3 + extension yang dibutuhkan: sudo apt install php8.3-fpm php8.3-mbstring php8.3-xml php8.3-mysql php8.3-curl -y
  • Composer — pilih salah satu cara install:
    • Lewat package manager (gampang, tapi versinya sering ketinggalan jauh dari rilis terbaru): sudo apt install composer -y
    • Lewat installer resmi composer.phar (selalu versi terbaru, direkomendasikan): php -r "copy('https://getcomposer.org/installer','composer-setup.php');" && php composer-setup.php --install-dir=/usr/local/bin --filename=composer && php -r "unlink('composer-setup.php');"
    • Cek hasil install (cara mana pun): composer -V
  • Git: sudo apt install git -y
  • Text editor di server buat edit .env/config, minimal nano atau vim — server diakses lewat SSH, tidak ada GUI editor: sudo apt install vim -y
  • Database (MySQL/PostgreSQL) sudah jalan & kredensialnya siap diisi ke .env: sudo apt install mysql-server -y

1. Buat user khusus untuk app

sudo adduser --disabled-password --gecos "" laravelapp

--disabled-password — user ini tidak bisa login pakai password (cuma diakses lewat sudo -iu laravelapp dari user SSH yang sudah masuk). --gecos "" skip pertanyaan nama/nomor telepon dsb yang tidak relevan buat service account. Home directory-nya otomatis jadi /home/laravelapp.

2. Ambil kode & siapkan environment (jalan sebagai laravelapp)

sudo -iu laravelapp
git clone <url-repo-git> app
cd app
composer install --no-dev --optimize-autoloader
cp .env.example .env       # cuma sekali di awal, lalu isi manual sesuai server
php artisan key:generate
exit                       # kembali ke user SSH biasa

sudo -iu laravelapp masuk sebagai user itu dengan $HOME=/home/laravelapp, jadi git clone ... app otomatis jadi /home/laravelapp/app — seluruh isinya otomatis kepemilikan laravelapp, tidak perlu chown manual lagi di langkah manapun setelah ini.

3. Buat database & user MySQL

sudo mysql -u root -p
CREATE DATABASE app_db CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci;
CREATE USER 'app_user'@'localhost' IDENTIFIED BY 'password-yang-kuat';
GRANT ALL PRIVILEGES ON app_db.* TO 'app_user'@'localhost';
FLUSH PRIVILEGES;
EXIT;

Isi DB_DATABASE=app_db, DB_USERNAME=app_user, DB_PASSWORD=password-yang-kuat (dan DB_HOST=127.0.0.1) di .env sesuai yang dibuat di sini — php artisan migrate di Langkah 6 gagal kalau ini belum ada.

4. PHP-FPM pool khusus untuk laravelapp

Pool PHP-FPM default jalan sebagai www-data dan dipakai bersama semua site di server — biar app ini benar-benar isolated (proses PHP-nya jalan sebagai laravelapp, bukan www-data yang sama dengan project lain), bikin pool sendiri:

/etc/php/8.3/fpm/pool.d/laravelapp.conf

[laravelapp]
user = laravelapp
group = laravelapp
listen = /run/php/laravelapp.sock
listen.owner = www-data
listen.group = www-data
pm = dynamic
pm.max_children = 5
pm.start_servers = 2
pm.min_spare_servers = 1
pm.max_spare_servers = 3

listen.owner/listen.group tetap www-data supaya Nginx (yang jalan sebagai www-data) boleh nulis ke socket-nya, walau proses PHP di baliknya jalan sebagai laravelapp.

sudo systemctl restart php8.3-fpm

5. Izin baca untuk Nginx

storage/bootstrap/cache sudah otomatis writable — pool PHP-FPM di atas jalan sebagai laravelapp, pemilik aslinya, jadi tidak perlu chown ke www-data seperti di setup shared folder. Yang masih perlu diatur cuma akses baca Nginx ke asset statis di public/ (home directory Linux defaultnya tertutup dari user lain):

sudo chmod o+rx /home/laravelapp
sudo chmod -R o+rX /home/laravelapp/app/public

6. Nginx + PHP-FPM

server {
    listen 80;
    server_name app.example.com;
    root /home/laravelapp/app/public;

    index index.php;

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

    location ~ \.php$ {
        include snippets/fastcgi-php.conf;
        fastcgi_pass unix:/run/php/laravelapp.sock;
    }

    location ~ /\.(?!well-known).* {
        deny all;
    }
}
sudo ln -s /etc/nginx/sites-available/app /etc/nginx/sites-enabled/
sudo nginx -t && sudo systemctl reload nginx
sudo ufw allow 'Nginx Full'   # buka port 80/443 di firewall — tanpa ini situs tidak bisa diakses dari luar meski Nginx sudah jalan benar, kalau ufw aktif (default di banyak VPS)

7. Migrasi & cache config

sudo -iu laravelapp
cd app
php artisan migrate --force
php artisan config:cache
php artisan route:cache
php artisan view:cache
exit

8. Urutan tiap kali deploy update

sudo -iu laravelapp
cd app
git pull origin production
composer install --no-dev --optimize-autoloader
php artisan config:clear
php artisan config:cache
php artisan migrate --force
php artisan queue:restart      # kalau pakai queue worker — lihat catatan TIL soal ini
exit

Urutan config:clear sebelum config:cache penting — kalau langsung config:cache tanpa clear dulu, kadang masih baca cache config lama (lihat catatan TIL bulan Juli soal .env yang tidak ke-apply gara-gara ini).

Opsional: HTTPS

sudo apt install certbot python3-certbot-nginx -y
sudo certbot --nginx -d app.example.com

Konfigurasi SSL-nya tidak perlu ditulis manual — plugin --nginx otomatis suntik langsung ke server block yang sudah dibuat di Langkah 6 (listen 443 ssl, path sertifikat, redirect HTTP→HTTPS), dan pasang auto-renewal sendiri (sertifikat Let's Encrypt berlaku 90 hari, diperpanjang otomatis sebelum expired). Hasilnya kira-kira begini:

server {
    listen 80;
    server_name app.example.com;
    root /home/laravelapp/app/public;

    index index.php;

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

    location ~ \.php$ {
        include snippets/fastcgi-php.conf;
        fastcgi_pass unix:/run/php/laravelapp.sock;
    }

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

    listen 443 ssl; # managed by Certbot
    ssl_certificate /etc/letsencrypt/live/app.example.com/fullchain.pem; # managed by Certbot
    ssl_certificate_key /etc/letsencrypt/live/app.example.com/privkey.pem; # managed by Certbot
    include /etc/letsencrypt/options-ssl-nginx.conf; # managed by Certbot
    ssl_dhparam /etc/letsencrypt/ssl-dhparam.pem; # managed by Certbot
}

server {
    if ($host = app.example.com) {
        return 301 https://$host$request_uri;
    } # managed by Certbot

    listen 80;
    server_name app.example.com;
    return 404; # managed by Certbot
}

Kalau nanti edit config ini lagi, jangan hapus baris # managed by Certbot — dan tetap sudo nginx -t && sudo systemctl reload nginx tiap habis edit.

Kalau domain dipasang di belakang Cloudflare (proxy oranye), lihat catatan cloudflare.md — ada penyesuaian mode SSL/TLS & opsi sertifikat yang perlu diperhatikan.