Deployment FPDP Menggunakan Dokploy

1. Hasil yang disediakan

Repository menyediakan deployment Dokploy berbasis Docker Compose dengan:

- PHP 8.3, Apache, dan extension PHP wajib;

- MySQL 8.4 pada jaringan Compose privat;

- migration otomatis dan repeatable sebelum Apache berjalan;

- bootstrap owner pertama yang opsional dan idempotent;

- persistent volume untuk MySQL dan storage/;

- health check aplikasi dan database;

- tanpa host-port binding, container_name, atau label Traefik manual;

- default production dengan web installer terkunci.

File utama: Dockerfile, dokploy-compose.yml, .env.dokploy.example, docker/apache-vhost.conf, docker/entrypoint.sh, dan database/bootstrap-owner.php.

2. Arsitektur

flowchart LR
    U[Visitor] --> DNS[DNS A/AAAA]
    DNS --> T[Dokploy Traefik<br/>terminasi TLS]
    T -->|HTTP port 80| A[FPDP app<br/>PHP 8.3 + Apache]
    A -->|jaringan privat| DB[(MySQL 8.4)]
    A --> S[(volume fpdp_storage)]
    DB --> D[(volume fpdp_mysql)]
    G[Git repository] --> DP[Dokploy Compose deployment]
    DP --> A
    DP --> DB

Hanya service app yang mendapat domain publik. MySQL tidak mempublikasikan port host.

3. Prasyarat

- Server Dokploy yang aktif.

- Repository Git yang dapat diakses Dokploy.

- Domain dengan record A menuju server Dokploy; gunakan AAAA hanya jika IPv6 benar-benar dikonfigurasi.

- Port 80 dan 443 dapat diakses.

- Secret aplikasi, user database, dan root MySQL yang kuat serta berbeda.

4. Membuat service Compose

1. Buat atau buka Project dan Environment di Dokploy.

2. Tambahkan service Docker Compose, bukan Docker Stack karena repository memakai build.

3. Pilih repository dan branch production.

4. Isi Compose Path dengan ./dokploy-compose.yml.

5. Gunakan satu replica app. Migration startup dan volume storage lokal pada MVP belum dirancang untuk multi-replica.

6. Isolated deployment boleh diaktifkan. Routing domain memakai fitur Domains Dokploy, tanpa label Traefik manual.

Dokploy menulis variable Compose ke .env di samping file Compose. dokploy-compose.yml memakai env_file: .env secara eksplisit karena variable dari editor Dokploy tidak otomatis masuk ke container jika tidak direferensikan atau dimuat oleh Compose.

5. Mengatur environment variable

Salin .env.dokploy.example ke editor Environment service Compose, lalu ganti seluruh placeholder.

Secret wajib:

APP_KEY=<minimal-64-karakter-hex-acak>
DB_PASSWORD=<password-user-database-kuat>
MYSQL_ROOT_PASSWORD=<password-root-berbeda-dan-kuat>
NODE_DOMAIN=example.com

Membuat application key:

php -r "echo bin2hex(random_bytes(32)), PHP_EOL;"

Khusus deployment pertama, isi:

BOOTSTRAP_OWNER_EMAIL=owner@example.com
BOOTSTRAP_OWNER_PASSWORD=<minimal-12-karakter>
BOOTSTRAP_OWNER_HANDLE=profile
BOOTSTRAP_OWNER_DISPLAY_NAME=Node Owner
BOOTSTRAP_OWNER_LOCALE=id

Startup menunggu MySQL, menjalankan migration, lalu membuat owner sebelum Apache menerima traffic. Proses ini idempotent: startup berikutnya menemukan email yang sama dan melewati pembuatan. Setelah login pertama berhasil, hapus semua variable BOOTSTRAP_OWNER_*, terutama password, lalu deploy ulang.

Model registrasi saat ini membentuk hostname node sebagai <handle>.<NODE_DOMAIN>. Contoh di atas menghasilkan profile.example.com; gunakan hostname yang sama pada Domains Dokploy. DNS dan kedua variable tersebut harus konsisten.

Pertahankan nilai berikut kecuali topologi Compose diubah:

APP_ENV=production
APP_DEBUG=false
DB_CONNECTION=mysql
DB_HOST=db
DB_PORT=3306

Google visitor login memerlukan GOOGLE_CLIENT_ID dan GOOGLE_CLIENT_SECRET. Daftarkan callback dengan mengganti HANDLE menjadi handle owner yang sebenarnya:

https://profile.example.com/api/v1/profiles/HANDLE/visitor-auth/google/callback

Google tidak mendukung wildcard pada redirect URI. Panduan pembuatan credential, consent screen, callback lokal/production, dan troubleshooting tersedia di Konfigurasi Google OAuth.

6. Domain dan HTTPS

Gunakan fitur Domains native Dokploy:

