docker-compose.yml #
File docker-compose.yml adalah dokumen yang mendefinisikan stack aplikasi multi-container. Formatnya YAML, dengan struktur hierarkis yang membagi konfigurasi menjadi beberapa bagian logis: service (wajib), volume, network, config, dan secret.
Artikel ini membahas anatomi file docker-compose.yml secara menyeluruh — apa saja top-level key, bagaimana mereka saling terkait, dan konvensi yang sebaiknya diikuti agar file tetap mudah dibaca dan di-maintain.
Top-Level Keys #
File docker-compose.yml paling lengkap punya beberapa top-level keys. Beberapa wajib, beberapa opsional.
# docker-compose.yml
version: "3.8" # opsional di Compose V2
services: # WAJIB
web:
...
api:
...
volumes: # opsional
db-data:
networks: # opsional
backend:
configs: # opsional, untuk file konfigurasi
nginx.conf:
secrets: # opsional, untuk data sensitif
db-password:
services — Wajib #
services adalah satu-satunya top-level key yang wajib. Ia berisi daftar service yang akan dijalankan. Setiap service merepresentasikan satu container (atau beberapa container jika di-scale).
volumes — Untuk Data Persisten #
volumes mendeklarasikan volume yang digunakan service. Jika service punya field volumes: yang merujuk ke volume yang belum dideklarasikan, Compose akan otomatis membuatkannya. Tapi best practice adalah deklarasi eksplisit di top-level.
networks — Untuk Isolasi #
networks mendeklarasikan network yang digunakan service. Secara default, Compose membuat satu network untuk semua service di file. Untuk konfigurasi yang lebih advanced, kamu bisa mendefinisikan beberapa network.
configs — Untuk File Konfigurasi #
configs memungkinkan kamu mounting file atau direktori ke dalam container. Bedanya dengan bind mount: config didefinisikan di Compose file dan di-inject saat runtime.
secrets — Untuk Data Sensitif #
secrets mirip dengan configs tapi untuk data sensitif seperti password, API key, dan sertifikat. Secrets disimpan di Docker’s secret storage (terenkripsi di production) dan di-mount ke /run/secrets/ di container.
Versi schema: Sejak Docker Compose V2, fieldversion:di top-level menjadi opsional dan sudah di-deprecate. Jika kamu menggunakan Compose V2, hilangkanversion:dari file. Ini membuat file lebih future-proof dan menghindari error “version tidak dikenal”.
Anatomi Service #
Setiap service di bawah services: punya banyak field. Tidak semua wajib, tapi masing-masing punya peran spesifik.
Field Dasar #
services:
web:
image: nginx:alpine # image yang dipakai
# ATAU
build: ./web # path ke Dockerfile
container_name: my-nginx # opsional, nama container
hostname: web-server # opsional, hostname di dalam container
ports:
- "8080:80" # port mapping
environment:
- NODE_ENV=production # env var
- DEBUG=false
volumes:
- ./html:/usr/share/nginx/html:ro
- db-data:/var/lib/data
networks:
- frontend
- backend
depends_on:
- api
- db
restart: unless-stopped
user: "1000:1000"
working_dir: /app
command: ["nginx", "-g", "daemon off;"]
entrypoint: ["/docker-entrypoint.sh"]
Image vs Build #
Service bisa di-spesifikasikan dengan image: (langsung pakai image dari registry) atau build: (build dari Dockerfile lokal).
services:
# Pakai image dari Docker Hub
redis:
image: redis:7-alpine
# Build dari Dockerfile lokal
api:
build:
context: ./api
dockerfile: Dockerfile.dev
args:
- NODE_ENV=development
# Build DAN kasih tag
api-prod:
build: ./api
image: myregistry.com/api:v1.2.3
Saat menggunakan build:, image yang dihasilkan disimpan di cache Docker lokal. Untuk multi-stage, Compose otomatis menggunakan stage yang ditentukan di Dockerfile (biasanya stage terakhir).
Ports #
Field ports memetakan port host ke port container.
ports:
- "8080:80" # host:container
- "127.0.0.1:8080:80" # host_ip:host_port:container_port
- "8443:443" # HTTPS
- "3000" # random host port, expose 3000
Format singkat "HOST:CONTAINER" adalah yang paling umum. Untuk kasus khusus (binding ke IP tertentu), gunakan format panjang.
Untuk production, hindari- "80:80"atau port rendah lainnya di host. Port di bawah 1024 butuh root untuk binding. Gunakan port tinggi di host (mis."8080:80") lalu reverse proxy.
Environment Variables #
services:
api:
environment:
- NODE_ENV=production
- DATABASE_URL=postgres://user:pass@db:5432/myapp
- DEBUG=false
# Atau pakai map syntax
environment:
NODE_ENV: production
DATABASE_URL: postgres://user:pass@db:5432/myapp
# Pakai file .env
env_file:
- .env
- .env.production
env_file membaca file .env dan inject variabelnya ke service. File .env harus di-.gitignore jika berisi secret.
Volumes #
Field volumes punya beberapa format.
services:
web:
volumes:
# Named volume
- db-data:/var/lib/data
# Bind mount
- ./html:/usr/share/nginx/html
# Bind mount dengan opsi
- ./html:/usr/share/nginx/html:ro
# Anonymous volume
- /var/lib/data
# tmpfs
- type: tmpfs
source: /tmp
target: /tmp
tmpfs:
size: 100m
Format source:target:options adalah yang paling eksplisit dan disarankan. Format singkat - source:target juga valid dan sering dipakai.
Networks #
Service otomatis join ke network default yang dibuat Compose. Untuk custom network:
services:
web:
networks:
- frontend
- backend
networks:
frontend:
backend:
driver: bridge
driver_opts:
com.docker.network.bridge.name: br-backend
Service web sekarang punya dua network interface. Ia bisa komunikasi dengan service lain di frontend maupun backend. Service di backend tidak bisa di-reach dari service di frontend kecuali di-expose juga.
Depends On #
depends_on menentukan urutan startup.
services:
web:
depends_on:
- api
api:
depends_on:
- db
- redis
Dalam contoh di atas, db dan redis start duluan, lalu api, lalu web. Tapi — penting — depends_on hanya menunggu container start, bukan service siap menerima request.
Untuk benar-benar menunggu service siap (mis. database sudah menerima connection), gunakan healthcheck + depends_on: { ... condition: service_healthy }.
services:
api:
depends_on:
db:
condition: service_healthy
db:
image: postgres:16
healthcheck:
test: ["CMD-SHELL", "pg_isready -U postgres"]
interval: 5s
timeout: 5s
retries: 5
api sekarang tidak start sampai healthcheck db mengembalikan status healthy. Ini lebih reliable untuk dependency yang butuh waktu inisialisasi.
Restart Policy #
services:
web:
restart: "no" # default
restart: always # selalu restart
restart: on-failure # restart hanya kalau exit code non-zero
restart: unless-stopped # restart kecuali di-stop manual
Untuk production, unless-stopped atau always adalah pilihan yang aman. Untuk development, no (default) biasanya cukup karena kamu sendiri yang start/stop.
User, Working Directory, Command #
services:
app:
user: "1000:1000" # jalankan sebagai non-root
working_dir: /app
command: ["python", "app.py"]
entrypoint: ["/entrypoint.sh"]
command override CMD di Dockerfile. entrypoint override ENTRYPOINT. user membuat container jalan sebagai user tertentu (bukan root) untuk keamanan.
Resource Limits #
services:
api:
deploy:
resources:
limits:
cpus: "0.5"
memory: 512M
reservations:
cpus: "0.25"
memory: 256M
deploy adalah field yang biasa dipakai di Docker Swarm. Untuk Compose di single host, kamu bisa pakai deploy.resources (didukung di Compose V2) atau gunakan mem_limit dan cpus di top-level (legacy).
Contoh File Lengkap #
File docker-compose.yml untuk stack e-commerce sederhana:
services:
web:
build: ./web
ports:
- "3000:3000"
environment:
- API_URL=http://api:8080
depends_on:
api:
condition: service_healthy
networks:
- frontend
restart: unless-stopped
api:
build: ./api
environment:
- DATABASE_URL=postgres://user:pass@db:5432/shop
- REDIS_URL=redis://cache:6379
depends_on:
db:
condition: service_healthy
cache:
condition: service_healthy
networks:
- frontend
- backend
restart: unless-stopped
db:
image: postgres:16-alpine
environment:
- POSTGRES_USER=user
- POSTGRES_PASSWORD=pass
- POSTGRES_DB=shop
volumes:
- db-data:/var/lib/postgresql/data
healthcheck:
test: ["CMD-SHELL", "pg_isready -U user -d shop"]
interval: 10s
timeout: 5s
retries: 5
networks:
- backend
restart: unless-stopped
cache:
image: redis:7-alpine
healthcheck:
test: ["CMD", "redis-cli", "ping"]
interval: 10s
timeout: 3s
retries: 3
networks:
- backend
restart: unless-stopped
proxy:
image: nginx:alpine
ports:
- "80:80"
- "443:443"
volumes:
- ./nginx.conf:/etc/nginx/nginx.conf:ro
- ./certs:/etc/nginx/certs:ro
depends_on:
- web
networks:
- frontend
restart: unless-stopped
volumes:
db-data:
networks:
frontend:
backend:
Stack ini punya 5 service: web (frontend), api (backend), database, cache, dan reverse proxy. Network dipisah jadi frontend dan backend — web dan proxy di frontend, database dan cache di backend yang tidak di-expose ke host.
Konvensi Penulisan #
Indentasi dan Formatting #
Gunakan indentasi 2 spasi (standar YAML). Jangan pakai tab. Compose parser tidak menerima tab.
# BAGUS
services:
web:
image: nginx
ports:
- "8080:80"
# BURUK
services:
web:
image: nginx
ports:
- "8080:80"
Panjang Baris #
Usahakan tidak lebih dari 120 karakter per baris agar mudah dibaca di code review.
Komentar #
Gunakan komentar untuk menjelaskan WHY, bukan WHAT. Kode menjelaskan what; komentar menjelaskan why.
services:
api:
# PostgreSQL dengan tuning untuk high-concurrency workload
image: postgres:16-alpine
command: postgres -c max_connections=200
Urutan Service #
Tidak ada aturan baku, tapi beberapa konvensi yang baik:
- Service yang paling penting duluan (mis. web, api)
- Database/cache di akhir
- Helper service (proxy, monitoring) sesuai peran
- Group service yang terkait bersama
Konsistensi Penamaan #
- Service: kebab-case (
user-service,auth-service) - Volume: kebab-case (
user-uploads,app-cache) - Network: deskriptif (
frontend,backend,monitoring)
Multiple Compose Files #
Untuk environment berbeda, kamu bisa override dengan file tambahan.
# Default + development override
docker compose -f docker-compose.yml -f docker-compose.dev.yml up
# Default + production override
docker compose -f docker-compose.yml -f docker-compose.prod.yml up
Override file menimpa field di default. Ini memungkinkan konfigurasi base yang sama dipakai untuk banyak environment.
Validasi #
Sebelum menjalankan, validasi file Compose-mu.
# Tampilkan parsed config
docker compose config
# Tampilkan dan highlight issues
docker compose config --quiet
docker compose config membaca semua file (default + override + env file), merge, parse, lalu tampilkan hasilnya. Jika ada error syntax atau referensi yang tidak valid, ia akan muncul di sini.
--quiet flag hanya menampilkan error dan warning tanpa full output — cocok untuk CI/CD validation step.
Referensi Lanjutan #
Profiles #
Docker Compose punya fitur profiles yang memungkinkan kamu group service berdasarkan tujuan (development, testing, production). Service dengan profile hanya dijalankan jika profile tersebut diaktifkan.
services:
web:
image: nginx
ports:
- "80:80"
debug-tools:
image: alpine
profiles: ["debug"]
command: sleep infinity
load-generator:
image: loadtest
profiles: ["load-test"]
# Default — debug-tools dan load-generator tidak jalan
docker compose up
# Dengan profile debug
docker compose --profile debug up
# Dengan profile load-test
docker compose --profile load-test up
Profile sangat berguna untuk development environment di mana kamu kadang butuh tools tambahan (debugger, log viewer, mock server) tanpa harus menambahkannya ke setup default.
Variable Substitution #
YAML mendukung variable substitution dari environment host.
services:
api:
image: myapp:${VERSION:-latest}
ports:
- "${PORT:-8080}:8080"
environment:
- LOG_LEVEL=${LOG_LEVEL}
${VAR} mengambil nilai dari environment. ${VAR:-default} mengambil nilai default jika VAR tidak di-set. Compose membaca environment host saat file di-parse.
Multiple Compose Files dan Merge #
Field overriding saat pakai multiple files:
docker compose -f base.yml -f override.yml up
Saat override.yml punya service yang sama dengan base.yml, field di override menimpa field di base. Tapi lists dan maps di-merge, bukan replace.
# base.yml
services:
web:
environment:
- LOG_LEVEL=info
- NODE_ENV=production
# override.yml
services:
web:
environment:
- LOG_LEVEL=debug
Hasil: LOG_LEVEL=debug (overridden) + NODE_ENV=production (preserved).
JSON Syntax #
Compose juga support JSON sebagai alternatif YAML. Berguna jika YAML tidak available di toolchain-mu.
{
"services": {
"web": {
"image": "nginx",
"ports": ["80:80"]
}
}
}
Tapi YAML lebih readable untuk konfigurasi yang kompleks, jadi JSON jarang dipakai kecuali untuk konfigurasi yang di-generate programmatically.
Tools pendukung: Beberapa IDE punya extension untuk docker-compose.yml — auto-complete, validation, dan visualisasi service dependency. Rekomendasi: VSCode dengan extension “Docker” atau “Kubernetes” (juga support Compose).
Ringkasan #
docker-compose.ymladalah file YAML deklaratif yang mendefinisikan seluruh stack aplikasi multi-container.- Top-level keys:
services(wajib),volumes,networks,configs,secrets. Fieldversion:sudah opsional di Compose V2.- Setiap service punya banyak field:
image/build,ports,environment,volumes,networks,depends_on,restart,command, dll.depends_on≠ service ready —depends_onhanya menunggu container start. Untuk benar-benar menunggu service siap, pakai healthcheck +condition: service_healthy.- Network isolation — pisahkan service ke beberapa network (frontend, backend) untuk membatasi komunikasi.
- Resource limits penting di production untuk mencegah satu service menghabiskan semua resource.
- Validasi dengan
docker compose configsebelum menjalankan, untuk catch error syntax dan referensi invalid.- Override file (
-f docker-compose.dev.yml) memungkinkan konfigurasi base yang sama untuk banyak environment.- Konvensi: indentasi 2 spasi, tidak pakai tab, komentar menjelaskan why, nama service dan volume deskriptif.
← Sebelumnya: Apa itu Docker Compose? Berikutnya: Multi-Container →