TDCTF Academy Logo TDCTF ACADEMY

Basic Docker Compose

Apa itu Docker Compose?

Docker Compose adalah tool untuk mendefinisikan dan menjalankan aplikasi multi-container dengan satu file konfigurasi YAML. Dengan satu perintah docker compose up, semua service, network, dan volume yang dibutuhkan akan dibuat dan dijalankan.

Instalasi Docker Compose

Docker Compose Plugin (v2 - Versi Modern)

Di Docker versi terbaru, Compose sudah jadi plugin bawaan:

# Cek apakah compose sudah terinstall
docker compose version

Jika belum:

# Install plugin
sudo apt update
sudo apt install docker-compose-plugin

Legacy docker-compose (v1)

sudo curl -L "https://github.com/docker/compose/releases/latest/download/docker-compose-$(uname -s)-$(uname -m)" -o /usr/local/bin/docker-compose
sudo chmod +x /usr/local/bin/docker-compose
tip

Gunakan docker compose (v2) yang lebih baru. Jika butuh v1, gunakan docker-compose. Keduanya support file docker-compose.yml yang sama.

Anatomi docker-compose.yml

version: '3.8' # Versi format file (3.x untuk semua fitur modern)

services: # Daftar container/service
web: # Nama service (bisa diakses via hostname ini)
image: nginx:alpine # Image yang digunakan
container_name: web-nginx # Nama container (opsional)
ports:
- "80:80" # host_port:container_port
volumes:
- ./html:/usr/share/nginx/html:ro # Mount folder
environment:
- NGINX_HOST=example.com
networks:
- frontend
depends_on:
- api
restart: unless-stopped

volumes: # Named volumes untuk persistent data
db_data:

networks: # Custom networks
frontend:
backend:
internal: true # Internal = tidak bisa diakses dari luar

Template 1: MariaDB/MySQL + phpMyAdmin

version: '3.8'

services:
db:
image: mariadb:latest
container_name: mariadb-server
restart: unless-stopped
environment:
MYSQL_ROOT_PASSWORD: rootpassword123
MYSQL_DATABASE: app_db
MYSQL_USER: app_user
MYSQL_PASSWORD: app_password
ports:
- "3306:3306"
volumes:
- db_data:/var/lib/mysql
- ./init:/docker-entrypoint-initdb.d # Auto-import SQL saat pertama
networks:
- internal

phpmyadmin:
image: phpmyadmin:latest
container_name: phpmyadmin-ui
restart: unless-stopped
ports:
- "8080:80"
environment:
PMA_HOST: db # Hostname = nama service
PMA_PORT: 3306
UPLOAD_LIMIT: 100M
depends_on:
- db
networks:
- internal

volumes:
db_data:

networks:
internal:

Jalankan:

docker compose up -d

Akses phpMyAdmin di http://vuln.cybersecurity.or.id - login dengan root / rootpassword123.

tip

Untuk MySQL sebagai pengganti MariaDB, cukup ganti image: mariadb:latest dengan image: mysql:8.0.

Template 2: Nginx + PHP-FPM

version: '3.8'

services:
nginx:
image: nginx:alpine
container_name: web-nginx
restart: unless-stopped
ports:
- "80:80"
- "443:443"
volumes:
- ./html:/var/www/html # File PHP/HTML
- ./nginx/conf.d:/etc/nginx/conf.d # Konfigurasi Nginx custom
- ./nginx/ssl:/etc/nginx/ssl # SSL certificates
depends_on:
- php
networks:
- app

php:
image: php:8.2-fpm-alpine
container_name: web-php
restart: unless-stopped
volumes:
- ./html:/var/www/html
environment:
- PHP_MEMORY_LIMIT=256M
- PHP_MAX_EXECUTION_TIME=60
networks:
- app

networks:
app:

Contoh nginx/conf.d/default.conf:

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

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

location ~ \.php$ {
fastcgi_pass php:9000; # Hostname service PHP-FPM
fastcgi_index index.php;
fastcgi_param SCRIPT_FILENAME $document_root$fastcgi_script_name;
include fastcgi_params;
}
}

Template 3: Redis + Aplikasi Python

version: '3.8'

services:
redis:
image: redis:7-alpine
container_name: redis-cache
restart: unless-stopped
command: redis-server --requirepass redispass123 --appendonly yes
ports:
- "6379:6379"
volumes:
- redis_data:/data
networks:
- backend

app:
build:
context: ./app
dockerfile: Dockerfile
container_name: python-app
restart: unless-stopped
ports:
- "5000:5000"
environment:
REDIS_HOST: redis
REDIS_PORT: 6379
REDIS_PASSWORD: redispass123
depends_on:
- redis
volumes:
- ./app:/app
networks:
- backend

volumes:
redis_data:

networks:
backend:

Template 4: CTFd (Platform CTF)

version: '3.8'

services:
ctfd:
image: ctfd/ctfd:latest
container_name: ctfd-platform
restart: unless-stopped
ports:
- "8000:8000"
environment:
- DATABASE_URL=mysql+pymysql://ctfd:ctfdpassword@db/ctfd
- REDIS_URL=redis://:redispass@cache:6379
- SECRET_KEY=your-secret-key-here
- WORKERS=4
- LOG_FOLDER=/var/log/CTFd
volumes:
- ctfd_data:/var/log/CTFd
- ctfd_uploads:/var/uploads
depends_on:
db:
condition: service_healthy
cache:
condition: service_started
networks:
- ctfd