1. Buka tab Domains pada service Compose.

2. Tambahkan profile.example.com.

3. Pilih service app.

4. Gunakan container port 80 dan path /.

5. Aktifkan HTTPS serta provisioning certificate.

6. Simpan dan redeploy setelah perubahan domain.

Dokploy menambahkan routing Traefik secara internal. Jangan menambahkan ports: "80:80"; Compose memakai expose: 80 agar tidak menyebabkan konflik port host.

7. Deployment pertama

Klik Deploy dan pantau log. Startup sukses akan menampilkan migration yang diterapkan dan pesan bootstrap owner tanpa mencetak password atau access token.

Verifikasi:

curl --fail https://profile.example.com/api/v1/health

Login melalui:

https://profile.example.com/dashboard/posts

Setelah berhasil:

1. hapus semua variable BOOTSTRAP_OWNER_*;

2. redeploy;

3. uji health dan login kembali;

4. pastikan /.env tidak dapat diakses;

5. pastikan /install.php menyatakan instalasi terkunci.

8. Lifecycle deployment

flowchart TD
    P[Push ke branch production] --> B[Dokploy build image]
    B --> H[Health check MySQL]
    H --> M[Jalankan pending migration]
    M --> O{Email bootstrap tersedia?}
    O -->|Deployment pertama| C[Buat owner secara idempotent]
    O -->|Tidak| W[Jalankan Apache]
    C --> W
    W --> A[Health check aplikasi]
    A --> R[Traefik mengarahkan traffic HTTPS]

Migration bersifat forward-only dan dijalankan otomatis. Ambil backup sebelum migration yang mengubah atau menghapus data. Jangan menambah replica app sebelum migration locking dan shared/object storage tersedia.

9. Update dan rollback

Update normal

1. Backup MySQL dan volume storage.

2. Push atau merge revisi yang sudah diuji ke branch production.

3. Deploy melalui Dokploy.

4. Pantau log build, migration, Apache, dan health check.

5. Jalankan health check serta smoke test login/post.

Rollback aplikasi

Deploy ulang commit/image terakhir yang diketahui stabil. Rollback kode tidak membalikkan migration database. Rollback lintas perubahan schema hanya aman jika compatibility dan restore plan migration mengizinkannya.

Rollback database

Pulihkan backup sebelum deployment jika migration destruktif tidak dapat diperbaiki secara forward. Pulihkan MySQL dan fpdp_storage dari recovery point yang sama ketika record mereferensikan dokumen tersimpan.

10. Backup dan restore

Minimal backup kedua named volume:

- fpdp_mysql: database dan migration state;

- fpdp_storage: dokumen CV, runtime files, dan install lock.

Contoh logical backup dari terminal Dokploy atau shell server:

docker compose -f dokploy-compose.yml exec -T db \
  mysqldump -u root -p"$MYSQL_ROOT_PASSWORD" --single-transaction --routines --triggers fpdp \
  > fpdp-$(date +%F-%H%M).sql

Simpan backup di luar server/volume yang sama, enkripsi saat disimpan, dan uji restore secara rutin di staging. Container boleh dibuat ulang; ketahanan data bergantung pada volume.

11. Troubleshooting

| Gejala | Pemeriksaan |

|---|---|

| Build extension PHP gagal | Periksa build log dan rebuild tanpa cache setelah memastikan repository terbaru |

| App terus menunggu MySQL | Pastikan DB_HOST=db, credential sama dengan variable service MySQL, dan health check db lulus |

| Domain 404/502 | Domain harus menuju service app, port 80; redeploy dan periksa health app |

| Migration gagal | Periksa migration spesifik pada log; jangan menghapus volume database sebagai jalan pintas |

| Bootstrap owner gagal | Password 12–128 karakter; handle huruf kecil/angka/hyphen sepanjang 3–63 karakter |

| CV hilang setelah redeploy | Pastikan volume fpdp_storage:/var/www/html/storage tetap terpasang |

| Detail debug terlihat | Atur APP_ENV=production, APP_DEBUG=false, lalu redeploy |

| Installer dapat dijalankan | DISABLE_WEB_INSTALLER=true membuat install lock persisten setiap startup |

12. Checklist keamanan

- Jangan tambahkan mapping ports pada service db.

- Gunakan nilai berbeda untuk DB_PASSWORD dan MYSQL_ROOT_PASSWORD.

- Hapus credential bootstrap setelah deployment pertama.

- Simpan secret pada variable Dokploy atau external secret provider, bukan Git.

- Gunakan APP_DEBUG=false dan HTTPS.

- Batasi akses dashboard Dokploy.

- Backup dan uji restore kedua persistent volume.

- Jangan menyalin token, OAuth secret, atau password database dari log ke ticket.

13. Referensi Dokploy

- Docker Compose di Dokploy

- Domain untuk Compose

- Environment variables

- Troubleshooting domain