~/saidqb cat wiki/stack/server/deploy-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
- Lewat package manager (gampang, tapi versinya sering ketinggalan jauh dari rilis terbaru):
- Git:
sudo apt install git -y - Text editor di server buat edit
.env/config, minimalnanoatauvim— 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.