Skip to content

Docker Compose

Pada praktiknya di pekerjaan, kamu jarang mengetik banyak perintah docker run untuk menjalankan seluruh aplikasi. Docker Compose memungkinkan kamu menuliskan konfigurasi beberapa container dalam satu file, lalu menjalankannya dengan satu command.

File konfigurasi tersebut biasanya bernama compose.yaml. Docker Compose juga membaca nama lama docker-compose.yml, tetapi di course ini kita menggunakan compose.yaml.

Command yang digunakan adalah docker compose, dengan spasi. Docker Compose tetap menggunakan Docker CLI dan Docker Engine. Docker CLI mengirim konfigurasi Compose ke Docker Engine, lalu Engine membuat dan menjalankan container yang diperlukan.

Membuat project Compose

Kita akan membuat contoh project dengan dua service:

  • web, yaitu web server Nginx.
  • db, yaitu database PostgreSQL.

Tujuan contoh ini adalah menunjukkan cara Compose menjalankan beberapa container, membuat network antar-container, dan menyimpan data database pada named volume.

Buat folder project agar semua file contoh tersimpan di satu tempat:

sh
mkdir compose-demo
cd compose-demo
mkdir html

Buat file html/index.html. File ini akan ditampilkan oleh Nginx:

html
<h1>Hello from Docker Compose</h1>

Selanjutnya, buat file compose.yaml dengan isi berikut:

yaml
services:
  web:
    image: nginx:1.27-alpine
    ports:
      - "8080:80"
    volumes:
      - ./html:/usr/share/nginx/html:ro

  db:
    image: postgres:16
    environment:
      POSTGRES_PASSWORD: ${POSTGRES_PASSWORD}
      POSTGRES_DB: app
    volumes:
      - pgdata:/var/lib/postgresql/data
    restart: unless-stopped

volumes:
  pgdata:

Buat file .env di folder yang sama dengan compose.yaml. File ini menyimpan nilai environment variable untuk local development:

dotenv
POSTGRES_PASSWORD=secret

Jangan commit file .env yang berisi secret ke Git. Penjelasan lebih lengkap tentang environment variable tersedia di lesson Environment Variable.

Bagian penting dalam compose.yaml

  • services: daftar service yang akan dijalankan. Setiap service, seperti web dan db, menghasilkan satu container.
  • image: image yang digunakan untuk membuat container, sama seperti image pada docker run.
  • ports: memetakan port dari host ke container, sama seperti option -p. Pada contoh ini, port 8080 di laptop diarahkan ke port 80 pada container Nginx.
  • volumes: memasang storage ke container. ./html adalah bind mount dari folder project, sedangkan pgdata adalah named volume yang dikelola Docker. Detailnya dibahas di lesson Docker Volume.
  • environment: memberikan environment variable ke container, sama seperti option -e pada docker run.
  • restart: unless-stopped: meminta Docker menjalankan kembali container ketika Docker Engine restart, kecuali container tersebut memang dihentikan oleh user.

Pada ./html:/usr/share/nginx/html:ro, ./html adalah folder di laptop, sedangkan /usr/share/nginx/html adalah path di dalam container. Suffix :ro berarti container hanya boleh membaca isi folder tersebut, bukan mengubahnya.

Network dan nama service

Saat menjalankan project, Compose membuat network default untuk project tersebut. Container pada network ini dapat saling menghubungi menggunakan nama service sebagai hostname.

Karena itu, service web dapat menghubungi PostgreSQL menggunakan hostname db dan port 5432, yaitu port PostgreSQL di dalam network Docker. Port tersebut berbeda dari port 8080 yang digunakan untuk mengakses Nginx dari laptop.

Contoh connection string dari app:

text
postgres://postgres:${POSTGRES_PASSWORD}@db:5432/app

Perhatikan bahwa kita memakai @db:5432. Gunakan db, bukan localhost. localhost dari dalam container web menunjuk ke container web itu sendiri, bukan ke container database.

