Pernah nggak sih kamu baru join project baru, tapi butuh waktu seharian cuma buat setup environment? Install MySQL, Redis, PHP, Nginx satu-satu. Belum lagi kalau versinya bentrok sama project lain. Saya pernah mengalami ini dan rasanya frustrasi banget. Semua masalah itu selesai ketika saya kenal Docker Compose.
Docker Compose itu tool yang memungkinkan kamu mendefinisikan dan menjalankan multiple container Docker sekaligus, cukup dengan satu file YAML. Bayangkan kamu punya satu file yang bisa langsung spin up MySQL, Redis, PHP-FPM, Nginx, dan bahkan phpMyAdmin dalam hitungan detik. Tanpa install apapun di host machine. Tanpa konflik versi. Tanpa drama.
Kenapa Docker Compose Itu Game Changer untuk Developer
Sebelum kenal Docker Compose, setup development environment itu proses yang menyebalkan. Kamu harus install semua dependency di machine lokal, pastikan versinya cocok, dan kalau ada update, kadang semuanya jadi broken. Dengan Docker Compose, semua environment didefinisikan dalam satu file. Mau ganti dari PHP 8.1 ke PHP 8.3? Tinggal ubah satu baris. Mau tambah Elasticsearch? Tambah beberapa baris config. Selesai.
Keunggulan utama Docker Compose untuk development lokal:
- Reproducible environment Semua developer di tim punya environment yang identik. Tidak ada lagi "di saya jalan, di kamu nggak"
- Isolasi per project Project A pakai PHP 7.4 + MySQL 5.7, Project B pakai PHP 8.3 + MySQL 8.0. Keduanya jalan berdampingan tanpa konflik
- One command setup
docker compose up -ddan semua service langsung berjalan. New developer bisa mulai coding dalam 5 menit - Clean uninstall Hapus semua container dan volume, machine kamu bersih kembali tanpa sisa-sisa installasi
Instalasi Docker dan Docker Compose
Sebelum mulai, pastikan Docker sudah terinstall. Di Linux (Ubuntu/Debian), jalankan perintah berikut:
# Install Docker
curl -fsSL https://get.docker.com -o get-docker.sh
sudo sh get-docker.sh
# Tambahkan user ke docker group (tanpa sudo)
sudo usermod -aG docker $USER
# Logout dan login ulang, lalu verify
docker --version
docker compose version
Untuk macOS dan Windows, tinggal download Docker Desktop dari docker.com. Docker Compose sudah included di dalamnya, jadi tidak perlu install terpisah.
Verify instalasi berhasil:
docker --version
# Docker version 27.x.x
docker compose version
# Docker Compose version v2.x.x
Anatomy File docker-compose.yml
File docker-compose.yml (atau compose.yaml di versi baru) adalah jantung dari Docker Compose. Di dalam file ini kamu mendefinisikan semua service, network, dan volume yang dibutuhkan project. Mari kita bedah strukturnya:
services:
# Nama service (bebas, ini jadi hostname di internal network)
app:
image: php:8.3-fpm # Docker image yang digunakan
volumes:
- ./:/var/www/html # Mount folder project ke container
networks:
- app-network # Network yang diikuti
nginx:
image: nginx:alpine
ports:
- "8080:80" # Port mapping host:container
volumes:
- ./:/var/www/html
- ./nginx.conf:/etc/nginx/conf.d/default.conf
depends_on:
- app # Pastikan app berjalan dulu
networks:
- app-network
networks:
app-network:
driver: bridge
Setiap service yang didefinisikan akan punya hostname sesuai nama servicenya. Jadi kalau service app berjalan di port 9000, service lain bisa mengaksesnya via app:9000. Ini sangat powerful karena kamu nggak perlu pusing soal IP address internal.
Setup Lengkap: PHP + MySQL + Redis + Nginx
Sekarang kita bikin setup yang real-world. Sebuah environment lengkap untuk development aplikasi web PHP dengan database MySQL, caching Redis, dan web server Nginx. Ini setup yang paling banyak dipakai di project-project Indonesian web development:
services:
# PHP-FPM Application Server
app:
build:
context: .
dockerfile: Dockerfile
container_name: myapp-php
volumes:
- ./:/var/www/html
- ./php/local.ini:/usr/local/etc/php/conf.d/local.ini
networks:
- myapp-network
restart: unless-stopped
# Nginx Web Server
nginx:
image: nginx:alpine
container_name: myapp-nginx
ports:
- "8080:80"
volumes:
- ./:/var/www/html
- ./nginx/default.conf:/etc/nginx/conf.d/default.conf
depends_on:
- app
networks:
- myapp-network
restart: unless-stopped
# MySQL Database
database:
image: mysql:8.0
container_name: myapp-mysql
ports:
- "3306:3306"
environment:
MYSQL_ROOT_PASSWORD: rootsecret123
MYSQL_DATABASE: myapp_db
MYSQL_USER: myapp_user
MYSQL_PASSWORD: myapp_pass123
volumes:
- mysql-data:/var/lib/mysql
- ./database/init.sql:/docker-entrypoint-initdb.d/init.sql
networks:
- myapp-network
restart: unless-stopped
# Redis Cache
redis:
image: redis:7-alpine
container_name: myapp-redis
ports:
- "6379:6379"
command: redis-server --requirepass redissecret123
volumes:
- redis-data:/data
networks:
- myapp-network
restart: unless-stopped
# phpMyAdmin (opsional, untuk debugging)
phpmyadmin:
image: phpmyadmin/phpmyadmin
container_name: myapp-pma
ports:
- "8081:80"
environment:
PMA_HOST: database
PMA_USER: myapp_user
PMA_PASSWORD: myapp_pass123
depends_on:
- database
networks:
- myapp-network
restart: unless-stopped
volumes:
mysql-data:
redis-data:
networks:
myapp-network:
driver: bridge
Perhatikan beberapa hal penting di config di atas:
- Volumes untuk persistence
mysql-datadanredis-datamemastikan data tidak hilang saat container di-restart. Tanpa volume ini, semua data hilang setiap kalidocker compose down - depends_on Memastikan service dependency berjalan dulu. Nginx butuh PHP-FPM, phpMyAdmin butuh MySQL
- Network terpisah Semua service berada di network yang sama sehingga bisa saling berkomunikasi via hostname
- restart: unless-stopped Container otomatis restart kalau crash, tapi tetap mati kalau kamu stop manual
Custom Dockerfile untuk PHP
Image php:8.3-fpm itu basic. Kamu hampir selalu butuh menambahkan extension seperti PDO, Redis, GD, dll. Buat file Dockerfile di root project:
FROM php:8.3-fpm
# Install system dependencies
RUN apt-get update && apt-get install -y \
git \
curl \
libpng-dev \
libjpeg-dev \
libfreetype6-dev \
libonig-dev \
libxml2-dev \
libzip-dev \
zip \
unzip \
&& docker-php-ext-configure gd --with-freetype --with-jpeg \
&& docker-php-ext-install pdo_mysql mbstring exif pcntl bcmath gd zip \
&& pecl install redis \
&& docker-php-ext-enable redis \
&& apt-get clean \
&& rm -rf /var/lib/apt/lists/*
# Install Composer
COPY --from=composer:latest /usr/bin/composer /usr/bin/composer
# Set working directory
WORKDIR /var/www/html
# Set proper permissions
RUN chown -R www-data:www-data /var/www/html
EXPOSE 9000
CMD ["php-fpm"]
Dockerfile ini melakukan beberapa hal sekaligus: install system library yang dibutuhkan, compile PHP extension, pasang Composer, dan set permissions yang benar. Semua terjadi di build time, jadi container yang dihasilkan sudah siap pakai.
Konfigurasi Nginx untuk PHP-FPM
Buat folder nginx/ dan file default.conf:
server {
listen 80;
server_name localhost;
root /var/www/html/public;
index index.php index.html;
location / {
try_files $uri $uri/ /index.php?$query_string;
}
location ~ \.php$ {
fastcgi_split_path_info ^(.+\.php)(/.+)$;
fastcgi_pass app:9000;
fastcgi_index index.php;
include fastcgi_params;
fastcgi_param SCRIPT_FILENAME $document_root$fastcgi_script_name;
fastcgi_param PATH_INFO $fastcgi_path_info;
}
location ~ /\.ht {
deny all;
}
}
Perhatikan fastcgi_pass app:9000 ini merujuk ke service app yang kita definisikan di compose file. Docker network memungkinkan komunikasi antar container via nama service. Nggak perlu tahu IP addressnya.
Menjalankan dan Mengelola Environment
Sekarang saatnya menjalankan semuanya. Pastikan semua file sudah tersimpan di folder project:
# Build dan start semua container
docker compose up -d --build
# Lihat status container
docker compose ps
# Lihat log semua service
docker compose logs -f
# Lihat log satu service saja
docker compose logs -f app
# Masuk ke container PHP
docker compose exec app bash
# Jalankan artisan/migration (Laravel)
docker compose exec app php artisan migrate
# Jalankan composer install di dalam container
docker compose exec app composer install
# Stop semua container
docker compose down
# Stop DAN hapus semua data (hati-hati!)
docker compose down -v
Setelah semua container berjalan, buka browser dan akses http://localhost:8080. Kalau pakai Laravel atau framework lain yang punya public/ folder, app langsung bisa diakses. phpMyAdmin tersedia di http://localhost:8081.
Hot Reload: Edit File Langsung Ter-Reflect
Salah satu keunggulan Docker Compose untuk development adalah volume mounting. Ketika kamu mount folder project ke container dengan:
volumes:
- ./:/var/www/html
Setiap perubahan file di host machine langsung ter-reflect di container. Edit file di VS Code, save, refresh browser perubahan langsung terlihat. Nggak perlu rebuild container setiap kali edit code. Ini yang bikin workflow development dengan Docker Compose terasa seamless.
Docker Compose Profiles: Satu File untuk Semua Kebutuhan
Kadang kamu nggak butuh semua service sekaligus. Misalnya, saat development biasa kamu butuh PHP + MySQL + Redis. Tapi saat debugging database, kamu butuh phpMyAdmin juga. Docker Compose profiles solusinya:
services:
app:
# ... config biasa
database:
# ... config biasa
redis:
# ... config biasa
phpmyadmin:
image: phpmyadmin/phpmyadmin
profiles:
- debug
# ... config lainnya
mailhog:
image: mailhog/mailhog
profiles:
- debug
ports:
- "1025:1025"
- "8025:8025"
# Jalankan service biasa saja (tanpa debug tools)
docker compose up -d
# Jalankan semua termasuk debug tools
docker compose --profile debug up -d
Ini sangat berguna saat kamu punya service yang hanya dibutuhkan sesekali. MailHog untuk testing email, Selenium untuk testing browser, atau Elasticsearch untuk full-text search. Semua bisa diaktifkan sesuai kebutuhan tanpa mengubah file.
Environment Variables dan Secrets
Jangan hardcode password dan secret di dalam docker-compose.yml. Gunakan file .env di folder project:
# .env file
DB_ROOT_PASSWORD=supersecretroot
DB_NAME=myapp_db
DB_USER=myapp_user
DB_PASSWORD=myapppassword
REDIS_PASSWORD=redispassword123
APP_PORT=8080
PMA_PORT=8081
Lalu referensikan di compose file:
services:
database:
image: mysql:8.0
ports:
- "3306:3306"
environment:
MYSQL_ROOT_PASSWORD: ${DB_ROOT_PASSWORD}
MYSQL_DATABASE: ${DB_NAME}
MYSQL_USER: ${DB_USER}
MYSQL_PASSWORD: ${DB_PASSWORD}
nginx:
ports:
- "${APP_PORT}:80"
Penting: Tambahkan .env ke .gitignore. Jangan pernah commit secrets ke repository. Buat file .env.example dengan nilai placeholder sebagai dokumentasi untuk developer lain.
Tips dan Trik dari Pengalaman
Setelah pakai Docker Compose di belasan project, ini beberapa tips yang saya pelajari:
- Gunakan named volumes, bukan bind mounts untuk database Bind mount (
./data:/var/lib/mysql) sering bermasalah dengan permission. Named volume (mysql-data:/var/lib/mysql) lebih reliable - Pin versi image Jangan pakai
mysql:latestkarena bisa berubah sewaktu-waktu. Pakaimysql:8.0atau bahkanmysql:8.0.36untuk konsistensi - Pakai .dockerignore Buat file
.dockerignoreuntuk excludenode_modules,vendor, dan.gitdari build context. Ini mempercepat build secara signifikan - Jangan jalankan production di Docker Compose Docker Compose bagus untuk development dan testing. Untuk production, gunakan Kubernetes, Docker Swarm, atau managed container service
- Backup volume secara berkala Jalankan
docker compose exec database mysqldump -u root -p dbname > backup.sqlsebelum melakukan perubahan besar
Troubleshooting Umum
Beberapa masalah yang sering muncul dan solusinya:
- Port sudah dipakai Ganti port mapping di compose file. Misalnya MySQL conflict, pakai
"3307:3306"di host - Permission denied pada volume Tambahkan user mapping di compose:
user: "${UID}:${GID}"atau set ownership di Dockerfile - Container langsung exit Cek log dengan
docker compose logs nama_service. Biasanya error di config atau missing dependency - Network tidak bisa resolve hostname Pastikan semua service ada di network yang sama. Cek dengan
docker network inspect nama_network
Kesimpulan
Docker Compose mengubah cara saya bekerja sebagai developer. Dari yang dulunya butuh waktu berjam-jam setup environment, sekarang cukup satu perintah. Semua service berjalan dengan versi yang tepat, terisolasi dari system lokal, dan bisa di-share ke seluruh tim via git. Kalau kamu belum pakai Docker Compose untuk development lokal, sekarang waktu yang tepat untuk mulai. File docker-compose.yml di atas bisa langsung kamu copy dan sesuaikan dengan kebutuhan projectmu. Selamat mencoba!