Deploy aplikasi Laravel ke VPS sering terasa rumit karena banyak komponen harus berjalan bersama: Nginx, PHP, database, cache, queue worker, hingga scheduler. Dengan Docker Compose, semua kebutuhan tersebut dapat dikemas menjadi satu konfigurasi yang lebih rapi, konsisten, dan mudah dipindahkan dari development ke server production.
Pada tutorial ini, kita akan membuat setup Laravel production menggunakan Docker Compose di VPS Ubuntu, lengkap dengan Nginx, PHP-FPM, MySQL, Redis, queue worker, dan scheduler.
Persiapan Server
Pastikan VPS Ubuntu sudah memiliki kebutuhan berikut:
Docker Engine
Docker Compose Plugin
Git
Domain yang sudah diarahkan ke IP VPS
Nginx di host server jika ingin memakai reverse proxy dan SSL Certbot
Cek instalasi Docker:
docker --version
docker compose version
Jika Docker belum terpasang, gunakan perintah berikut:
sudo apt update
sudo apt install -y docker.io docker-compose-plugin git
sudo systemctl enable docker
sudo systemctl start docker
Agar user saat ini dapat menjalankan Docker tanpa sudo:
sudo usermod -aG docker $USER
Setelah itu, logout lalu login kembali ke VPS.
Clone Project Laravel
Masuk ke folder tempat menyimpan aplikasi:
cd /var/www
sudo git clone https://github.com/username/nama-project.git laravel-app
cd laravel-app
Salin file environment:
cp .env.example .env
Jangan gunakan konfigurasi development untuk production. Edit file .env:
APP_NAME="Laravel App"
APP_ENV=production
APP_KEY=
APP_DEBUG=false
APP_URL=https://domainanda.com
LOG_CHANNEL=stack
LOG_LEVEL=error
DB_CONNECTION=mysql
DB_HOST=mysql
DB_PORT=3306
DB_DATABASE=laravel_db
DB_USERNAME=laravel_user
DB_PASSWORD=password_database_yang_kuat
CACHE_STORE=redis
SESSION_DRIVER=redis
QUEUE_CONNECTION=redis
REDIS_HOST=redis
REDIS_PASSWORD=null
REDIS_PORT=6379
Perhatikan bagian DB_HOST=mysql dan REDIS_HOST=redis. Nama tersebut akan mengarah ke nama service Docker Compose, bukan ke localhost.
Membuat Dockerfile Laravel
Buat file bernama Dockerfile di root project Laravel:
FROM php:8.3-fpm
RUN apt-get update && apt-get install -y \
git \
curl \
libpng-dev \
libonig-dev \
libxml2-dev \
libzip-dev \
zip \
unzip \
&& docker-php-ext-install pdo_mysql mbstring exif pcntl bcmath gd zip
COPY --from=composer:2 /usr/bin/composer /usr/bin/composer
WORKDIR /var/www
COPY . .
RUN composer install --no-dev --optimize-autoloader --no-interaction
RUN chown -R www-data:www-data /var/www/storage /var/www/bootstrap/cache
CMD ["php-fpm"]
Dockerfile tersebut menggunakan PHP 8.3 FPM dan menginstal ekstensi Laravel yang umum dipakai, seperti PDO MySQL, Redis-ready cache setup, GD, ZIP, serta PCNTL untuk queue worker.
Membuat Konfigurasi Nginx
Buat folder Nginx:
mkdir -p docker/nginx
Kemudian buat file docker/nginx/default.conf:
server {
listen 80;
server_name _;
root /var/www/public;
index index.php index.html;
client_max_body_size 20M;
add_header X-Frame-Options "SAMEORIGIN";
add_header X-Content-Type-Options "nosniff";
location / {
try_files $uri $uri/ /index.php?$query_string;
}
location ~ \.php$ {
try_files $uri =404;
fastcgi_pass app:9000;
fastcgi_index index.php;
fastcgi_param SCRIPT_FILENAME $realpath_root$fastcgi_script_name;
include fastcgi_params;
}
location ~ /\.(?!well-known).* {
deny all;
}
location ~* \.(jpg|jpeg|png|gif|ico|svg|webp|css|js)$ {
expires 30d;
add_header Cache-Control "public, max-age=2592000";
access_log off;
}
}
Konfigurasi ini mengarahkan semua request Laravel ke public/index.php dan mengaktifkan cache aset statis agar website lebih ringan.
Membuat File Docker Compose
Buat file compose.yaml di root project:
services:
app:
build:
context: .
dockerfile: Dockerfile
container_name: laravel-app
restart: unless-stopped
working_dir: /var/www
env_file:
- .env
volumes:
- ./:/var/www
networks:
- laravel-network
nginx:
image: nginx:alpine
container_name: laravel-nginx
restart: unless-stopped
ports:
- "8080:80"
volumes:
- ./:/var/www
- ./docker/nginx/default.conf:/etc/nginx/conf.d/default.conf
depends_on:
- app
networks:
- laravel-network
mysql:
image: mysql:8.4
container_name: laravel-mysql
restart: unless-stopped
environment:
MYSQL_DATABASE: laravel_db
MYSQL_USER: laravel_user
MYSQL_PASSWORD: password_database_yang_kuat
MYSQL_ROOT_PASSWORD: root_password_yang_kuat
volumes:
- mysql-data:/var/lib/mysql
networks:
- laravel-network
redis:
image: redis:7-alpine
container_name: laravel-redis
restart: unless-stopped
networks:
- laravel-network
queue:
build:
context: .
dockerfile: Dockerfile
container_name: laravel-queue
restart: unless-stopped
working_dir: /var/www
command: php artisan queue:work redis --sleep=3 --tries=3 --timeout=90
env_file:
- .env
volumes:
- ./:/var/www
depends_on:
- app
- redis
networks:
- laravel-network
scheduler:
build:
context: .
dockerfile: Dockerfile
container_name: laravel-scheduler
restart: unless-stopped
working_dir: /var/www
command: php artisan schedule:work
env_file:
- .env
volumes:
- ./:/var/www
depends_on:
- app
networks:
- laravel-network
networks:
laravel-network:
driver: bridge
volumes:
mysql-data:
Pada konfigurasi di atas:
appmenjalankan PHP-FPM dan aplikasi Laravel.nginxmenerima request dari web dan meneruskannya ke PHP-FPM.mysqlmenyimpan database aplikasi.redisdigunakan untuk cache, session, atau queue.queuemenjalankan background job Laravel, misalnya pengiriman email dan notifikasi.schedulermenjalankan jadwal Artisan seperti pengecekan order expired atau laporan otomatis.
Menjalankan Container
Build dan jalankan seluruh service:
docker compose up -d --build
Cek status container:
docker compose ps
Jika semua berjalan normal, buka aplikasi melalui:
http://IP_VPS:8080
Generate Application Key dan Migrasi Database
Masuk ke container Laravel:
docker compose exec app bash
Kemudian jalankan perintah berikut:
php artisan key:generate
php artisan migrate --force
php artisan storage:link
php artisan config:cache
php artisan route:cache
php artisan view:cache
Keluar dari container:
exit
Untuk melihat error aplikasi, gunakan:
docker compose logs -f app
Sedangkan untuk melihat log Nginx:
docker compose logs -f nginx
Menghubungkan Domain dengan Nginx Host
Agar aplikasi bisa diakses menggunakan domain dan HTTPS, Nginx pada host server dapat diarahkan ke container pada port 8080.
Contoh konfigurasi Nginx host:
server {
listen 80;
server_name domainanda.com www.domainanda.com;
location / {
proxy_pass http://127.0.0.1:8080;
proxy_http_version 1.1;
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header X-Forwarded-Proto $scheme;
}
}
Simpan konfigurasi, lalu cek dan reload Nginx:
sudo nginx -t
sudo systemctl reload nginx
Setelah domain dapat diakses melalui HTTP, pasang SSL gratis dari Let’s Encrypt:
sudo certbot --nginx -d domainanda.com -d www.domainanda.com
Cara Update Aplikasi Laravel di Server
Saat ada perubahan kode dari GitHub, jalankan:
cd /var/www/laravel-app
git pull origin main
docker compose up -d --build
docker compose exec app php artisan migrate --force
docker compose exec app php artisan optimize
docker compose exec app php artisan queue:restart
queue:restart penting agar queue worker membaca kode terbaru setelah deploy.
Tips Penting untuk Production
Gunakan
APP_DEBUG=falseagar detail error aplikasi tidak tampil ke pengunjung.Jangan upload file
.envke repository GitHub.Gunakan password database yang panjang dan unik.
Buat backup database secara berkala dengan
mysqldump.Gunakan
restart: unless-stoppedagar container aktif kembali setelah VPS restart.Pisahkan konfigurasi development dan production bila project sudah semakin besar.
Pantau log dengan
docker compose logssaat terjadi error.Pastikan folder
storagedanbootstrap/cachedapat ditulis oleh aplikasi.
Penutup
Docker Compose membantu proses deploy Laravel menjadi lebih terstruktur karena semua kebutuhan aplikasi berada dalam satu konfigurasi. Dengan setup ini, aplikasi Laravel tidak hanya berjalan di laptop developer, tetapi juga dapat dipindahkan ke VPS dengan environment yang konsisten.
Konfigurasi ini dapat dikembangkan lebih lanjut dengan PostgreSQL, MinIO atau S3 untuk file storage, Laravel Horizon untuk monitoring queue, serta pipeline CI/CD agar proses deploy dari GitHub menjadi otomatis.

Comments
Post a Comment