db:
image: mariadb:10.11
container_name: ctfd-db
restart: unless-stopped
environment:
MYSQL_ROOT_PASSWORD: rootpass
MYSQL_USER: ctfd
MYSQL_PASSWORD: ctfdpassword
MYSQL_DATABASE: ctfd
volumes:
- db_data:/var/lib/mysql
healthcheck:
test: ["CMD", "healthcheck.sh", "--connect", "--innodb_initialized"]
interval: 10s
timeout: 5s
retries: 5
networks:
- ctfd

cache:
image: redis:7-alpine
container_name: ctfd-cache
restart: unless-stopped
command: redis-server --requirepass redispass
volumes:
- cache_data:/data
networks:
- ctfd

volumes:
ctfd_data:
ctfd_uploads:
db_data:
cache_data:

networks:
ctfd:
tip

Template CTFd ini siap pakai untuk event CTF. Tinggal ganti SECRET_KEY dengan key random: python3 -c "import secrets; print(secrets.token_hex(16))"

Perintah Penting Docker Compose

Perintah Deskripsi
docker compose up -d Jalankan semua service di background
docker compose down Hentikan dan hapus semua container, network
docker compose down -v Down + hapus semua volume (⚠️ data hilang)
docker compose ps Lihat status semua service
docker compose logs -f Streaming log semua service
docker compose logs -f web Log service spesifik (contoh: web)
docker compose exec web sh Masuk ke container yang sedang berjalan
docker compose exec db mysql -u root -p Eksekusi command di dalam container
docker compose build Build ulang image (untuk service dengan build:)
docker compose build --no-cache Build tanpa cache
docker compose pull Pull image terbaru dari registry
docker compose restart Restart semua service
docker compose stop Stop tanpa hapus container
docker compose start Start container yang sudah di-stop
docker compose config Validasi dan lihat konfigurasi final
docker compose top Lihat proses yang berjalan di setiap service

Environment Variables (.env)

Buat file .env di folder yang sama dengan docker-compose.yml:

# .env
DB_ROOT_PASSWORD=supersecret
DB_NAME=app_db
APP_PORT=8080
NGINX_PORT=80

Lalu gunakan di docker-compose.yml:

services:
db:
image: mariadb:latest
environment:
MYSQL_ROOT_PASSWORD: ${DB_ROOT_PASSWORD}
MYSQL_DATABASE: ${DB_NAME}

app:
image: myapp:latest
ports:
- "${APP_PORT}:5000"
peringatan

Jangan commit file .env ke Git! Tambahkan ke .gitignore. Gunakan .env.example sebagai template.

Volumes & Persistent Data

Named Volumes (direkomendasikan)

volumes:
db_data:

services:
db:
volumes:
- db_data:/var/lib/mysql

Data disimpan di /var/lib/docker/volumes/ - aman, terkelola Docker.

Bind Mounts

services:
web:
volumes:
- ./html:/usr/share/nginx/html # Folder lokal : folder container
- ./config/nginx.conf:/etc/nginx/nginx.conf:ro # File spesifik (read-only)

Cocok untuk development - perubahan langsung terlihat tanpa rebuild.

Volume Permission Issues

# Solusi: set user ID di container
services:
php:
image: php:8.2-fpm
user: "${UID:-1000}:${GID:-1000}"
volumes:
- ./html:/var/www/html

Networks: Internal vs External

Internal Network

networks:
backend:
internal: true # Hanya bisa diakses antar service, tidak dari host

External Network (gabung dengan stack lain)

networks:
webnet:
external: true # Network sudah ada di luar compose

services:
web:
networks:
- webnet

Buat external network:

docker network create webnet

Multi-Network Setup

services:
web:
networks:
- frontend # Bisa diakses dari luar
- backend # Internal, komunikasi dengan DB

db:
networks:
- backend # Hanya backend, DB tidak terekspos

Troubleshooting

Masalah Solusi
port is already allocated Port sudah dipakai. Ganti port host di ports: atau hentikan service lain: sudo lsof -i :80
Cannot start service: permission denied Volume mount permission. Tambahkan user:"1000:1000" di service
service "db" didn't start Cek log: docker compose logs db. Mungkin port conflict atau env salah
Volume data hilang setelah down -v Jangan gunakan -v di down jika ingin data tetap ada
Container restart loop Cek log: docker compose logs --tail=50. Biasanya karena config error
Environment variable tidak terbaca Pastikan .env ada di folder yang sama, atau pakai docker compose --env-file .env.prod up
docker compose: command not found Install docker compose plugin: sudo apt install docker-compose-plugin

Kesimpulan

Docker Compose adalah wajib untuk development dan deployment multi-service. Dengan satu file YAML, kamu bisa mendefinisikan database, backend, frontend, cache, dan lainnya yang saling terhubung.

Workflow standar:

# 1. Buat docker-compose.yml
# 2. Setup .env untuk konfigurasi
# 3. Jalankan
docker compose up -d

# 4. Cek status
docker compose ps

# 5. Lihat log jika ada error
docker compose logs -f

# 6. Masuk ke container untuk debugging
docker compose exec web sh

# 7. Hentikan
docker compose down

Dengan template-template di atas, kamu bisa langsung deploy aplikasi apapun - dari website sederhana sampai platform CTF penuh!

PADA HALAMAN INI