Saya ingat pertama kali coba deploy aplikasi full-stack di VPS sendiri. Nginx error, MySQL port conflict, Redis gak jalan... semua error bertumpuk. Butuh waktu 2 hari cuma buat satu aplikasi jalan. Sekarang? Dengan Docker Compose, saya bisa deploy website + database + Redis dalam waktu kurang dari 10 menit. Serius, tanpa lebay.
Kalau kamu juga pernah frustasi ngatur environment development yang berantakan install MySQL di laptop, lupa versi, gak bisa reproduksi di server Docker Compose adalah solusi yang kamu butuhkan. Artikel ini akan guide kamu step-by-step, dari nol sampai aplikasi jalan di production-ready setup.
Docker Compose adalah tool dari Docker yang memungkinkan kamu mendefinisikan dan menjalankan multi-container application dalam satu file konfigurasi YAML. Bayangkan kamu punya satu "resep" yang isinya: nginx config, aplikasi PHP/Python/Node, database MySQL, dan cache Redis semua didefinisikan di satu file, dan satu command untuk menjalankan semuanya.
Banyak developer di Indonesia masih deploy dengan cara manual SSH ke server, install dependencies satu per satu, konfigurasi service by service. Masalahnya:
Docker Compose menyelesaikan semua masalah ini. Sekali kamu punya docker-compose.yml, deployment ke server baru tinggal satu command: docker compose up -d. Selesai.
Pastikan kamu sudah install Docker dan Docker Compose di system kamu. Untuk Docker Compose v2 yang terbaru, cukup install Docker Engine saja Compose sudah included sebagai plugin docker compose (tanpa hyphen).
# Cek versi Docker
docker --version
# Output: Docker version 24.0.7, build afdd53b
# Cek versi Compose
docker compose version
# Output: Docker Compose version v2.21.0
Belum install? Di Ubuntu/Debian cukup jalankan:
# Install Docker di Ubuntu
curl -fsSL https://get.docker.com -o get-docker.sh
sudo sh get-docker.sh
# Tambah user ke group docker (agar tidak perlu sudo)
sudo usermod -aG docker $USER
Setelah install, logout dan login ulang agar group membership aktif. Kalau kamu di Windows, install Docker Desktop yang include Docker Compose secara default.
Kita akan buat aplikasi sederhana: website PHP dengan Nginx sebagai web server, MySQL sebagai database, dan Redis sebagai cache/session store. Ini adalah stack yang sangat umum digunakan di production, jadi ilmunya langsung applicable ke project nyata.
Struktur foldernya seperti ini:
my-app/
docker-compose.yml # Main config file
nginx/
default.conf # Nginx configuration
php/
Dockerfile # Custom PHP image
app/
index.php # Main application
db-test.php # Database connection test
mysql/
init.sql # Database initialization
File ini adalah jantung dari seluruh setup. Di sinilah kamu mendefinisikan semua service, network, dan volume yang dibutuhkan.
version: '3.8'
services:
# Web Server
nginx:
image: nginx:alpine
ports:
- "8080:80"
volumes:
- ./app:/var/www/html
- ./nginx/default.conf:/etc/nginx/conf.d/default.conf
depends_on:
- php
networks:
- app-network
# PHP Application
php:
build:
context: ./php
dockerfile: Dockerfile
volumes:
- ./app:/var/www/html
networks:
- app-network
# MySQL Database
mysql:
image: mysql:8.0
environment:
MYSQL_ROOT_PASSWORD: secretpassword
MYSQL_DATABASE: myapp_db
MYSQL_USER: appuser
MYSQL_PASSWORD: apppass123
volumes:
- mysql-data:/var/lib/mysql
- ./mysql/init.sql:/docker-entrypoint-initdb.d/init.sql
ports:
- "3306:3306"
networks:
- app-network
# Redis Cache
redis:
image: redis:7-alpine
ports:
- "6379:6379"
command: redis-server --requirepass redispass123 --maxmemory 128mb --maxmemory-policy allkeys-lru
volumes:
- redis-data:/data
networks:
- app-network
networks:
app-network:
driver: bridge
volumes:
mysql-data:
redis-data:
Beberapa hal penting di sini:
Nginx perlu tahu bahwa request ke PHP harus dikirim ke PHP-FPM container, bukan ke file PHP langsung. Karena di Docker, nama host untuk PHP service adalah "php" (sesuai nama di docker-compose.yml).
server {
listen 80;
server_name localhost;
root /var/www/html;
index index.php index.html;
location / {
try_files $uri $uri/ /index.php?$query_string;
}
location ~ \.php$ {
fastcgi_split_path_info ^(.+\.php)(/.+)$;
fastcgi_pass php:9000;
fastcgi_index index.php;
include fastcgi_params;
fastcgi_param SCRIPT_FILENAME $document_root$fastcgi_script_name;
fastcgi_param PATH_INFO $fastcgi_path_info;
}
}
Yang krusial di sini adalah baris fastcgi_pass php:9000. Docker Compose memberikan DNS internal untuk setiap service, jadi kita bisa akses PHP-FPM menggunakan hostname "php" (sesuai nama service di compose file). Tidak perlu IP address, tidak perlu linked containers, cukup nama service.
Kita butuh image PHP yang sudah include extension yang diperlukan oleh aplikasi. Membuat Dockerfile sendiri memberi kontrol penuh atas versi PHP dan extensions yang diinstall.
FROM php:8.2-fpm-alpine
# Install system dependencies
RUN apk add --no-cache \
libzip-dev \
icu-dev \
oniguruma-dev \
linux-headers
# Install PHP extensions
RUN docker-php-ext-install \
pdo_mysql \
mysqli \
zip \
intl \
mbstring \
opcache
# Install Redis extension
RUN apk add --no-cache $PHPIZE_DEPS \
&& pecl install redis \
&& docker-php-ext-enable redis
# Set recommended PHP settings
RUN echo "opcache.enable=1" >> /usr/local/etc/php/conf.d/opcache.ini \
&& echo "opcache.memory_consumption=128" >> /usr/local/etc/php/conf.d/opcache.ini \
&& echo "opcache.max_accelerated_files=10000" >> /usr/local/etc/php/conf.d/opcache.ini
# Set working directory
WORKDIR /var/www/html
EXPOSE 9000
CMD ["php-fpm"]
Extension yang kita install:
Kita buat file PHP sederhana yang mendemonstrasikan integrasi MySQL dan Redis. File ini bukan production-ready application, tapi cukup untuk memahami bagaimana semua service terkoneksi.
<?php
// /app/index.php
$startTime = microtime(true);
// Koneksi ke MySQL
try {
$pdo = new PDO(
'mysql:host=mysql;dbname=myapp_db;charset=utf8mb4',
'appuser',
'apppass123',
[PDO::ATTR_ERRMODE => PDO::ERRMODE_EXCEPTION]
);
$dbStatus = "<span style='color: green;'>Connected</span>";
} catch (PDOException $e) {
$dbStatus = "<span style='color: red;'>Error: " . $e->getMessage() . "</span>";
}
// Koneksi ke Redis
try {
$redis = new Redis();
$redis->connect('redis', 6379);
$redis->auth('redispass123');
// Test cache: simpan dan ambil data
$cacheKey = 'page_views';
$views = $redis->incr($cacheKey);
$redisStatus = "<span style='color: green;'>Connected</span>";
} catch (Exception $e) {
$redisStatus = "<span style='color: red;'>Error: " . $e->getMessage() . "</span>";
$views = 'N/A';
}
$loadTime = round((microtime(true) - $startTime) * 1000, 2);
?>
<!DOCTYPE html>
<html>
<head>
<title>Docker Compose Full-Stack Demo</title>
</head>
<body>
<h1>Docker Compose Full-Stack</h1>
<p>Nginx: <?= $dbStatus ?></p>
<p>MySQL: <?= $dbStatus ?></p>
<p>Redis: <?= $redisStatus ?> | Views: <?= $views ?></p>
<p>Load time: <?= $loadTime ?>ms</p>
</body>
</html>
Yang perlu diperhatikan dari kode di atas:
File init.sql akan dijalankan otomatis saat MySQL container pertama kali start. Kamu bisa define schema di sini.
-- /mysql/init.sql
CREATE TABLE IF NOT EXISTS users (
id INT AUTO_INCREMENT PRIMARY KEY,
name VARCHAR(100) NOT NULL,
email VARCHAR(100) UNIQUE NOT NULL,
created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP
);
INSERT INTO users (name, email) VALUES
('Budi Santoso', '[email protected]'),
('Siti Rahayu', '[email protected]'),
('Andi Prasetyo', '[email protected]')
ON DUPLICATE KEY UPDATE name = VALUES(name);
Sekarang semua file sudah siap. Saatnya build dan jalankan. Ini adalah bagian paling satisfying satu command untuk menjalankan empat service sekaligus.
# Build dan jalankan semua service
docker compose up -d --build
# Lihat status semua container
docker compose ps
# Lihat log (real-time)
docker compose logs -f
# Lihat log service tertentu
docker compose logs -f nginx
docker compose logs -f php
docker compose logs -f mysql
Setelah semua container start, buka browser dan akses http://localhost:8080. Kamu akan melihat halaman yang menunjukkan status semua service.
Coba refresh halaman beberapa kali angka page views di Redis counter akan bertambah. Data ini tersimpan di Redis, bukan database, sehingga sangat cepat.
Berikut command-command yang sering kamu pakai saat development:
# Stop semua service (data tetap tersimpan di volumes)
docker compose down
# Stop DAN hapus volumes (fresh start dari nol)
docker compose down -v
# Rebuild service tertentu (contoh: setelah update Dockerfile)
docker compose up -d --build php
# Masuk ke container (seperti SSH ke server)
docker compose exec php sh
docker compose exec mysql mysql -u root -p
# Jalankan command satu kali di container
docker compose exec php php artisan migrate
docker compose exec php composer install
# Cek resource usage
docker stats
# Hapus semua container, image, dan volume (nuclear option)
docker system prune -a --volumes
Docker Compose mendukung multiple compose files untuk environment yang berbeda. Kamu bisa punya docker-compose.yml untuk base config, dan docker-compose.prod.yml untuk production overrides.
# docker-compose.prod.yml
version: '3.8'
services:
nginx:
ports:
- "80:80"
restart: always
php:
restart: always
mysql:
restart: always
environment:
MYSQL_ROOT_PASSWORD: ${MYSQL_ROOT_PASSWORD}
MYSQL_PASSWORD: ${MYSQL_PASSWORD}
command: --default-authentication-plugin=mysql_native_password
redis:
restart: always
command: redis-server --requirepass ${REDIS_PASSWORD} --maxmemory 512mb
Jalankan di production dengan:
# Gabungkan base + prod config
docker compose -f docker-compose.yml -f docker-compose.prod.yml up -d --build
Perbedaan utama development vs production:
.env file, bukan hardcode di YAMLDi production, kamu pasti butuh SSL. Cara termudah adalah dengan menambahkan Certbot sebagai service di compose file:
# Tambahkan di docker-compose.prod.yml
services:
certbot:
image: certbot/certbot
volumes:
- certbot-etc:/etc/letsencrypt
- certbot-var:/var/lib/letsencrypt
- ./app:/var/www/html
entrypoint: "/bin/sh -c 'trap exit TERM; while :; do certbot renew; sleep 12h; done;'"
Persiapan SSL memang butuh beberapa langkah (install certbot, generate certificate, auto-renew), tapi setelah terpasang, semua HTTP traffic otomatis redirect ke HTTPS.
Beberapa optimasi yang saya pelajari dari pengalaman:
nginx:alpine cuma 40MB vs nginx yang 180MBallkeys-lru otomatis hapus key yang jarang dipakai saat memory penuh. Cocok untuk caching.dockerignore untuk exclude node_modules, .git, dll saat build context
# .dockerignore
.git
node_modules
.env
*.md
.vscode
docker-compose*.yml
Saat pertama kali pakai Docker Compose, beberapa error yang sering muncul dan cara mengatasinya:
sudo systemctl start docker atau login ulang setelah usermodsudo lsof -i :8080, ganti port di docker-compose.ymldepends_on dengan healthcheck, atau jalankan docker compose restart php
# Debugging tips
docker compose logs --tail=50 # Lihat 50 baris log terakhir
docker compose ps # Cek status semua service
docker compose down && docker compose up -d --build # Full rebuild
Docker Compose mengubah cara saya deploy aplikasi dari yang tadinya manual dan error-prone menjadi repeatable dan reliable. Sekarang setup server baru tinggal clone repo, docker compose up -d --build, dan aplikasi langsung jalan.
Yang paling saya suka: semua konfigurasi tersimpan sebagai code di repository. Tidak ada lagi "di server produksi saya pakai MySQL 5.7, di development pakai 8.0" semua container menggunakan versi yang sama persis.
Kalau kamu masih deploy manual di 2026, coba pakai Docker Compose untuk project berikutnya. Sekali terbiasa, kamu tidak akan mau kembali.
Sudah coba Docker Compose? Atau masih pakai cara manual? Ceritakan pengalaman kamu di comment section saya penasaran stack apa yang kamu pakai untuk deployment.