Membangun environment development Laravel dengan Docker memberi beberapa keuntungan praktis: dependensi sistem menjadi konsisten antar mesin, proses onboarding tim lebih mudah, dan konfigurasi service seperti PHP, Nginx, MySQL, serta Redis dapat didefinisikan sebagai kode. Untuk project Laravel, pendekatan ini sangat membantu karena aplikasi modern umumnya tidak berdiri sendiri; ia membutuhkan web server, database, cache, dan runtime PHP dengan extension tertentu.
Pada artikel ini, kita akan menyiapkan project Laravel dengan Docker dari nol untuk kebutuhan local development. Fokus utama kita adalah membuat susunan container yang jelas: app untuk PHP-FPM, nginx untuk web server, mysql untuk database, dan redis untuk cache/queue sederhana. Di akhir, kita akan memverifikasi aplikasi berjalan, menjalankan perintah Artisan dari dalam container, dan membahas masalah umum yang sering muncul.
Catatan: Artikel ini berfokus pada fondasi environment development. Topik seperti queue worker, scheduler, testing container, multi-stage build, dan optimasi image untuk production lebih cocok dibahas pada bagian berikutnya.
Mengapa Memisahkan Service di Docker?
Kesalahan umum saat mulai memakai Docker adalah mencoba memasukkan semua komponen ke satu container. Secara teknis hal itu bisa dilakukan, tetapi kurang ideal. Dalam praktik yang baik, setiap service dipisahkan berdasarkan tanggung jawabnya.
- app: menjalankan PHP-FPM dan kode Laravel.
- nginx: menerima request HTTP dan meneruskannya ke PHP-FPM.
- mysql: menyediakan database relasional.
- redis: untuk cache, session, atau queue backend ringan.
Pemisahan ini penting karena:
- Lebih mudah dirawat: jika Nginx bermasalah, kita cukup cek container Nginx tanpa mengganggu MySQL.
- Lebih fleksibel: versi MySQL atau Redis bisa diganti tanpa membangun ulang seluruh stack.
- Lebih dekat ke arsitektur nyata: meskipun ini environment lokal, pola ini lebih mirip deployment modern.
- Debugging lebih sederhana: log setiap service terpisah.
Selain service, kita juga akan memanfaatkan network Docker. Network internal memungkinkan container saling berkomunikasi menggunakan nama service, misalnya mysql atau redis, tanpa harus mengetahui IP dinamis container. Ini membuat konfigurasi lebih stabil dan mudah dipahami.
Struktur Folder Project
Supaya konfigurasi rapi, buat struktur folder seperti berikut:
laravel-docker-app/
├── app/
├── bootstrap/
├── config/
├── database/
├── docker/
│ ├── nginx/
│ │ └── default.conf
│ └── php/
│ └── Dockerfile
├── public/
├── resources/
├── routes/
├── storage/
├── .env
├── docker-compose.yml
└── artisanFolder docker/ digunakan untuk menyimpan semua file konfigurasi yang terkait container. Ini lebih baik daripada mencampur file Docker ke root project tanpa struktur yang jelas. Nantinya, jika stack bertambah kompleks, kita bisa menambahkan folder lain seperti docker/mysql, docker/supervisor, atau docker/queue.
Membuat Dockerfile untuk PHP-FPM
Laravel tidak langsung melayani request HTTP. Biasanya Laravel berjalan di atas PHP-FPM, lalu Nginx meneruskan request PHP ke service tersebut. Karena itu, kita butuh image khusus untuk service app.
Buat file docker/php/Dockerfile:
FROM php:8.2-fpm
RUN apt-get update && apt-get install -y \
git \
curl \
unzip \
libpng-dev \
libonig-dev \
libxml2-dev \
libzip-dev \
default-mysql-client \
&& docker-php-ext-install pdo pdo_mysql mbstring exif pcntl bcmath gd zip
COPY --from=composer:2 /usr/bin/composer /usr/bin/composer
WORKDIR /var/wwwPenjelasannya:
php:8.2-fpmdipakai karena cocok untuk memisahkan web server dan PHP runtime.- Paket seperti
libzip-dev,libpng-dev, dan extensionpdo_mysqldiperlukan Laravel atau package umum di ekosistemnya. - Composer disalin dari image resmi Composer agar kita tidak perlu instal manual.
WORKDIR /var/wwwmenjadi direktori kerja utama di container.
Untuk development, Dockerfile ini sudah cukup. Nanti pada tahap lanjutan, kita bisa menambahkan user non-root, opcache, atau optimasi build agar image lebih aman dan efisien.
Menyusun docker-compose.yml
File docker-compose.yml adalah pusat definisi seluruh stack. Di sini kita mendeskripsikan service, port, volume, environment, dan network.
version: '3.9'
services:
app:
build:
context: .
dockerfile: docker/php/Dockerfile
container_name: laravel_app
working_dir: /var/www
volumes:
- ./:/var/www
depends_on:
- mysql
- redis
networks:
- laravel
nginx:
image: nginx:alpine
container_name: laravel_nginx
ports:
- "8000:80"
volumes:
- ./:/var/www
- ./docker/nginx/default.conf:/etc/nginx/conf.d/default.conf
depends_on:
- app
networks:
- laravel
mysql:
image: mysql:8.0
container_name: laravel_mysql
restart: unless-stopped
environment:
MYSQL_DATABASE: laravel
MYSQL_ROOT_PASSWORD: root
MYSQL_PASSWORD: secret
MYSQL_USER: laravel
ports:
- "3307:3306"
volumes:
- mysql_data:/var/lib/mysql
networks:
- laravel
redis:
image: redis:alpine
container_name: laravel_redis
ports:
- "6379:6379"
networks:
- laravel
volumes:
mysql_data:
networks:
laravel:Beberapa poin penting:
- Volume
./:/var/wwwmembuat source code lokal langsung tersedia di container. Ini cocok untuk development karena perubahan file langsung terlihat tanpa rebuild image. - Port 8000:80 berarti Nginx di container bisa diakses dari host melalui
http://localhost:8000. - Port MySQL 3307:3306 dipakai agar tidak bentrok jika host Anda sudah menjalankan MySQL lokal di port 3306.
- Named volume
mysql_datamenjaga data database tetap ada meskipun container dihapus dan dibuat ulang. depends_onmembantu urutan startup, tetapi bukan jaminan service benar-benar siap menerima koneksi. Ini penting saat debugging koneksi database saat startup awal.
Konfigurasi Nginx Dasar
Buat file docker/nginx/default.conf:
server {
listen 80;
index index.php index.html;
server_name localhost;
root /var/www/public;
location / {
try_files $uri $uri/ /index.php?$query_string;
}
location ~ \.php$ {
include fastcgi_params;
fastcgi_pass app:9000;
fastcgi_index index.php;
fastcgi_param SCRIPT_FILENAME $document_root$fastcgi_script_name;
}
location ~ /\.ht {
deny all;
}
}Kenapa konfigurasi ini bekerja?
root /var/www/publicmenunjuk ke folder publik Laravel, bukan root project. Ini penting untuk keamanan dan routing yang benar.try_filesakan mencoba file statis lebih dulu, lalu meneruskan request keindex.phpjika route ditangani Laravel.fastcgi_pass app:9000memakai nama service Docker, bukan IP. Karena berada di network yang sama, Nginx bisa menjangkau PHP-FPM melalui hostnameapp.
Kesalahan umum di tahap ini adalah mengarahkan root ke /var/www alih-alih /var/www/public. Akibatnya, asset dan routing Laravel sering gagal atau lebih buruk, file sensitif bisa terekspos.
Menyiapkan Laravel dan Dependency
Jika project belum ada, Anda bisa membuatnya dari dalam container agar versi Composer dan PHP konsisten dengan environment Docker.
Bangun dan jalankan container terlebih dulu:
docker compose up -d --buildLalu buat project Laravel di direktori kerja saat ini:
docker compose run --rm app composer create-project laravel/laravel .Jika project sudah ada, cukup install dependency:
docker compose exec app composer installSetelah itu, salin file environment:
cp .env.example .envLalu generate application key:
docker compose exec app php artisan key:generateKonfigurasi .env untuk Docker
Karena Laravel berjalan di dalam network Docker, host database dan Redis bukan 127.0.0.1, melainkan nama service pada docker-compose.yml.
Sesuaikan bagian penting di file .env:
APP_NAME=Laravel
APP_ENV=local
APP_KEY=
APP_DEBUG=true
APP_URL=http://localhost:8000
DB_CONNECTION=mysql
DB_HOST=mysql
DB_PORT=3306
DB_DATABASE=laravel
DB_USERNAME=laravel
DB_PASSWORD=secret
CACHE_STORE=redis
REDIS_HOST=redis
REDIS_PASSWORD=null
REDIS_PORT=6379Hal yang perlu diperhatikan:
DB_HOST=mysql, bukanlocalhost. Di dalam container,localhostberarti container itu sendiri.DB_PORT=3306karena itu port internal antar-container. Port host3307hanya dipakai jika Anda mengakses MySQL dari mesin lokal, misalnya via TablePlus atau DBeaver.APP_URLdisesuaikan dengan port yang diekspos oleh Nginx di host.
Permission Storage dan Bootstrap Cache
Laravel membutuhkan hak tulis pada folder storage dan bootstrap/cache. Jika permission salah, gejalanya bisa berupa error log, cache gagal ditulis, atau halaman 500 tanpa penjelasan yang jelas.
Jalankan perintah berikut:
docker compose exec app chmod -R 775 storage bootstrap/cachePada beberapa sistem, terutama Linux, Anda mungkin perlu menyesuaikan owner file agar cocok dengan user di host atau container. Untuk tahap awal development, pengaturan permission di atas biasanya cukup. Jika masih bermasalah, cek user yang menjalankan PHP-FPM dan siapa pemilik file di host.
Menjalankan Migrasi dan Verifikasi Koneksi Database
Setelah semua service aktif, uji koneksi database dengan migrasi bawaan Laravel:
docker compose exec app php artisan migrateJika berhasil, berarti koneksi Laravel ke MySQL berjalan dengan benar. Anda juga bisa menguji Redis secara sederhana melalui Tinker atau fitur cache di tahap berikutnya.
Untuk melihat log service tertentu:
docker compose logs app
docker compose logs nginx
docker compose logs mysqlLog adalah alat debugging pertama yang paling berguna. Jangan langsung mengubah banyak konfigurasi sebelum memeriksa log; sering kali penyebab error sudah terlihat di sana.
Verifikasi Aplikasi Berjalan
Pada titik ini, stack development minimal sudah siap. Lakukan verifikasi berikut:
- Pastikan semua container aktif dengan
docker compose ps. - Buka
http://localhost:8000di browser. - Jika halaman default Laravel muncul, artinya alur Nginx -> PHP-FPM -> Laravel sudah berjalan.
- Jalankan
docker compose exec app php artisan route:listuntuk memastikan Artisan bisa diakses dari container.
Beberapa perintah Artisan yang sering dipakai selama development:
docker compose exec app php artisan migrate
docker compose exec app php artisan db:seed
docker compose exec app php artisan cache:clear
docker compose exec app php artisan config:clear
docker compose exec app php artisan route:listKeuntungan menjalankan perintah ini dari dalam container adalah konsistensi environment. Anda tidak bergantung pada instalasi PHP lokal host yang mungkin berbeda versi atau extension-nya tidak lengkap.
Masalah Umum dan Solusinya
1. Error koneksi database
Gejala: SQLSTATE[HY000] [2002] Connection refused atau php_network_getaddresses.
- Pastikan
DB_HOST=mysql, bukanlocalhost. - Pastikan service MySQL sudah benar-benar siap. Container bisa aktif, tetapi database belum selesai inisialisasi.
- Cek kredensial di
.envdan environment MySQL pada Compose.
2. Permission denied pada storage atau cache
- Jalankan ulang
chmod -R 775 storage bootstrap/cache. - Periksa owner file jika memakai Linux dan bind mount dari host.
3. 502 Bad Gateway dari Nginx
- Biasanya Nginx tidak bisa menjangkau PHP-FPM.
- Pastikan
fastcgi_pass app:9000sesuai nama service. - Cek log Nginx dan app untuk melihat apakah PHP-FPM berjalan normal.
4. Perubahan file tidak terlihat
- Pastikan volume project ter-mount dengan benar:
./:/var/www. - Jika memakai WSL2, Docker Desktop, atau filesystem network tertentu, kadang performa file sync lebih lambat.
5. Port bentrok
- Jika
8000,3307, atau6379sudah dipakai host, ganti mapping port didocker-compose.yml. - Port internal container biasanya tidak perlu diubah.
Penutup
Sampai di sini, kita sudah memiliki fondasi environment Laravel berbasis Docker yang cukup baik untuk local development: service dipisahkan dengan jelas, Nginx mengarah ke PHP-FPM, MySQL dan Redis tersedia di network internal, dependency dikelola konsisten, dan konfigurasi Laravel sudah menyesuaikan nama service Docker.
Pendekatan ini bukan sekadar agar “Laravel bisa jalan”, tetapi agar project mudah dirawat, mudah dipahami anggota tim lain, dan siap dikembangkan ke kebutuhan berikutnya. Di part selanjutnya, fondasi ini bisa diperluas ke topik seperti hot reload aset frontend, queue worker, scheduler, optimasi workflow development, atau penyesuaian image agar lebih dekat ke environment production.
Komentar
0 komentar
Masuk ke akun kamu untuk ikut berkomentar.
Belum ada komentar
Jadilah yang pertama ikut berdiskusi!