· 4 min read · Tutorial

Menjalankan Aplikasi PHP dan Nginx dengan Docker Compose

Panduan setup PHP-FPM + Nginx + MySQL pakai Docker Compose. Mencakup Dockerfile custom, konfigurasi Nginx, optimasi production (OPCache), setup Laravel, dan troubleshooting 502 error.

Kenapa Docker Compose untuk PHP + Nginx

Menjalankan PHP dengan Nginx secara tradisional butuh instalasi terpisah: PHP-FPM, Nginx, ekstensi PHP, konfigurasi virtual host. Setiap pindah server atau onboarding developer baru harus mengulang setup yang sama.

Docker Compose menyatukan semua komponen dalam satu file YAML. Satu perintah docker compose up -d, semua service berjalan. Environment development dan production bisa identik.

Artikel ini membahas setup PHP-FPM + Nginx + MySQL menggunakan Docker Compose, siap untuk aplikasi Laravel, CodeIgniter, atau PHP native.

Arsitektur Container


Browser → Nginx (port 80) → PHP-FPM (port 9000, internal)
                 ↓
           MySQL (port 3306, internal)

Nginx menerima request HTTP dan meneruskannya ke PHP-FPM via FastCGI. MySQL hanya bisa diakses dari dalam Docker network — tidak terbuka ke internet.

Persiapan

mkdir -p ~/myapp/{src,nginx}
cd ~/myapp

1. Dockerfile PHP-FPM + Ekstensi

Buat Dockerfile:

FROM php:8.3-fpm

RUN apt-get update && apt-get install -y \
    libpng-dev libjpeg-dev libfreetype6-dev \
    libzip-dev unzip git curl \
    && docker-php-ext-configure gd --with-freetype --with-jpeg \
    && docker-php-ext-install gd pdo pdo_mysql zip opcache

COPY --from=composer:2 /usr/bin/composer /usr/bin/composer

WORKDIR /var/www/html
  • php:8.3-fpm — image PHP-FPM resmi
  • Ekstensi: gd (gambar), pdo_mysql (database), zip (composer)
  • composer:2 — multi-stage build, ambil binary composer dari image terpisah

2. Konfigurasi Nginx

Buat nginx/default.conf:

server {
    listen 80;
    server_name localhost;
    root /var/www/html/public;
    index index.php;

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

    location ~ \.php$ {
        fastcgi_pass app:9000;
        fastcgi_param SCRIPT_FILENAME $document_root$fastcgi_script_name;
        include fastcgi_params;
    }

    location ~ /\. {
        deny all;
    }
}

Penjelasan:

  • root /var/www/html/public — root folder Laravel (atau /var/www/html untuk PHP native)
  • try_files ... /index.php?$query_string — rewrite rule untuk clean URL
  • fastcgi_pass app:9000 — kirim request PHP ke container bernama app
  • location ~ /\. — tolak akses ke file tersembunyi (.env, .git, dll.)

3. docker-compose.yml

services:
  app:
    build: .
    container_name: php_app
    restart: unless-stopped
    working_dir: /var/www/html
    volumes:
      - ./src:/var/www/html
    networks:
      - app_net
    depends_on:
      - db

  nginx:
    image: nginx:alpine
    container_name: nginx_web
    restart: unless-stopped
    ports:
      - "80:80"
    volumes:
      - ./src:/var/www/html
      - ./nginx/default.conf:/etc/nginx/conf.d/default.conf
    networks:
      - app_net
    depends_on:
      - app

  db:
    image: mysql:8.0
    container_name: mysql_db
    restart: unless-stopped
    environment:
      MYSQL_ROOT_PASSWORD: root_password
      MYSQL_DATABASE: app_db
      MYSQL_USER: app_user
      MYSQL_PASSWORD: app_password
    volumes:
      - db_data:/var/lib/mysql
    networks:
      - app_net
    ports:
      - "127.0.0.1:3306:3306"

volumes:
  db_data:

networks:
  app_net:
    driver: bridge
  • app: build dari Dockerfile, volume source code
  • nginx: image Alpine (ringan), port 80 terbuka ke host
  • db: MySQL 8, volume persistent, port MySQL hanya bind ke localhost (127.0.0.1)
docker compose up -d --build

4. Optimasi untuk Production

OPCache

Tambahkan file opcache.ini dan copy via Dockerfile:

opcache.enable=1
opcache.memory_consumption=128
opcache.interned_strings_buffer=16
opcache.max_accelerated_files=10000
opcache.validate_timestamps=0
opcache.revalidate_freq=0
COPY opcache.ini /usr/local/etc/php/conf.d/opcache.ini

Gunakan Versi Image Spesifik

Jangan pakai php:8.3-fpm di production — pakai digest SHA untuk memastikan build yang sama setiap saat:

FROM php:8.3-fpm@sha256:abc123...

Non-Root User

Jalankan PHP-FPM sebagai user non-root:

RUN addgroup --gid 1000 appgroup && adduser --uid 1000 --gid 1000 appuser
USER appuser

5. Setup untuk Laravel

Setelah container berjalan, install Laravel:

docker compose exec app composer create-project laravel/laravel .
docker compose exec app php artisan key:generate

Update .env untuk koneksi database:

DB_CONNECTION=mysql
DB_HOST=db
DB_PORT=3306
DB_DATABASE=app_db
DB_USERNAME=app_user
DB_PASSWORD=app_password

Host database adalah db — nama service di docker-compose.yml. Docker Compose otomatis membuat DNS internal.

6. Troubleshooting

Nginx return 502 Bad Gateway

  • Penyebab: PHP-FPM belum siap saat Nginx mulai
  • Solusi: docker compose restart app
  • Pencegahan: tambahkan depends_on dengan healthcheck

Permission denied saat write file

  • Penyebab: user di container berbeda dengan user di host (file permission)
  • Solusi: set permission folder src: sudo chown -R 1000:1000 ./src

Laravel: "No application encryption key"

  • Penyebab: .env tidak berisi APP_KEY
  • Solusi: docker compose exec app php artisan key:generate

Container app terus restart

  • Cek log: docker compose logs app
  • Penyebab umum: typo di Dockerfile, ekstensi PHP tidak terinstall, Composer gagal

Takeaway

Docker Compose mengubah setup PHP + Nginx dari proses 20+ langkah manual menjadi satu file YAML dan satu perintah up -d.

Keuntungan utama:

  • Environment identik di development, staging, dan production
  • Onboarding developer baru: clone repo, docker compose up -d, selesai
  • Pindah server: pindahkan folder + docker-compose.yml, jalankan, semua jalan
  • Tidak ada konflik versi PHP antar proyek

Suka artikel seperti ini? Dukung operasional konten lewat Saweria.

Donate via Saweria

Discussion (0)

Leave a comment

No comments yet. Be the first to start the discussion!