Di tahun 2026, kalimat “works on my machine” sudah tidak lagi menjadi lelucon developer — karena Docker sudah mematikan meme itu. Dengan Docker, aplikasi Laravel yang jalan di laptop Mac Anda akan berjalan persis sama di VPS Ubuntu, di laptop Windows teman, bahkan di server production. Artikel ini adalah panduan Docker untuk pemula yang akan mengajarkan Anda containerize dua stack paling populer: Laravel (PHP) dan Next.js (Node.js).
Saya sendiri baru “serius” pakai Docker setelah kejadian pahit: aplikasi Laravel saya jalan sempurna di local, tapi error aneh saat deploy ke VPS. Ternyata PHP versi berbeda, extension tidak sama, dan folder permission beda. Satu malam debugging yang tidak perlu terjadi kalau saya pakai Docker sejak awal.
1. Apa Itu Docker? Analogi Kontainer Kapal
Bayangkan Anda pindah rumah dari Jakarta ke Surabaya. Anda punya perabotan, baju, peralatan dapur. Kalau pindah tanpa kotak, barang akan berantakan, pecah, atau tertinggal. Tapi kalau Anda pakai kontainer — kotak standar yang rapi — semua barang sampai di tujuan dalam kondisi sama persis.
Docker adalah “kontainer” untuk aplikasi. Di dalamnya ada:
- OS minimal (Ubuntu Alpine, biasanya cuma 5MB)
- Runtime yang dibutuhkan (PHP 8.3, Node.js 20, Nginx)
- Dependency (Composer packages, NPM modules)
- Kode aplikasi Anda
- Environment variables
Anda build kontainer ini sekali, lalu run di mana saja. Laptop teman, VPS production, CI/CD pipeline — semua akan melihat environment yang identik.
Kenapa Docker Wajib Dipahami di 2026?
- Consistency: Local = Staging = Production. Tidak ada lagi “kok di saya jalan?”
- Isolation: App A pakai PHP 8.2, App B pakai PHP 8.3. Tidak bentrok.
- Portability: Pindah dari DigitalOcean ke AWS? Cukup pindah kontainer.
- Scalability: Butuh 3 instance app?
docker-compose up --scale app=3 - Team Onboarding: Developer baru cukup
docker-compose up— tidak perlu install PHP, MySQL, Node.js satu per satu.
2. Install Docker & Docker Compose di VPS
SSH ke VPS Ubuntu 24.04 Anda:
curl -fsSL https://get.docker.com -o get-docker.sh
sudo sh get-docker.shTambahkan user ke grup docker (agar bisa run tanpa sudo):
sudo usermod -aG docker $USER
newgrp dockerInstall Docker Compose (plugin):
sudo apt install docker-compose-plugin -yVerifikasi:
docker --version
docker compose versionHarus muncul Docker 24.x+ dan Compose 2.x+.
3. Containerize Aplikasi Laravel
Struktur folder project:
laravel-docker/
├── app/ # Kode Laravel Anda
├── docker/
│ └── nginx/
│ └── default.conf
├── Dockerfile
├── docker-compose.yml
└── .envStep 1: Dockerfile untuk Laravel
Buat file Dockerfile di root project:
FROM php:8.3-fpm-alpine
# Install system dependencies
RUN apk add --no-cache \
nginx \
supervisor \
libpng-dev \
libzip-dev \
zip \
unzip \
git \
curl \
oniguruma-dev \
libxml2-dev
# Install PHP extensions
RUN docker-php-ext-install \
pdo_mysql \
mbstring \
zip \
exif \
pcntl \
bcmath \
gd
# Install Composer
COPY --from=composer:latest /usr/bin/composer /usr/bin/composer
# Set working directory
WORKDIR /var/www
# Copy application
COPY app/ .
# Install dependencies
RUN composer install --no-dev --optimize-autoloader
# Set permissions
RUN chown -R www-data:www-data /var/www/storage /var/www/bootstrap/cache
RUN chmod -R 775 /var/www/storage /var/www/bootstrap/cache
# Expose port
EXPOSE 9000
CMD ["php-fpm"]Penjelasan:
php:8.3-fpm-alpine— Base image PHP 8.3 dengan FPM, versi Alpine (paling ringan, ~80MB).docker-php-ext-install— Cara install extension PHP di Docker.COPY --from=composer:latest— Multi-stage build: copy binary Composer dari image official.WORKDIR /var/www— Semua command selanjutnya berjalan di sini.
Step 2: Nginx Config untuk Laravel
Buat docker/nginx/default.conf:
server {
listen 80;
server_name localhost;
root /var/www/public;
index index.php index.html;
location / {
try_files $uri $uri/ /index.php?$query_string;
}
location ~ \.php$ {
fastcgi_pass app:9000;
fastcgi_index index.php;
fastcgi_param SCRIPT_FILENAME $document_root$fastcgi_script_name;
include fastcgi_params;
}
location ~ /\.ht {
deny all;
}
}Perhatikan fastcgi_pass app:9000; — app adalah nama service di Docker Compose (nanti kita definisikan). Docker punya internal DNS, jadi kita tidak perlu IP.
Step 3: Docker Compose untuk Laravel Stack
Buat docker-compose.yml:
version: '3.8'
services:
app:
build:
context: .
dockerfile: Dockerfile
container_name: laravel_app
restart: unless-stopped
working_dir: /var/www
volumes:
- ./app:/var/www
- ./app/storage:/var/www/storage
networks:
- laravel_network
nginx:
image: nginx:alpine
container_name: laravel_nginx
restart: unless-stopped
ports:
- "8000:80"
volumes:
- ./app:/var/www
- ./docker/nginx/default.conf:/etc/nginx/conf.d/default.conf
depends_on:
- app
networks:
- laravel_network
db:
image: mysql:8.0
container_name: laravel_db
restart: unless-stopped
environment:
MYSQL_DATABASE: laravel_db
MYSQL_ROOT_PASSWORD: rootpassword
MYSQL_USER: laravel_user
MYSQL_PASSWORD: userpassword
ports:
- "3306:3306"
volumes:
- db_data:/var/lib/mysql
networks:
- laravel_network
networks:
laravel_network:
driver: bridge
volumes:
db_data:Penjelasan service:
app— Container PHP-FPM yang menjalankan Laravel.nginx— Web server yang reverse proxy ke PHP-FPM. Port 8000 di host akan di-map ke port 80 di container.db— MySQL 8.0 dengan volume persistent. Kalau container dihapus, data tetap ada didb_data.networks— Container bisa saling berkomunikasi dengan nama service sebagai hostname.
Step 4: Build & Run
docker compose up -d --buildFlag:
-d— Detached mode (berjalan di background).--build— Build image terlebih dahulu.
Cek:
docker psHarus muncul 3 container: laravel_app, laravel_nginx, laravel_db.
Buka browser: http://IP_VPS:8000
Setup database:
docker compose exec app php artisan migrateDone! Laravel Anda sekarang berjalan di Docker.
4. Containerize Aplikasi Next.js
Next.js punya kebutuhan berbeda: build time + runtime. Kita pakai multi-stage build untuk ukuran image optimal.
Struktur folder:
nextjs-docker/
├── app/ # Kode Next.js Anda
├── Dockerfile
└── docker-compose.ymlStep 1: Dockerfile Multi-Stage untuk Next.js
# Stage 1: Dependencies
FROM node:20-alpine AS deps
WORKDIR /app
COPY app/package.json app/package-lock.json ./
RUN npm ci
# Stage 2: Builder
FROM node:20-alpine AS builder
WORKDIR /app
COPY --from=deps /app/node_modules ./node_modules
COPY app/ .
RUN npm run build
# Stage 3: Runner (Production)
FROM node:20-alpine AS runner
WORKDIR /app
ENV NODE_ENV=production
ENV NEXT_TELEMETRY_DISABLED=1
COPY --from=builder /app/public ./public
COPY --from=builder /app/.next/standalone ./
COPY --from=builder /app/.next/static ./.next/static
EXPOSE 3000
CMD ["node", "server.js"]Penjelasan multi-stage:
Stage 1 (deps):Install node_modules. Layer ini di-cache, jadi kalau package.json tidak berubah, build lebih cepat.Stage 2 (builder):Copy node_modules dari stage 1, lalu build Next.js.Stage 3 (runner):Hanya copy hasil build. Tidak ada source code, tidak ada devDependencies. Image final jauh lebih kecil (~150MB vs ~1GB).
Catatan penting: Pastikan di next.config.js ada:
module.exports = {
output: 'standalone',
}Ini membuat Next.js menghasilkan file server.js yang self-contained.
Step 2: Docker Compose untuk Next.js
version: '3.8'
services:
nextjs:
build:
context: .
dockerfile: Dockerfile
container_name: nextjs_app
restart: unless-stopped
ports:
- "3000:3000"
environment:
- DATABASE_URL=mysql://user:pass@db:3306/nextjs_db
networks:
- nextjs_network
db:
image: mysql:8.0
container_name: nextjs_db
restart: unless-stopped
environment:
MYSQL_DATABASE: nextjs_db
MYSQL_ROOT_PASSWORD: rootpassword
volumes:
- nextjs_db_data:/var/lib/mysql
networks:
- nextjs_network
networks:
nextjs_network:
driver: bridge
volumes:
nextjs_db_data:Step 3: Build & Run
docker compose up -d --buildBuka: http://IP_VPS:3000
5. Docker Compose Multi-Service: Laravel + Next.js + Nginx Reverse Proxy
Bagaimana kalau Anda ingin keduanya berjalan bersamaan di 1 VPS, dengan 1 Nginx sebagai reverse proxy?
Buat docker-compose.yml gabungan:
version: '3.8'
services:
# Laravel Stack
laravel_app:
build: ./laravel
container_name: laravel_app
restart: unless-stopped
volumes:
- ./laravel/app:/var/www
networks:
- app_network
# Next.js Stack
nextjs_app:
build: ./nextjs
container_name: nextjs_app
restart: unless-stopped
networks:
- app_network
# Shared Nginx Reverse Proxy
nginx:
image: nginx:alpine
container_name: shared_nginx
restart: unless-stopped
ports:
- "80:80"
- "443:443"
volumes:
- ./nginx/default.conf:/etc/nginx/conf.d/default.conf
- ./nginx/ssl:/etc/nginx/ssl
depends_on:
- laravel_app
- nextjs_app
networks:
- app_network
networks:
app_network:
driver: bridgeNginx config:
server {
listen 80;
server_name api.zulfianto.com;
location / {
proxy_pass http://laravel_app:9000;
include proxy_params;
}
}
server {
listen 80;
server_name app.zulfianto.com;
location / {
proxy_pass http://nextjs_app:3000;
include proxy_params;
}
}Sekarang Anda punya microservices sederhana di 1 VPS, semua di-manage Docker.
6. Command Docker yang Harus Dihafal
# Lihat container yang jalan
docker ps
# Lihat semua container (termasuk yang mati)
docker ps -a
# Stop container
docker stop nama_container
# Start ulang
docker start nama_container
# Masuk ke dalam container (seperti SSH)
docker exec -it nama_container /bin/sh
# Lihat log container
docker logs -f nama_container
# Build ulang setelah ubah Dockerfile
docker compose up -d --build
# Hapus container & volume (hati-hati!)
docker compose down -v
# Monitor resource
docker stats7. Troubleshooting Docker Umum
Port Sudah Digunakan
Error: Ports are not available: exposing port TCP 0.0.0.0:80 -> 0.0.0.0:0: listen tcp 0.0.0.0:80: bind: address already in useSolusi: Nginx di host VPS sudah jalan. Stop dulu: sudo systemctl stop nginx, atau ganti port mapping di docker-compose.
Permission Denied di Storage
Solusi: Laravel butuh write access. Tambahkan di Dockerfile:
RUN chown -R www-data:www-data /var/www/storageAtau jalankan container dengan user ID yang sama:
user: "1000:1000"Container Exit Immediately
Solusi: Cek log: docker logs nama_container. Biasanya CMD salah atau dependency tidak ketemu.
Image Terlalu Besar
Solusi: Pakai Alpine base image. Hindari RUN apt update tanpa cleanup. Gunakan multi-stage build.
8. Kapan Pakai Docker, Kapan Tidak?
| Skenario | Pakai Docker? | Alasan |
|---|---|---|
| 1 app simple di 1 VPS | Opsional | Direct install lebih cepat setup |
| Multiple app, beda stack | Wajib | Isolation, tidak bentrok dependency |
| Team > 2 orang | Wajib | Onboarding cepat, environment sama |
| CI/CD pipeline | Wajib | Build & test konsisten |
| Microservices | Wajib | Scale independen |
| Belajar server dari nol | Tunda dulu | Pahami dulu LAMP/LEMP manual, lalu Docker |
Kesimpulan: Docker adalah Standar Deployment Modern
Di 2026, Docker bukan lagi “tools opsional untuk DevOps.” Ini adalah skill wajib developer yang ingin deploy aplikasi modern. Kalau Anda bisa Dockerize Laravel dan Next.js, Anda sudah punya keunggulan kompetitif:
- Client percaya karena deployment Anda reproducible
- Team senang karena onboarding cukup
docker compose up - Anda tenang karena local = production
Tapi Docker bukan pengganti pemahaman server dasar. Saya sarankan: belajar LEMP manual dulu, pahami kenapa Nginx butuh konfigurasi tertentu, lalu abstraksi dengan Docker. Kalau Anda langsung loncat ke Docker tanpa paham fondasi, saat container error Anda akan bingung.
Semua konsep Docker — Dockerfile, Compose, multi-stage build, volume, network — saya ajarkan secara praktis di modul Docker kursus Belajar Server. Kita tidak hanya copy-paste Dockerfile, tapi pahami kenapa image Alpine dipilih, cara optimize layer, dan cara debug container yang crash.
— Tested on Docker 26.x + Ubuntu 24.04 LTS, Mei 2026