Penjelasan tentang komunikasi antar-container tersedia di lesson Docker Network.

Menjalankan service dengan up

Pastikan terminal berada di folder yang berisi compose.yaml. Jalankan semua service di background agar terminal tetap bisa digunakan:

sh
docker compose up -d

up membuat network, volume, dan container yang dibutuhkan, lalu menjalankan service. Option -d berarti detached, sehingga log tidak terus mengalir di terminal.

Buka http://localhost:8080 di browser. Halaman tersebut membuktikan bahwa container web berjalan dan port host berhasil diarahkan ke port Nginx.

Melihat log service

Untuk memeriksa proses startup semua service, tampilkan log dari seluruh service:

sh
docker compose logs -f

Option -f berarti follow. Terminal akan terus menampilkan log baru sampai kamu menghentikannya dengan Ctrl+C.

Untuk memeriksa log database saja, sebutkan nama service-nya:

sh
docker compose logs -f db

Command ini hanya menampilkan log service db, bukan log service web.

Menghentikan service dengan down

Untuk menghentikan dan menghapus container serta network yang dibuat Compose, jalankan:

sh
docker compose down

Command ini tidak menghapus named volume pgdata, sehingga data database tetap tersedia jika service dijalankan lagi.

Jika ingin menghapus container, network, dan named volume sekaligus, gunakan:

sh
docker compose down -v

Option -v menghapus named volume beserta data di dalamnya. Gunakan command ini hanya jika memang ingin mereset data local.

Urutan start dengan depends_on

depends_on dapat digunakan ketika sebuah service bergantung pada service lain. Contoh berikut meminta Compose menjalankan db sebelum web:

yaml
services:
  web:
    image: nginx:1.27-alpine
    depends_on:
      - db

  db:
    image: postgres:16

depends_on hanya mengatur urutan start container. Option ini tidak berarti PostgreSQL sudah siap menerima koneksi ketika container db sudah dimulai.

App yang mencoba connect ke database terlalu cepat masih bisa gagal karena PostgreSQL sedang melakukan startup. Pada praktiknya di pekerjaan, app biasanya memiliki mekanisme retry ketika koneksi database belum tersedia.

Dari Dockerfile ke Compose

Pada contoh sebelumnya, service web dan db menggunakan image yang sudah dibuat oleh pihak lain melalui field image. Compose akan mengambil image tersebut jika belum tersedia di laptop, bukan membuatnya dari source code kita.

Untuk app yang image-nya dibuat dari Dockerfile sendiri, gunakan field build. Contoh berikut meminta Compose menggunakan Dockerfile dari folder project saat ini:

yaml
services:
  api:
    build: .
    ports:
      - "3000:3000"
    environment:
      DATABASE_URL: postgres://${POSTGRES_USER}:${POSTGRES_PASSWORD}@db:5432/app
    depends_on:
      - db

build: . meminta Compose menggunakan Dockerfile dari folder project saat ini. Compose akan menggunakan image hasil build tersebut untuk menjalankan service api, sehingga kamu tidak perlu menjalankan docker build secara manual setiap kali menjalankan project.

Setelah mengubah Dockerfile atau source code yang ikut disalin ke image, gunakan --build agar Compose membuat ulang image sebelum menjalankan service:

sh
docker compose up -d --build

Option --build hanya berpengaruh pada service yang menggunakan field build. Service yang hanya menggunakan field image, seperti web dan db pada contoh sebelumnya, tidak dibuat ulang dari source code.

Tanpa --build, Compose dapat menggunakan image hasil build sebelumnya. Akibatnya, perubahan pada Dockerfile atau source code belum tentu masuk ke container yang baru dijalankan.


Jika kamu sudah bisa menjalankan app dan database dengan Compose, memeriksa log, serta membuat app berbicara dengan database melalui nama service, kamu sudah menguasai dasar Docker & Docker Compose. Selamat 🥳🥳.


Cek Cheatsheet Docker Essentials untuk melihat summary command Docker Essentials