Server/Infra

Docker Compose Full-Stack - Deploy Website MySQL dan Redis dalam 10 Menit

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.

Apa Itu Docker Compose dan Kenapa Developer Harus Paham?

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:

  • Time consuming setup server baru butuh berjam-jam
  • Error-prone lupa install satu package, error muncul di production
  • Hard to reproduce "di laptop saya jalan" syndrome
  • No rollback kalau update error, rollback manual satu per satu

Docker Compose menyelesaikan semua masalah ini. Sekali kamu punya docker-compose.yml, deployment ke server baru tinggal satu command: docker compose up -d. Selesai.

Prasyarat Sebelum Memulai

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.

Struktur Project yang Kita Build

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

Step 1: Membuat docker-compose.yml

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:

  • depends_on memastikan PHP container start sebelum Nginx mencoba connect ke PHP-FPM
  • volumes data MySQL dan Redis persist di named volumes, tidak hilang saat container restart
  • networks semua service terhubung di network yang sama, bisa akses satu sama lain menggunakan nama service sebagai hostname
  • ports mapping port host ke container. Port 8080 di host ke 80 di nginx container

Step 2: Konfigurasi Nginx

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.

Step 3: Custom PHP Dockerfile

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:

  • pdo_mysql untuk koneksi ke MySQL dari PHP menggunakan PDO
  • redis PHP client untuk Redis
  • opcache cache bytecode PHP supaya lebih cepat
  • intl, mbstring, zip extension yang sering dibutuhkan framework modern

Step 4: File Aplikasi PHP

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:

  • MySQL host = "mysql" bukan localhost atau 127.0.0.1. Docker Compose memberikan DNS resolution otomatis berdasarkan nama service
  • Redis host = "redis" sama, pakai nama service sebagai hostname
  • Counter pakai Redis INCR ini demonstrasi Redis sebagai counter yang sangat cepat. Untuk production, kamu bisa pakai pattern ini untuk caching query, session storage, atau rate limiting

Step 5: Database Initialization

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);

Step 6: Build dan Jalankan Semuanya

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.

Useful Docker Compose Commands untuk Daily Use

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

Konfigurasi untuk Development vs Production

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:

  • restart: always container otomatis restart saat crash atau server reboot
  • Environment variables password dari .env file, bukan hardcode di YAML
  • Port mapping production pakai port 80/443, development pakai 8080
  • Redis memory production pakai lebih banyak RAM (512MB vs 128MB)

Menambahkan HTTPS dengan Let's Encrypt

Di 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.

Performance Tips untuk Docker Compose

Beberapa optimasi yang saya pelajari dari pengalaman:

  • Gunakan Alpine images image Alpine 5-10x lebih kecil dari image Debian-based. nginx:alpine cuma 40MB vs nginx yang 180MB
  • Named volumes untuk data jangan mount bind volumes untuk database di production. Named volumes lebih cepat karena menggunakan Docker's volume driver
  • Redis maxmemory policy allkeys-lru otomatis hapus key yang jarang dipakai saat memory penuh. Cocok untuk caching
  • PHP OPcache selalu enable di production. Bisa improve response time 3-5x
  • .dockerignore buat file .dockerignore untuk exclude node_modules, .git, dll saat build context

# .dockerignore
.git
node_modules
.env
*.md
.vscode
docker-compose*.yml

Troubleshooting Masalah Umum

Saat pertama kali pakai Docker Compose, beberapa error yang sering muncul dan cara mengatasinya:

  • "Cannot connect to the Docker daemon" Docker belum running atau user belum di group docker. Jalankan sudo systemctl start docker atau login ulang setelah usermod
  • "port is already allocated" port sudah dipakai service lain. Cek dengan sudo lsof -i :8080, ganti port di docker-compose.yml
  • MySQL "Access denied for user" container MySQL belum selesai inisialisasi tapi PHP sudah mencoba koneksi. Tambahkan depends_on dengan healthcheck, atau jalankan docker compose restart php
  • "Name or service not known" di PHP typo nama service di docker-compose.yml. Pastikan nama service sama persis dengan yang dipakai di kode PHP (mysql, redis, dll)

# 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

Kesimpulan

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.


You may also like


0 Comments


Leave a Reply

Comments with links or spam keywords will be rejected.
Scroll to Top