Pernah nggak sih, kamu baru join project baru, clone repo, jalankan npm install, tapi ternyata nggak jalan karena versi MySQL-nya beda, PHP-nya kurang, atau Redis belum ke-install? Saya pernah menghabiskan 2 hari cuma buat setup environment sebelum mulai coding. Dua hari yang seharusnya bisa dipakai buat produktif.
Masalah ini sebenarnya sudah solved sejak lama namanya Docker Compose. Tool ini memungkinkan kamu mendefinisikan seluruh stack aplikasi (database, cache, web server, app server) dalam satu file YAML. Satu command, semua service langsung jalan. Konsisten di laptop siapapun.
Di artikel ini, saya akan walkthrough dari nol sampai kamu punya development environment yang reproducible. Kita akan setup contoh real: aplikasi PHP + MySQL + Redis + Nginx.
Kenapa Docker Compose, Bukan Docker Biasa?
Kalau kamu sudah pernah pakai Docker, kamu tahu bahwa setiap container dijalankan satu per satu dengan perintah yang panjang:
docker run -d --name mysql -e MYSQL_ROOT_PASSWORD=secret -p 3306:3306 mysql:8.0
docker run -d --name redis -p 6379:6379 redis:7-alpine
docker run -d --name nginx -p 80:80 -v ./nginx.conf:/etc/nginx/nginx.conf nginx:alpine
Tiga perintah. Belum lagi kalau ada dependency MySQL harus jalan duluan sebelum app connect. Nginx butuh tahu di mana app server-nya. Manual banget.
Docker Compose menyelesaikan ini dengan satu file deklaratif. Kamu tulis apa yang kamu butuhkan, Compose yang urus urutan, networking, dan dependency-nya.
Install Docker dan Docker Compose
Sebelum mulai, pastikan Docker sudah terinstall. Di Ubuntu/Debian:
# Update dan install dependency
sudo apt update
sudo apt install -y ca-certificates curl gnupg
# Tambah Docker GPG key
sudo install -m 0755 -d /etc/apt/keyrings
curl -fsSL https://download.docker.com/linux/ubuntu/gpg | sudo gpg --dearmor -o /etc/apt/keyrings/docker.gpg
# Tambah repository
echo "deb [arch=$(dpkg --print-architecture) signed-by=/etc/apt/keyrings/docker.gpg] https://download.docker.com/linux/ubuntu $(lsb_release -cs) stable" | sudo tee /etc/apt/sources.list.d/docker.list > /dev/null
# Install Docker Engine + Compose plugin
sudo apt update
sudo apt install -y docker-ce docker-ce-cli containerd.io docker-compose-plugin
# Verifikasi
docker --version
docker compose version
Di macOS, cukup install Docker Desktop dari docker.com. Sudah include Compose di dalamnya.
Catatan penting: Perintahnya docker compose (spasi), bukan docker-compose (dash). Versi lama pakai dash, versi baru (V2) pakai spasi sebagai plugin.
Anatomi docker-compose.yml
Ini dia jantung dari semuanya. Buat file docker-compose.yml di root project:
version: "3.8"
services:
app:
build: .
container_name: myapp-php
volumes:
- .:/var/www/html
depends_on:
- db
- cache
networks:
- app-network
db:
image: mysql:8.0
container_name: myapp-mysql
environment:
MYSQL_ROOT_PASSWORD: secret
MYSQL_DATABASE: myapp
MYSQL_USER: appuser
MYSQL_PASSWORD: apppass
ports:
- "3306:3306"
volumes:
- mysql-data:/var/lib/mysql
networks:
- app-network
cache:
image: redis:7-alpine
container_name: myapp-redis
ports:
- "6379:6379"
networks:
- app-network
webserver:
image: nginx:alpine
container_name: myapp-nginx
ports:
- "80:80"
volumes:
- .:/var/www/html
- ./nginx.conf:/etc/nginx/conf.d/default.conf
depends_on:
- app
networks:
- app-network
volumes:
mysql-data:
networks:
app-network:
driver: bridge
Mari kita bedah satu per satu:
servicesSetiap service adalah satu container. Nama di sini (app,db,cache,webserver) juga jadi hostname internal. Artinya, dari containerapp, kamu bisa connect ke MySQL pakai hostnamedbbukanlocalhost.depends_onMengatur urutan startup.appbaru jalan setelahdbdancacherunning.volumesAda dua tipe..:/var/www/html= bind mount (folder lokal di-mount ke container).mysql-data:/var/lib/mysql= named volume (data persist walau container dihapus).networksSemua service dalam network yang sama, bisa saling panggil pakai nama service.
Setup untuk Aplikasi PHP
Kita butuh Dockerfile untuk container PHP karena image default php:8.2-fpm belum include extension yang dibutuhkan. Buat file Dockerfile:
FROM php:8.2-fpm-alpine
# Install system dependencies
RUN apk add --no-cache \
libzip-dev \
oniguruma-dev \
libpng-dev \
freetype-dev \
libjpeg-turbo-dev
# Install PHP extensions
RUN docker-php-ext-configure gd --with-freetype --with-jpeg \
&& docker-php-ext-install pdo_mysql mbstring zip gd bcmath opcache
# Install Composer
COPY --from=composer:latest /usr/bin/composer /usr/bin/composer
WORKDIR /var/www/html
EXPOSE 9000
CMD ["php-fpm"]
Lalu buat nginx.conf untuk web server:
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_pass app:9000;
fastcgi_param SCRIPT_FILENAME $document_root$fastcgi_script_name;
include fastcgi_params;
}
location ~ /\. {
deny all;
}
}
Perhatikan fastcgi_pass app:9000 ini menggunakan nama service app sebagai hostname. Docker Compose otomatis resolve nama ini ke IP container yang benar.
Menjalankan Semuanya
# Build dan start semua container
docker compose up -d --build
# Lihat status container
docker compose ps
# Lihat log (real-time)
docker compose logs -f
# Lihat log specific service
docker compose logs -f app
Kalau semua berjalan benar, kamu akan melihat empat container running:
NAME STATUS PORTS
myapp-php Up 2 minutes 9000/tcp
myapp-mysql Up 2 minutes 0.0.0.0:3306->3306/tcp
myapp-redis Up 2 minutes 0.0.0.0:6379->6379/tcp
myapp-nginx Up 2 minutes 0.0.0.0:80->80/tcp
Buka http://localhost di browser. Done. Seluruh stack sudah jalan.
Tips dan Trik yang Sering Terlewat
1. Environment Variables dengan .env file
Jangan hardcode password di docker-compose.yml. Buat file .env:
MYSQL_ROOT_PASSWORD=rahasia123
MYSQL_DATABASE=myapp
MYSQL_USER=appuser
MYSQL_PASSWORD=apppass123
Lalu referensikan di compose file:
db:
image: mysql:8.0
environment:
MYSQL_ROOT_PASSWORD: ${MYSQL_ROOT_PASSWORD}
MYSQL_DATABASE: ${MYSQL_DATABASE}
Tambahkan .env ke .gitignore. Jangan pernah commit credentials ke repository.
2. Health Check untuk Dependency yang Sehat
depends_on hanya menunggu container start, bukan ready. MySQL mungkin belum siap menerima koneksi saat app sudah mulai. Tambahkan health check:
db:
image: mysql:8.0
healthcheck:
test: ["CMD", "mysqladmin", "ping", "-h", "localhost"]
interval: 10s
timeout: 5s
retries: 5
environment:
MYSQL_ROOT_PASSWORD: ${MYSQL_ROOT_PASSWORD}
app:
build: .
depends_on:
db:
condition: service_healthy
Sekarang app baru mulai setelah MySQL benar-benar ready.
3. Shortcut Perintah Sehari-hari
# Masuk ke container app (shell)
docker compose exec app sh
# Jalankan artisan/migrate di container
docker compose exec app php artisan migrate
# Jalankan composer install di container
docker compose exec app composer install
# Restart satu service saja
docker compose restart app
# Stop semua, hapus volume (fresh start)
docker compose down -v
# Rebuild satu service setelah ubah Dockerfile
docker compose up -d --build app
Mengatasi Masalah Umum
Port sudah dipakai: Kalau port 3306 atau 80 sudah dipakai service lain di host, ganti mapping-nya:
ports:
- "3307:3306" # host:container
Permission denied pada volume: Container Linux sering punya masalah UID/GID mismatch. Fix dengan:
# Di Dockerfile, set user ID sama dengan host
RUN addgroup -g 1000 -S appuser && adduser -u 1000 -S appuser -G appuser
USER appuser
Perubahan file tidak ter-deteksi: Di macOS dan Windows, bind mount bisa lambat karena filesystem virtual. Solusinya pakai Docker Desktop's VirtioFS atau gRPC FUSE (Settings Resources File Sharing).
Container restart terus-menerus: Cek log dengan docker compose logs app. Biasanya error di aplikasi, bukan di Docker.
Workflow Sehari-hari dengan Docker Compose
Setelah environment ter-setup, workflow kamu jadi sangat sederhana:
- Pagi hari:
docker compose up -d - Coding seperti biasa di editor favorit
- Perubahan file langsung ter-reflect (bind mount)
- Butuh console?
docker compose exec app sh - Pulang:
docker compose down
Tidak ada lagi "works on my machine". Semua developer di team punya environment yang identik. Baru join? Clone repo, docker compose up -d, selesai. Tidak perlu install PHP, MySQL, atau Redis secara lokal.
Kesimpulan
Docker Compose bukan lagi "nice to have" ini sudah jadi standar development modern. Dengan satu file YAML, kamu bisa mendefinisikan seluruh stack aplikasi dan menjalankannya dengan satu command. Tidak ada lagi dependency hell, tidak ada lagi "coba install ini dulu".
Setup pertama mungkin butuh 30 menit sampai 1 jam (tergantung kompleksitas stack). Tapi setelahnya, onboarding developer baru cuma butuh 5 menit. Itu investasi yang sangat worth it untuk setiap team.
Kalau kamu belum pernah pakai Docker Compose, mulai dari yang simpel satu app + satu database. Nanti kalau sudah nyaman, tambah Redis, queue worker, atau service lainnya. Jangan langsung bikin 10 container sekaligus.
Punya pertanyaan atau pengalaman seru dengan Docker Compose? Share di kolom komentar di bawah!