Docker untuk Pemula: Containerize Aplikasi Laravel & Next.js

by -4 Views
Docker untuk Pemula

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

Tambahkan user ke grup docker (agar bisa run tanpa sudo):

sudo usermod -aG docker $USER
newgrp docker

Install Docker Compose (plugin):

sudo apt install docker-compose-plugin -y

Verifikasi:

docker --version
docker compose version

Harus 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
└── .env

Step 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 di db_data.
  • networks — Container bisa saling berkomunikasi dengan nama service sebagai hostname.

Step 4: Build & Run

docker compose up -d --build

Flag:

  • -d — Detached mode (berjalan di background).
  • --build — Build image terlebih dahulu.

Cek:

docker ps

Harus muncul 3 container: laravel_app, laravel_nginx, laravel_db.

Buka browser: http://IP_VPS:8000

Setup database:

docker compose exec app php artisan migrate

Done! 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.yml

Step 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 --build

Buka: 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: bridge

Nginx 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 stats

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

Solusi: 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/storage

Atau 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?

SkenarioPakai Docker?Alasan
1 app simple di 1 VPSOpsionalDirect install lebih cepat setup
Multiple app, beda stackWajibIsolation, tidak bentrok dependency
Team > 2 orangWajibOnboarding cepat, environment sama
CI/CD pipelineWajibBuild & test konsisten
MicroservicesWajibScale independen
Belajar server dari nolTunda duluPahami 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

No More Posts Available.

No more pages to load.