Cara Deploy Laravel dengan Docker Compose di VPS Ubuntu untuk Production

Cara Deploy Laravel dengan Docker Compose di VPS Ubuntu untuk Production

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:

  • app menjalankan PHP-FPM dan aplikasi Laravel.

  • nginx menerima request dari web dan meneruskannya ke PHP-FPM.

  • mysql menyimpan database aplikasi.

  • redis digunakan untuk cache, session, atau queue.

  • queue menjalankan background job Laravel, misalnya pengiriman email dan notifikasi.

  • scheduler menjalankan 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=false agar detail error aplikasi tidak tampil ke pengunjung.

  • Jangan upload file .env ke repository GitHub.

  • Gunakan password database yang panjang dan unik.

  • Buat backup database secara berkala dengan mysqldump.

  • Gunakan restart: unless-stopped agar container aktif kembali setelah VPS restart.

  • Pisahkan konfigurasi development dan production bila project sudah semakin besar.

  • Pantau log dengan docker compose logs saat terjadi error.

  • Pastikan folder storage dan bootstrap/cache dapat 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

Search Articles