Cara Install LEMP Menggunakan Docker Compose di Rocky Linux 10
LEMP menggabungkan Nginx, MariaDB, dan PHP-FPM menjadi satu stack web. Dengan Docker Compose di Rocky Linux 10, ketiga service itu berjalan sebagai container dan dijalankan lewat satu perintah. Data MariaDB tetap tersimpan di host melalui bind mount, sementara health check menjaga urutan startup.
Requirementsβ
- Rocky Linux 10.
- Akses root atau
sudo. - Minimal 2 vCPU, RAM 2 GB, dan disk 10 GB.
Environmentβ
- OS: Rocky Linux 10 (x86_64)
- Runtime: Docker Engine beserta plugin Compose dari repositori resmi Docker
- Web server: image
nginx:1.30.5-alpine - PHP: image
php:8.4.26-fpmyang ditambah ekstensipdo_mysql - Database: image
mariadb:11.8.9 - Direktori proyek:
/root/docker-compose - Port HTTP:
8080
Langkah pada panduan ini dijalankan pada server Rocky Linux 10 dengan akses root. Environment seperti itu tersedia sebagai Nusa Cloud VPS.
Install LEMP di Rocky Linux 10 dengan Docker Composeβ
1. Memasang Docker Engineβ
Repositori Docker menyediakan paket untuk RHEL 10 dan turunannya, termasuk Rocky Linux. Tambahkan repositori tersebut beserta paket pembantu config-manager.
sudo dnf install -y dnf-plugins-core curl
sudo dnf config-manager --add-repo https://download.docker.com/linux/rhel/docker-ce.repo
Pasang Docker Engine dan plugin Compose. Proses ini mengimpor kunci GPG repositori Docker dengan fingerprint 060A 61C5 1B55 8A7F 742B 77AA C52F EB6B 621E 9F35.
sudo dnf install -y docker-ce docker-ce-cli containerd.io docker-buildx-plugin docker-compose-plugin
sudo systemctl enable --now docker
Output:
Installing:
containerd.io-2.3.5-1.el10 docker-buildx-plugin-0.37.1-1.el10
docker-ce-3:29.8.1-1.el10 docker-ce-cli-1:29.8.1-1.el10
docker-compose-plugin-5.5.1-1.el10
Importing GPG key 0x621E9F35:
Fingerprint: 060A 61C5 1B55 8A7F 742B 77AA C52F EB6B 621E 9F35
Key imported successfully
Complete!
Created symlink '/etc/systemd/system/multi-user.target.wants/docker.service' β '/usr/lib/systemd/system/docker.service'.
Jalankan image uji resmi untuk memastikan daemon aktif.
sudo docker run --rm hello-world
Output:
Hello from Docker!
This message shows that your installation appears to be working correctly.
2. Menyiapkan Direktori dan Variabel Environmentβ
Buat empat direktori: konfigurasi Nginx, build context PHP, aplikasi, dan data MariaDB.
sudo mkdir -p /root/docker-compose/config/nginx \
/root/docker-compose/config/php \
/root/docker-compose/data/app \
/root/docker-compose/data/database
Selanjutnya buat file /root/docker-compose/.env. Password diisi nilai acak dari /dev/urandom, jadi tidak ada kredensial yang ditulis di panduan ini. Mode file 600 membatasi akses hanya untuk root.
sudo install -m 600 /dev/null /root/docker-compose/.env
sudo tee /root/docker-compose/.env > /dev/null <<EOF
DB_HOST=db
MARIADB_DATABASE=lempdb
MARIADB_USER=lempuser
MARIADB_ROOT_PASSWORD=$(head -c 24 /dev/urandom | base64)
MARIADB_PASSWORD=$(head -c 24 /dev/urandom | base64)
HTTP_PORT=8080
EOF
sudo stat -c '%a %n' /root/docker-compose/.env
Output:
600 /root/docker-compose/.env
Untuk mengganti port HTTP, edit /root/docker-compose/.env dengan text editor pilihan Anda, misalnya nano atau vim, lalu ubah nilai HTTP_PORT.
3. Membuat Image PHP-FPMβ
Image resmi php tidak memuat driver MySQL. Ekstensi pdo_mysql dipasang lewat Dockerfile kecil di bawah ini. Simpan sebagai /root/docker-compose/config/php/Dockerfile.
sudo tee /root/docker-compose/config/php/Dockerfile > /dev/null <<'EOF'
FROM php:8.4.26-fpm
RUN docker-php-ext-install pdo_mysql
EOF
4. Menyiapkan Konfigurasi Nginxβ
Nginx melayani file dari /var/www/html dan meneruskan request .php ke service php pada port 9000. Simpan berkas ini sebagai /root/docker-compose/config/nginx/default.conf.
sudo tee /root/docker-compose/config/nginx/default.conf > /dev/null <<'EOF'
server {
listen 80;
server_name _;
root /var/www/html;
index index.php index.html;
location / {
try_files $uri $uri/ =404;
}
location ~ \.php$ {
try_files $uri =404;
include fastcgi_params;
fastcgi_param SCRIPT_FILENAME $document_root$fastcgi_script_name;
fastcgi_pass php:9000;
}
}
EOF
5. Menambahkan Aplikasi Ujiβ
Buat file statis untuk health check Nginx, lalu skrip PHP yang membuka koneksi PDO ke MariaDB.
sudo tee /root/docker-compose/data/app/health.txt > /dev/null <<'EOF'
ok
EOF
Skrip berikut membaca kredensial dari environment container dan menampilkan pesan singkat saat koneksi berhasil. Simpan sebagai /root/docker-compose/data/app/index.php.
sudo tee /root/docker-compose/data/app/index.php > /dev/null <<'PHP'
<?php
declare(strict_types=1);
$dsn = sprintf(
'mysql:host=%s;dbname=%s;charset=utf8mb4',
(string) getenv('DB_HOST'),
(string) getenv('MARIADB_DATABASE')
);
header('Content-Type: text/plain; charset=utf-8');
try {
$pdo = new PDO($dsn, (string) getenv('MARIADB_USER'), (string) getenv('MARIADB_PASSWORD'));
$pdo->query('SELECT 1');
echo 'LEMP berfungsi: PHP ' . PHP_VERSION . ', koneksi MariaDB berhasil.' . PHP_EOL;
} catch (PDOException $e) {
http_response_code(500);
echo 'Koneksi database gagal: ' . $e->getMessage() . PHP_EOL;
}
PHP
6. Menulis Definisi Composeβ
Tiga service didefinisikan dalam satu file. Akhiran z pada bind mount memberi label SELinux agar container dapat membaca dan menulis direktori host. Simpan sebagai /root/docker-compose/docker-compose.yml.
sudo tee /root/docker-compose/docker-compose.yml > /dev/null <<'EOF'
name: lemp
services:
web:
image: nginx:1.30.5-alpine
restart: unless-stopped
ports:
- "${HTTP_PORT}:80"
volumes:
- ./config/nginx/default.conf:/etc/nginx/conf.d/default.conf:ro,z
- ./data/app:/var/www/html:ro,z
depends_on:
php:
condition: service_started
healthcheck:
test: ["CMD", "curl", "-fsS", "-o", "/dev/null", "http://127.0.0.1/health.txt"]
interval: 10s
timeout: 5s
retries: 5
php:
build:
context: ./config/php
restart: unless-stopped
env_file:
- .env
volumes:
- ./data/app:/var/www/html:z
depends_on:
db:
condition: service_healthy
db:
image: mariadb:11.8.9
restart: unless-stopped
env_file:
- .env
volumes:
- ./data/database:/var/lib/mysql:z
healthcheck:
test: ["CMD", "healthcheck.sh", "--connect", "--innodb_initialized"]
start_period: 20s
interval: 10s
timeout: 5s
retries: 5
EOF
7. Menjalankan Stackβ
Validasi definisi Compose lebih dulu. Perintah config --quiet hanya memeriksa sintaks dan tidak menampilkan isi rahasia.
Semua perintah docker compose di bawah ini menunjuk berkas /root/docker-compose/docker-compose.yml secara eksplisit. Direktori proyek dan file .env karena itu tetap pada /root/docker-compose, terlepas dari direktori kerja shell.
sudo docker compose -f /root/docker-compose/docker-compose.yml config --quiet
Tidak adanya output berarti konfigurasi valid. Bangun image PHP lalu jalankan seluruh service. Flag --wait menahan perintah sampai setiap service berjalan dan health check-nya lulus.
sudo docker compose -f /root/docker-compose/docker-compose.yml up -d --build --wait
Output:
Container lemp-db-1 Started
Container lemp-db-1 Waiting
Container lemp-db-1 Healthy
Container lemp-php-1 Started
Container lemp-web-1 Started
Container lemp-php-1 Healthy
Container lemp-db-1 Healthy
Container lemp-web-1 Healthy
Verifikasi Instalasi LEMP di Rocky Linux 10β
Pastikan ketiga container berjalan dan lolos health check.
sudo docker compose -f /root/docker-compose/docker-compose.yml ps
Output:
NAME IMAGE COMMAND SERVICE CREATED STATUS PORTS
lemp-db-1 mariadb:11.8.9 "docker-entrypoint.sβ¦" db 42 seconds ago Up 40 seconds (healthy) 3306/tcp
lemp-php-1 lemp-php "docker-php-entrypoiβ¦" php 42 seconds ago Up 24 seconds 9000/tcp
lemp-web-1 nginx:1.30.5-alpine "/docker-entrypoint.β¦" web 41 seconds ago Up 24 seconds (healthy) 0.0.0.0:8080->80/tcp, [::]:8080->80/tcp
Flag -T pada perintah exec menonaktifkan alokasi TTY agar perintah cocok dijalankan dari skrip.
Cek versi web server dan PHP yang benar-benar berjalan di dalam container.
sudo docker compose -f /root/docker-compose/docker-compose.yml exec -T web nginx -v
sudo docker compose -f /root/docker-compose/docker-compose.yml exec -T php php -r 'echo PHP_VERSION, PHP_EOL;'
Output:
nginx version: nginx/1.30.5
8.4.26
Pastikan ekstensi pdo_mysql termuat di PHP.
sudo docker compose -f /root/docker-compose/docker-compose.yml exec -T php php -m | grep -i pdo_mysql
Output:
pdo_mysql
Terakhir, pastikan MariaDB benar-benar siap menerima koneksi.
sudo docker compose -f /root/docker-compose/docker-compose.yml exec -T db healthcheck.sh --connect --innodb_initialized && echo "db siap"
Output:
db siap
Konfigurasi Dasarβ
Variabel di .envβ
| Variabel | Nilai default | Fungsi |
|---|---|---|
DB_HOST | db | Nama service database yang dipakai skrip PHP. Compose menyediakan resolusi nama ini di jaringan internal. |
MARIADB_DATABASE | lempdb | Nama database yang dibuat saat inisialisasi pertama. |
MARIADB_USER | lempuser | User aplikasi yang hanya punya akses ke MARIADB_DATABASE. |
MARIADB_PASSWORD | acak | Password user aplikasi, dipakai PHP dan MariaDB. |
MARIADB_ROOT_PASSWORD | acak | Password root MariaDB, hanya untuk administrasi. |
HTTP_PORT | 8080 | Port di host yang dipetakan ke port 80 container Nginx. |
Variabel host memakai nama DB_HOST, bukan MARIADB_HOST. Klien MariaDB di container db membaca MARIADB_HOST sebagai host default, sehingga healthcheck.sh akan menyambung ke IP bridge container dan ditolak.
Nilai acak dibuat sekali saat file .env dibuat. Mengubah password di .env setelah database terinisialisasi tidak mengubah password di dalam data MariaDB, karena password hanya dipakai pada inisialisasi pertama.
Direktif Nginx dan PHP-FPMβ
root /var/www/htmlmenunjuk ke direktori aplikasi yang di-bind mount daridata/app.fastcgi_pass php:9000mengarah ke servicephp, bukan ke alamat IP. Docker Compose menyediakan DNS internal berdasarkan nama service.try_files $uri =404mencegah request PHP ke berkas yang tidak ada diteruskan ke PHP-FPM.docker-php-ext-install pdo_mysqlpada Dockerfile menambahkan driver MySQL. Setiap perubahan Dockerfile perlu dibangun ulang dengansudo docker compose -f /root/docker-compose/docker-compose.yml up -d --build php.
Pengujian Fungsionalβ
Uji pertama memastikan rantai Nginx, PHP-FPM, dan MariaDB bekerja lewat satu request HTTP.
curl -fsS --retry 10 --retry-delay 2 --retry-connrefused http://localhost:8080/
Output:
LEMP berfungsi: PHP 8.4.26, koneksi MariaDB berhasil.
Uji kedua memastikan data MariaDB bertahan setelah container database di-restart. Buat tabel dan satu baris data dari dalam container.
sudo docker compose -f /root/docker-compose/docker-compose.yml exec -T db sh -c 'mariadb -u"$MARIADB_USER" -p"$MARIADB_PASSWORD" "$MARIADB_DATABASE" -e "CREATE TABLE IF NOT EXISTS uji_persistensi (id INT PRIMARY KEY); INSERT IGNORE INTO uji_persistensi VALUES (1); SELECT * FROM uji_persistensi;"'
Output:
id
1
Klien mariadb dapat menampilkan peringatan penggunaan password pada command line. Peringatan itu tidak menggagalkan perintah.
Restart container database, tunggu sampai sehat kembali, lalu baca ulang tabel yang sama.
sudo docker compose -f /root/docker-compose/docker-compose.yml restart db
until [ "$(sudo docker inspect -f '{{.State.Health.Status}}' lemp-db-1)" = "healthy" ]; do sleep 2; done
sudo docker compose -f /root/docker-compose/docker-compose.yml ps db
sudo docker compose -f /root/docker-compose/docker-compose.yml exec -T db sh -c 'mariadb -u"$MARIADB_USER" -p"$MARIADB_PASSWORD" "$MARIADB_DATABASE" -e "SELECT * FROM uji_persistensi;"'
Output:
Container lemp-db-1 Restarting
Container lemp-db-1 Started
NAME IMAGE COMMAND SERVICE CREATED STATUS PORTS
lemp-db-1 mariadb:11.8.9 "docker-entrypoint.sβ¦" db About a minute ago Up 15 seconds (healthy) 3306/tcp
id
1
Baris 1 tetap muncul setelah restart karena data tersimpan di /root/docker-compose/data/database pada host.
Troubleshootingβ
Nginx mengembalikan 502 Bad Gatewayβ
Gejala: curl mengembalikan halaman 502 Bad Gateway.
Penyebab: Container php berhenti atau fastcgi_pass tidak menunjuk ke port yang benar.
Perbaikan: Periksa log PHP-FPM dan status container.
sudo docker compose -f /root/docker-compose/docker-compose.yml logs php
sudo docker compose -f /root/docker-compose/docker-compose.yml ps
Output:
php-1 | NOTICE: fpm is running, pid 1
php-1 | NOTICE: ready to handle connections
php-1 | 172.18.0.4 - 25/Sep/2026:03:43:22 +0000 "GET /index.php" 200
PHP error "could not find driver"β
Gejala: Request HTTP menampilkan Koneksi database gagal: could not find driver.
Penyebab: Ekstensi pdo_mysql tidak ada di image, biasanya karena image dibangun sebelum Dockerfile diperbarui.
Perbaikan: Bangun ulang image PHP tanpa cache lalu nyalakan kembali.
sudo docker compose -f /root/docker-compose/docker-compose.yml build --no-cache php
sudo docker compose -f /root/docker-compose/docker-compose.yml up -d
Container db tidak menjadi healthyβ
Gejala: docker compose ps menampilkan status starting atau unhealthy untuk service db.
Penyebab: Inisialisasi data MariaDB pertama memakan waktu lebih lama, atau direktori data tidak dapat ditulis.
Perbaikan: Lihat log database dan beri waktu sampai health check selesai.
sudo docker compose -f /root/docker-compose/docker-compose.yml logs db
Output:
db-1 | mariadbd: ready for connections.
Port 8080 sudah dipakaiβ
Gejala: docker compose up gagal dengan pesan address already in use.
Penyebab: Ada proses lain yang menempati HTTP_PORT.
Perbaikan: Cari proses pemakai port, atau ubah HTTP_PORT di .env lalu jalankan ulang stack.
sudo ss -ltnp | grep ':8080'
SELinux menolak akses bind mountβ
Gejala: Nginx gagal membaca /etc/nginx/conf.d/default.conf, atau MariaDB gagal menulis ke /var/lib/mysql.
Penyebab: Direktori host belum memiliki label SELinux yang sesuai untuk container.
Perbaikan: Pastikan setiap bind mount pada docker-compose.yml memakai akhiran z atau ro,z, lalu jalankan ulang.
Pada sistem dengan SELinux nonaktif, akhiran z tidak berpengaruh dan tidak menimbulkan error.
Verifikasiβ
Stack LEMP dinyatakan siap ketika seluruh kondisi berikut terpenuhi.
docker compose psmenampilkanlemp-web-1danlemp-db-1berstatushealthy.curl http://localhost:8080/mengembalikan pesanLEMP berfungsi.- Ekstensi
pdo_mysqlmuncul saatphp -mdijalankan di containerphp. - Baris uji pada tabel
uji_persistensitetap ada setelah containerdbdi-restart.
Untuk melihat log terpusat saat ada masalah, gunakan perintah berikut.
sudo docker compose -f /root/docker-compose/docker-compose.yml logs --tail=50
Output:
mariadbd: ready for connections.
Version: '11.8.9-MariaDB-ubu2404' ... port: 3306
Untuk menghentikan stack tanpa menghapus data, jalankan perintah berikut.
sudo docker compose -f /root/docker-compose/docker-compose.yml down
Output:
Container lemp-db-1 Removed
Network lemp_default Removed
Perintah sudo rm -rf /root/docker-compose menghapus seluruh konfigurasi, aplikasi, dan data MariaDB secara permanen. Pastikan Anda sudah punya cadangan sebelum menjalankannya, dan jalankan down lebih dulu.
sudo docker compose -f /root/docker-compose/docker-compose.yml down --rmi local
sudo rm -rf /root/docker-compose
Output:
Image lemp-php:latest Removed
Dangling images Removed
