Bangun Vercel Sendiri di VPS? Tutorial Docker dan Coolify untuk Pemula
Ringkasan. Tutorial lengkap membangun dan men-deploy proyek PHP ke VPS seperti Vercel: membuat aplikasi dari nol, mengelola Docker dan Coolify, GitHub auto-deploy, PHP CRUD dengan MariaDB, Laravel, domain HTTPS, backup–restore, rollback, monitoring, dan multi-proyek. Semua source code ditampilkan langsung dalam artikel, tanpa perlu mengunduh sample.
Bayangkan setiap proyek PHP bisa dikelola seperti aplikasi frontend modern: pilih repository, isi domain, klik Deploy, lalu perubahan berikutnya cukup git push. Itulah pengalaman yang kita bangun dengan Docker + Coolify di VPS sendiri. Pada tutorial ini kita membuat semua file langsung dari nol, sehingga pembaca tidak perlu mengunduh proyek contoh.

1. Konsep: VPS rasa Vercel
Pada hosting tradisional, pengembang sering harus login SSH, menyiapkan PHP/Apache di host, membuat virtual host, menyalin kode, lalu mengulang prosedur tersebut ketika meluncurkan proyek baru. Dalam pendekatan ini, aplikasi dipaketkan menjadi Docker image, sedangkan Coolify menyimpan konfigurasi deployment dan mengelola container, proxy, domain, environment variables, serta log.
Istilah “rasa Vercel” adalah analogi pengalaman pengembang, bukan klaim bahwa Coolify menyediakan seluruh jaminan layanan platform terkelola. Keamanan VPS, backup, kapasitas server, dan pemantauan sumber daya tetap menjadi tanggung jawab operator.
| Komponen | Fungsi | Yang dikerjakan pembaca |
|---|---|---|
| GitHub | Menyimpan kode sumber dan riwayat commit | Push kode ke branch main |
| Docker | Memastikan runtime aplikasi konsisten | Menulis Dockerfile sekali, uji lokal |
| Coolify | Mengatur build, deployment, domain, SSL, log | Hubungkan repository dan konfigurasi aplikasi |
| VPS | Menjalankan container produksi | Siapkan DNS, sumber daya, keamanan dan backup |
2. Tools dan prasyarat
Praktik inti menggunakan aplikasi PHP tanpa framework. Pembaca membutuhkan komputer dengan Docker Desktop atau Docker Engine + Compose, Git, editor seperti VS Code, akun GitHub, VPS Linux yang didukung Coolify, serta domain yang bisa diedit DNS-nya. Contoh perintah lokal ditulis untuk Terminal macOS/Linux; Windows dapat memakai PowerShell dengan penyesuaian sintaks pembuatan folder.
| Tools | Kegunaan | Dokumentasi resmi |
|---|---|---|
| Docker | Build dan menjalankan container | docs.docker.com |
| Git | Version control dan push repository | git-scm.com |
| VS Code | Menulis source code PHP | code.visualstudio.com |
| GitHub | Hosting repository dan webhook | docs.github.com |
| Coolify | Deployment platform pada VPS | coolify.io/docs |
Periksa tools dan akses VPS
docker --version
docker compose version
git --versionContoh output akan memuat nomor versi masing-masing tools. Apabila Docker tidak terdeteksi, jalankan atau pasang Docker terlebih dahulu. Pastikan port 80 dan 443 pada VPS terbuka untuk HTTP/HTTPS; port dashboard Coolify dibatasi sesuai kebutuhan administrasi, bukan dibuka tanpa kontrol.
3. Memahami alur GitHub → Coolify → Docker

Perhatikan perbedaan sumber dan runtime: GitHub menyimpan file, Coolify mengambil commit terpilih, Docker membangun image berdasarkan Dockerfile, lalu container berjalan pada server. Proxy Coolify mengarahkan domain ke port HTTP internal container. Dengan demikian, pembaca tidak perlu membuka port 8080 ke internet untuk setiap proyek.
4. Membuat struktur proyek PHP dari nol
Kita membuat aplikasi mini bernama PHP Docker Demo yang menampilkan daftar pekerjaan deployment, versi aplikasi, dan status environment. File utama berada pada folder public/ agar hanya konten yang memang boleh diakses melalui Apache yang disajikan ke web.
mkdir -p php-coolify-demo/public/assets
mkdir -p php-coolify-demo/tests
cd php-coolify-demo
touch public/index.php public/health.php public/assets/style.css
touch Dockerfile compose.yaml compose.coolify.yaml compose.database.yaml
touch .dockerignore .gitignore .env.examplephp-coolify-demo/
├── public/
│ ├── index.php
│ ├── health.php
│ └── assets/
│ └── style.css
├── tests/
│ └── smoke.sh
├── Dockerfile
├── compose.yaml
├── compose.coolify.yaml
├── compose.database.yaml
├── .dockerignore
├── .gitignore
└── .env.exampleFolder dan file kosong baru merupakan kerangka. Langkah berikutnya menyalin seluruh isi setiap file seperti ditampilkan di bawah. Nama file dan struktur folder harus tepat agar perintah Docker dapat menemukan berkasnya.
5. Membuat halaman PHP dan endpoint health
Langkah 1 — Tulis seluruh isi public/index.php
Buka public/index.php di VS Code, lalu ganti isinya dengan kode berikut. Kode mengambil APP_NAME dan APP_ENV dari environment, menampilkan versi, serta mencetak data task demonstrasi. Fungsi safe() mencegah karakter HTML pada data tampil sebagai markup tidak diinginkan.
<?php
declare(strict_types=1);
$appName = getenv('APP_NAME') ?: 'PHP Docker Demo';
$environment = getenv('APP_ENV') ?: 'production';
$version = '1.0.0';
$tasks = [
['name' => 'Membuat Dockerfile', 'status' => 'Selesai', 'owner' => 'Developer'],
['name' => 'Menghubungkan GitHub ke Coolify', 'status' => 'Selesai', 'owner' => 'DevOps'],
['name' => 'Mengaktifkan deploy otomatis', 'status' => 'Dalam proses', 'owner' => 'DevOps'],
];
function safe(string $value): string {
return htmlspecialchars($value, ENT_QUOTES | ENT_SUBSTITUTE, 'UTF-8');
}
?>
<!doctype html>
<html lang="id">
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width,initial-scale=1">
<meta name="description" content="Contoh aplikasi PHP Docker yang di-deploy otomatis melalui Coolify.">
<title><?= safe($appName) ?></title>
<link rel="stylesheet" href="/assets/style.css">
</head>
<body>
<main class="shell">
<div class="eyebrow">PHP + Docker + Coolify</div>
<h1><?= safe($appName) ?></h1>
<p>Proyek PHP sederhana yang dapat dipaketkan menjadi Docker image dan dikelola melalui Coolify.</p>
<div class="meta"><span>Versi <?= safe($version) ?></span><span>Environment <?= safe($environment) ?></span><span>PHP <?= safe(PHP_VERSION) ?></span></div>
<section class="panel">
<h2>Contoh task proyek</h2>
<div class="table-wrap"><table><thead><tr><th>Pekerjaan</th><th>Penanggung jawab</th><th>Status</th></tr></thead><tbody>
<?php foreach ($tasks as $task): ?>
<tr><td><?= safe($task['name']) ?></td><td><?= safe($task['owner']) ?></td><td><?= safe($task['status']) ?></td></tr>
<?php endforeach; ?>
</tbody></table></div>
</section>
<p class="note">Ubah teks ini di <code>public/index.php</code>, commit, lalu push ke branch yang terhubung ke Coolify.</p>
</main>
</body>
</html>Langkah 2 — Buat halaman CSS
CSS berikut membuat aplikasi memiliki tampilan kartu dan tabel yang modern. CSS sengaja disimpan terpisah agar mudah dirawat dan dapat diganti tanpa mengubah logika PHP.
:root{font-family:system-ui,-apple-system,Segoe UI,sans-serif;color-scheme:light dark}*{box-sizing:border-box}body{margin:0;min-height:100vh;background:linear-gradient(135deg,#fef2f2,#f8fafc);color:#152238}.shell{max-width:930px;margin:8vh auto;padding:24px}.eyebrow{display:inline-block;border:1px solid #fca5a5;background:#fff1f2;color:#b91c1c;border-radius:999px;padding:8px 16px;font-weight:700}h1{font-size:clamp(2rem,5vw,3.7rem);margin:16px 0}p{line-height:1.75;color:#475569}.meta{display:flex;flex-wrap:wrap;gap:10px;margin:24px 0}.meta span{border:1px solid #e2e8f0;background:white;padding:8px 12px;border-radius:10px}.panel{margin-top:30px;background:white;border:1px solid #e2e8f0;border-radius:20px;padding:20px;box-shadow:0 16px 45px #0f172a0b}.table-wrap{overflow-x:auto}table{width:100%;border-collapse:collapse;text-align:left;min-width:480px}th,td{border-bottom:1px solid #e2e8f0;padding:14px 10px}tbody tr:last-child td{border-bottom:0}.note{font-size:.93rem}code{background:#e2e8f0;border-radius:5px;padding:3px 6px}@media(prefers-color-scheme:dark){body{background:linear-gradient(135deg,#111827,#0b1020);color:#f8fafc}p{color:#cbd5e1}.panel,.meta span{background:#192331;border-color:#334155}th,td{border-color:#334155}.eyebrow{background:#3c1f28;color:#fda4af;border-color:#9f1239}code{background:#334155}}Langkah 3 — Buat health endpoint
Coolify atau Docker perlu cara sederhana untuk memastikan aplikasi masih merespons HTTP. Endpoint /health.php mengembalikan teks OK; untuk aplikasi yang lebih kompleks, health check dapat dibedakan menjadi liveness dan readiness.
<?php
declare(strict_types=1);
header('Content-Type: text/plain; charset=utf-8');
header('Cache-Control: no-store');
echo 'OK';Halaman web menampilkan nama aplikasi, versi 1.0.0, label environment, versi PHP, dan tabel tiga task. URL /health.php mengembalikan OK.
6. Menulis Dockerfile dan berkas Docker

Langkah 4 — Dockerfile (Apache + PHP)
Dockerfile mendefinisikan image produksi: basis PHP+Apache, ekstensi PDO MySQL untuk latihan database berikutnya, modul Apache rewrite, direktori source, port internal 80, dan health check. EXPOSE 80 hanya mendokumentasikan port container; ia tidak membuka port host secara otomatis.
FROM php:8.3-apache
RUN set -eux; \
docker-php-ext-install pdo_mysql; \
a2enmod rewrite headers; \
{ echo 'ServerTokens Prod'; echo 'ServerSignature Off'; } > /etc/apache2/conf-available/security-extra.conf; \
a2enconf security-extra
COPY public/ /var/www/html/
EXPOSE 80
HEALTHCHECK --interval=30s --timeout=5s --start-period=15s --retries=3 \
CMD php -r 'exit(@file_get_contents("http://127.0.0.1/health.php") === "OK" ? 0 : 1);'Langkah 5 — .dockerignore dan .gitignore
File ini mencegah file rahasia dan berkas development ikut terbawa saat proses build atau masuk commit Git. Perhatikan bahwa .env.example boleh dibagikan karena hanya berisi contoh, sedangkan .env berisi nilai privat.
.git
.github
.env
.env.*
!.env.example
README.md
tests/
*.md.env
.DS_Store
.idea/
.vscode/
*.logLangkah 6 — Compose lokal dan Compose Coolify
Dua Compose dipakai untuk tujuan berbeda: file compose.yaml menerbitkan port 8080 hanya di loopback komputer lokal; file compose.coolify.yaml tidak menerbitkan port host karena akses publik akan dikelola proxy Coolify. Menggunakan file terpisah mencegah konflik port antarproyek pada VPS.
services:
web:
build:
context: .
dockerfile: Dockerfile
restart: unless-stopped
environment:
APP_NAME: ${APP_NAME:-PHP Docker Demo}
APP_ENV: ${APP_ENV:-production}
ports:
- "127.0.0.1:8080:80" # DEVELOPMENT ONLY; remove for Coolify Git Compose deployments# Repository-owned Compose for Coolify: no host port needed.
services:
web:
build:
context: .
dockerfile: Dockerfile
restart: unless-stopped
environment:
APP_NAME: ${APP_NAME:-PHP Docker Demo}
APP_ENV: ${APP_ENV:-production}
expose:
- "80"7. Menjalankan aplikasi dengan Docker di komputer
Opsional — Buat tes otomatis sederhana
Sebelum pengujian lokal, isi berkas tests/smoke.sh dengan skrip berikut. Ia memeriksa endpoint kesehatan dan memastikan halaman utama mengandung label demo. Ini adalah smoke test, bukan pengganti pengujian unit atau keamanan.
#!/usr/bin/env sh
set -eu
URL="${1:-http://127.0.0.1:8080}"
echo "Checking ${URL}/health.php"
[ "$(curl -fsS "${URL}/health.php")" = "OK" ]
echo "Checking homepage content"
curl -fsS "${URL}/" | grep -q 'PHP + Docker + Coolify'
echo 'Smoke test passed.'
Sesudah aplikasi hidup, jalankan sh tests/smoke.sh. Hasil yang diharapkan adalah Smoke test passed. Apabila tes gagal, cek URL, status container, dan log aplikasi.
Sebelum deploy ke VPS, jalankan image yang sama di laptop. Langkah ini menangkap kesalahan PHP dan Docker lebih awal sehingga proses debugging tidak harus dimulai dari log produksi.
docker compose up -d --build
docker compose ps
curl -i http://localhost:8080/
curl -i http://localhost:8080/health.php
docker compose logs --tail=50 webPerintah pertama membangun image dan membuat container. docker compose ps menunjukkan status layanan. curl ke halaman utama seharusnya memberikan HTTP 200; ke /health.php seharusnya menghasilkan OK. Contoh output berikut adalah ekspektasi demonstrasi, bukan hasil deploy VPS pengguna.
HTTP/1.1 200 OK
Content-Type: text/plain; charset=utf-8
OKBuka http://localhost:8080 dari komputer yang menjalankan Docker. Jika port 8080 sudah dipakai, ubah hanya port sisi kiri pada 127.0.0.1:8080:80, misalnya menjadi 127.0.0.1:8081:80, lalu gunakan URL yang sesuai.
docker compose down8. Membuat repository GitHub dan push pertama
Buat repository kosong di GitHub, misalnya php-coolify-demo, tanpa README otomatis jika folder lokal sudah memiliki file sendiri. Setelah itu jalankan perintah berikut; ganti GITHUB_USER dengan akun Anda. Untuk akses GitHub gunakan SSH key atau Git Credential Manager, bukan menuliskan token rahasia di artikel maupun source code.
git init
git branch -M main
git add .
git commit -m "Initial PHP Docker app"
git remote add origin https://github.com/GITHUB_USER/php-coolify-demo.git
git push -u origin mainPastikan file Dockerfile, compose.coolify.yaml, dan folder public terlihat pada repository GitHub, sedangkan file .env tidak ikut terkirim. Berkas proyek ditampilkan lengkap di artikel sehingga tidak ada tahap mengunduh sample project.
9. Deploy pertama di dashboard Coolify

Masuk ke dashboard Coolify pada VPS. Nama menu bisa sedikit berbeda menurut versi, tetapi pola pengaturannya mengikuti menu **Projects**, environment seperti **production**, dan **New Resource**. Untuk proyek tunggal berbasis Dockerfile, pilih Application yang bersumber dari GitHub; jangan memilih Service Docker Compose Empty untuk jalur dasar ini.
- Buka Projects, buat proyek baru misalnya
php-apps, lalu buka environmentproduction. - Klik New Resource → pilih Application yang memakai sumber GitHub (GitHub App atau public repository).
- Hubungkan akun GitHub melalui mekanisme otorisasi resmi Coolify dan pilih repository
php-coolify-demo. - Pilih branch
main. Pada bagian build, gunakan Dockerfile sebagai build strategy. - Pastikan Base Directory mengarah ke root repository dan Dockerfile Location adalah
Dockerfile. - Isi Ports Exposes / port internal aplikasi dengan
80, bukan 8080. - Tambahkan environment variables
APP_NAMEdanAPP_ENVbila dibutuhkan. - Klik Deploy; buka deployment log dan pastikan build serta startup container berhasil.
Coolify akan mengunduh source yang tersimpan di Git, membangun image memakai Dockerfile, menjalankan container, lalu mengatur routing domain ke HTTP internal port 80. Deployment pertama biasanya manual; setelah webhook diaktifkan, commit berikutnya dapat memicu deployment baru.
10. Memasang domain, DNS, dan HTTPS
Contoh domain tutorial: php.example.com (ganti dengan subdomain yang benar-benar Anda miliki). Buat record DNS A ke IP publik VPS dan masukkan domain yang sama ke pengaturan Domain Coolify. Sertifikat HTTPS otomatis membutuhkan DNS yang mengarah benar dan akses jaringan ke port HTTP/HTTPS yang dibutuhkan mekanisme validasi.
- Di pengelola DNS, buat record
Auntuk subdomain yang diinginkan menuju IP publik VPS. - Jika menggunakan Cloudflare, periksa mode proxy dan SSL/TLS agar tidak terjadi loop HTTPS. Jangan menjadikan mode Flexible sebagai rekomendasi umum untuk origin yang juga melayani HTTPS.
- Pada Application → Domains, masukkan
https://php.example.comatau domain sesuai antarmuka Coolify. - Simpan perubahan lalu redeploy apabila diperlukan, tunggu DNS dan sertifikat berlaku, kemudian akses halaman dari browser.
- Periksa
https://php.example.com/health.phpdan pastikan hasilnyaOK.
dig +short php.example.com
curl -I https://php.example.com/
curl https://php.example.com/health.phpDomain mengarah ke aplikasi PHP; browser tidak menunjukkan peringatan sertifikat dan health endpoint memberikan hasil OK. Nilai dan URL di atas merupakan contoh; sesuaikan dengan domain sebenarnya.
11. Menguji deployment otomatis dengan git push
Inilah tujuan utamanya. Setelah GitHub App/webhook dan opsi automatic deployment untuk branch main aktif, ubah satu baris versi aplikasi, commit, lalu push. Tidak perlu SSH ke VPS untuk memindahkan file aplikasi.
- Pada pengaturan Application, periksa integrasi GitHub serta aktifkan auto-deploy/webhook untuk branch target.
- Buka
public/index.phppada VS Code dan ubah$version = '1.0.0';menjadi$version = '1.1.0';. - Simpan file dan jalankan perintah Git berikut.
- Buka halaman deployment Coolify untuk melihat build dari commit terbaru.
- Refresh website publik dan pastikan tulisan versi berubah menjadi
1.1.0.
git add public/index.php
git commit -m "Bump version to 1.1.0"
git push origin mainJika versi di website tidak berubah, periksa urutannya: commit masuk ke branch benar, webhook GitHub terkirim, deployment baru dibuat, build selesai, healthcheck lolos, dan request domain mengarah ke container terbaru. Penanganan kegagalan build dan rollbacks perlu dibahas sebagai kebijakan operasional, bukan dianggap selalu otomatis sempurna.
12. VPS dari nol: keamanan dan instalasi Coolify
Coolify dapat dipasang pada VPS Linux, termasuk Ubuntu LTS. Panduan berikut menggunakan server baru dengan akses SSH dan alamat IPv4 publik. Jika Coolify sudah terpasang, Anda boleh langsung melanjutkan ke bagian GitHub App. Urutan ini penting karena dashboard Coolify mempunyai izin pengelolaan container dan server yang sensitif.
12.1 Pilih VPS dan siapkan DNS
Untuk latihan PHP–MariaDB sekaligus Coolify, siapkan VPS dengan sumber daya yang memadai (misalnya mulai 2 vCPU, 4 GB RAM, dan SSD; kebutuhan sebenarnya bergantung pada beban). Contoh domain menggunakan app.example.com dan panel.example.com; ganti dengan domain milik Anda.
| Komponen | Contoh | Kegunaan |
|---|---|---|
| VPS | Ubuntu 24.04 LTS | Menjalankan Coolify dan Docker |
| A record | app.example.com → IP VPS | Alamat aplikasi publik |
| A record | panel.example.com → IP VPS | Dashboard Coolify; amankan aksesnya |
| Port jaringan | 22, 80, 443, 8000; 6001/6002 jika dibutuhkan | SSH, HTTP(S), akses dashboard awal dan terminal |
12.2 Hubungkan terminal melalui SSH
Di macOS/Linux gunakan Terminal, sedangkan Windows gunakan PowerShell. Ganti alamat IP dan nama pengguna. Perintah administrator perlu dilakukan pada VPS yang memang Anda miliki.
ssh root@IP_VPS_ANDA
cat /etc/os-release
free -h
df -h
12.3 Instal Coolify menggunakan skrip resmi
Perintah instalasi berikut berasal dari dokumentasi resmi Coolify. Baca dokumentasi dan skrip terlebih dahulu sebelum mengeksekusi skrip installer dengan hak administrator.
curl -fsSL https://cdn.coollabs.io/coolify/install.sh | bash
Setelah instalasi berhasil, buka http://IP_VPS_ANDA:8000 untuk membuat administrator pertama. Segera simpan rahasia instalasi /data/coolify/source/.env secara aman di luar server. Akses dashboard sebaiknya dipindahkan ke domain HTTPS, lalu akses publik ke port dashboard langsung dibatasi.
12.4 Validasi server dan konfigurasi proxy
Di Coolify, periksa server lokal sudah terhubung, proxy berjalan, dan domain panel dapat dirutekan. Jangan mengaktifkan proxy Cloudflare secara membabi-buta pada tahap diagnostik; pastikan DNS, forwarding port, dan HTTPS pada origin benar dahulu.
docker --version
docker ps
curl -I http://IP_VPS_ANDA:8000

13. Hubungkan GitHub App: pengaturan klik demi klik
Untuk repository privat atau workflow deployment otomatis, GitHub App paling praktis. Pembuatan GitHub App memberi Coolify izin terbatas untuk membaca repository yang Anda pilih dan menerima event perubahan. Pilih hanya repository yang diperlukan, bukan seluruh akun jika tidak dibutuhkan.
13.1 Siapkan repository
Masuk ke GitHub, buat repository baru, lalu unggah source code dengan git push. Untuk contoh CRUD pada bab berikut, root repository harus berisi Dockerfile, compose.coolify.yaml, public/, src/, dan database/.
13.2 Daftarkan GitHub App di Coolify
Buka Sources → GitHub → Add GitHub App atau menu sumber Git yang setara pada versi Coolify Anda, gunakan alur instalasi otomatis, masuk ke GitHub, kemudian pilih akun dan repository yang diizinkan. Kembali ke Coolify dan pastikan sumber menunjukkan status terhubung. Nama menu dapat berubah antarrilis.
13.3 Buat Application dari repository
Buka Projects → New Project jika proyek belum ada; pilih environment production, kemudian + New → Application → GitHub App. Pilih repository, branch main, dan build strategy Docker Compose untuk versi CRUD. Tetapkan Base Directory / dan Compose Location /compose.coolify.yaml atau jalur yang diminta UI. Periksa isi Compose setelah disimpan.
13.4 Atur variabel dan domain
Di Environment Variables, isi DB_PASSWORD dan DB_ROOT_PASSWORD dengan kata sandi berbeda yang kuat; APP_NAME, DB_NAME, dan DB_USER juga dapat ditentukan. Di Domains, pilih service web, protokol HTTPS, hostname app.example.com, internal port 80. Service db tidak diberi domain. Setelah itu pilih Deploy.
13.5 Aktifkan auto-deploy dan verifikasi
Di aplikasi, buka Advanced → Deployment → Auto deploy → Deploy on push (webhooks). Dorong commit baru pada branch yang dipilih dan amati Deployment Logs: commit yang benar, build, container, dan health check. Bila tidak muncul deployment, cek status GitHub App, webhook, branch, dan pilihan Auto deploy.

14. Proyek lengkap: CRUD PHP + MariaDB dari nol
Sekarang kita naik satu tingkat: bukan lagi halaman PHP statis, melainkan aplikasi penyimpanan tugas KosKu. Semua file ditampilkan di bawah sehingga pembaca tidak perlu mengunduh sample proyek. Aplikasi dapat menambah tugas, mengubah status, menghapus tugas, menyimpan database di named volume, dan menjalankan health check.
14.1 Buat folder proyek dan struktur file
Buat repository baru untuk latihan database agar mudah membedakan versi statis dan versi CRUD. Struktur ini juga cocok langsung sebagai root repository Coolify.
mkdir -p kosku-crud/public kosku-crud/src kosku-crud/database
cd kosku-crud
touch Dockerfile compose.yaml compose.coolify.yaml .env.example .gitignore .dockerignore
touch public/index.php public/style.css public/health.php src/db.php database/init.sql
kosku-crud/
├── Dockerfile
├── compose.yaml # hanya lokal
├── compose.coolify.yaml # deployment di Coolify
├── .env.example # contoh variabel, bukan rahasia
├── .gitignore
├── .dockerignore
├── public/
│ ├── index.php
│ ├── style.css
│ └── health.php
├── src/
│ └── db.php
└── database/
└── init.sql
File: database/init.sql
SQL berikut membuat tabel tugas dengan ID, judul, status selesai, dan tanggal pembuatan. File ini dijalankan MariaDB pada inisialisasi volume yang masih kosong; file ini bukan mekanisme migrasi otomatis untuk database yang sudah berisi data.
CREATE TABLE IF NOT EXISTS tasks (
id BIGINT UNSIGNED AUTO_INCREMENT PRIMARY KEY,
title VARCHAR(180) NOT NULL,
completed TINYINT(1) NOT NULL DEFAULT 0,
created_at TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci;
File: src/db.php
Koneksi PDO menggunakan variabel lingkungan, prepared statements, exception handling, dan charset UTF-8. Dengan demikian kredensial tidak ditulis permanen di source code.
<?php
declare(strict_types=1);
function db(): PDO
{
static $pdo = null;
if ($pdo instanceof PDO) { return $pdo; }
$host = getenv('DB_HOST') ?: 'db';
$port = getenv('DB_PORT') ?: '3306';
$name = getenv('DB_NAME') ?: 'kosku';
$user = getenv('DB_USER') ?: 'kosku_user';
$pass = getenv('DB_PASSWORD');
if ($pass === false || $pass === '') {
throw new RuntimeException('DB_PASSWORD belum dikonfigurasi');
}
$dsn = "mysql:host={$host};port={$port};dbname={$name};charset=utf8mb4";
$pdo = new PDO($dsn, $user, $pass, [
PDO::ATTR_ERRMODE => PDO::ERRMODE_EXCEPTION,
PDO::ATTR_DEFAULT_FETCH_MODE => PDO::FETCH_ASSOC,
PDO::ATTR_EMULATE_PREPARES => false,
]);
return $pdo;
}
File: public/index.php
Aplikasi CRUD utama menggunakan metode POST, token CSRF sederhana, escape output HTML, validasi input, dan Post/Redirect/Get untuk mencegah submit ganda. Contoh sengaja tidak memiliki login sehingga tidak boleh dipakai untuk menyimpan data sensitif atau dibuka luas sebagai aplikasi produksi tanpa autentikasi.
<?php
declare(strict_types=1);
ini_set('display_errors', '0');
ini_set('session.use_strict_mode', '1');
if (isset($_SERVER['HTTPS']) && $_SERVER['HTTPS'] === 'on') {
ini_set('session.cookie_secure', '1');
}
ini_set('session.cookie_httponly', '1');
ini_set('session.cookie_samesite', 'Lax');
session_start();
require_once '/var/www/src/db.php';
function escape(string $text): string {
return htmlspecialchars($text, ENT_QUOTES | ENT_SUBSTITUTE, 'UTF-8');
}
if (!isset($_SESSION['csrf'])) {
$_SESSION['csrf'] = bin2hex(random_bytes(32));
}
$error = null;
try {
if ($_SERVER['REQUEST_METHOD'] === 'POST') {
if (!hash_equals($_SESSION['csrf'], (string)($_POST['csrf'] ?? ''))) {
http_response_code(403); exit('Permintaan ditolak (CSRF).');
}
$action = (string)($_POST['action'] ?? '');
$id = filter_var($_POST['id'] ?? null, FILTER_VALIDATE_INT);
if ($action === 'add') {
$title = trim((string)($_POST['title'] ?? ''));
if ($title === '' || mb_strlen($title) > 180) {
throw new InvalidArgumentException('Judul wajib 1–180 karakter.');
}
$stmt = db()->prepare('INSERT INTO tasks (title) VALUES (?)');
$stmt->execute([$title]);
} elseif ($action === 'toggle' && $id && $id > 0) {
$stmt = db()->prepare('UPDATE tasks SET completed=1-completed WHERE id=?');
$stmt->execute([$id]);
} elseif ($action === 'delete' && $id && $id > 0) {
$stmt = db()->prepare('DELETE FROM tasks WHERE id=?');
$stmt->execute([$id]);
} else {
throw new InvalidArgumentException('Aksi tidak dikenal.');
}
header('Location: /', true, 303); exit;
}
$tasks = db()->query('SELECT id,title,completed FROM tasks ORDER BY id DESC')->fetchAll();
} catch (InvalidArgumentException $e) {
$error = $e->getMessage(); $tasks = [];
} catch (Throwable $e) {
error_log('KosKu database error: ' . $e->getMessage());
http_response_code(503);
$error = 'Aplikasi sedang mengalami masalah. Coba lagi nanti.';
$tasks = [];
}
$appName = getenv('APP_NAME') ?: 'KosKu Tasks';
?><!doctype html>
<html lang="id"><head><meta charset="utf-8"><meta name="viewport" content="width=device-width, initial-scale=1">
<title><?= escape($appName) ?></title><link rel="stylesheet" href="/style.css"></head>
<body><main><h1><?= escape($appName) ?></h1><p>Proyek PHP + MariaDB yang dijalankan menggunakan Docker dan Coolify.</p>
<?php if ($error): ?><div class="notice" role="alert"><?= escape($error) ?></div><?php endif; ?>
<section class="card"><h2>Tambah tugas</h2>
<form method="post" class="add"><input type="hidden" name="csrf" value="<?= escape($_SESSION['csrf']) ?>">
<input type="hidden" name="action" value="add"><input type="text" name="title" maxlength="180" placeholder="Contoh: Review desain aplikasi" required>
<button type="submit">Tambah</button></form></section>
<section class="card"><h2>Daftar tugas (<?= count($tasks) ?>)</h2><ul>
<?php foreach ($tasks as $task): ?><li><span class="<?= $task['completed'] ? 'done' : '' ?>"><?= escape($task['title']) ?></span>
<div class="actions"><form method="post"><input type="hidden" name="csrf" value="<?= escape($_SESSION['csrf']) ?>">
<input type="hidden" name="id" value="<?= (int)$task['id'] ?>"><input type="hidden" name="action" value="toggle">
<button type="submit" class="secondary">Ubah status</button></form>
<form method="post" onsubmit="return confirm('Hapus tugas ini?')"><input type="hidden" name="csrf" value="<?= escape($_SESSION['csrf']) ?>">
<input type="hidden" name="id" value="<?= (int)$task['id'] ?>"><input type="hidden" name="action" value="delete">
<button type="submit">Hapus</button></form></div></li><?php endforeach; ?>
</ul><?php if (!$tasks): ?><p>Belum ada tugas atau data belum tersedia.</p><?php endif; ?></section>
<p><small>Demo edukasi: belum memiliki autentikasi dan otorisasi akun pengguna.</small></p></main></body></html>
File: public/style.css
CSS aplikasi terpisah dari HTML Blogger; tampilannya responsif dan mengikuti preferensi dark/light pengguna.
:root{font-family:system-ui,-apple-system,sans-serif;color-scheme:light dark}
*{box-sizing:border-box}body{margin:0;background:#f5f7fb;color:#1e293b}
main{width:min(760px,calc(100% - 32px));margin:44px auto}
h1{margin:0 0 8px;color:#b91c35}p{line-height:1.6;color:#475569}
.card{background:#fff;border:1px solid #e2e8f0;border-radius:18px;padding:24px;margin:18px 0;box-shadow:0 7px 24px #0f172a08}
form.add{display:flex;gap:9px}input[type=text]{flex:1;min-width:0;padding:12px;border:1px solid #cbd5e1;border-radius:10px}
button{background:#d7193f;color:#fff;border:0;border-radius:10px;padding:10px 14px;cursor:pointer}
button.secondary{background:#334155}ul{list-style:none;padding:0}li{display:flex;justify-content:space-between;gap:12px;align-items:center;padding:13px 0;border-bottom:1px solid #e2e8f0}
li:last-child{border:0}.actions{display:flex;gap:8px}.done{text-decoration:line-through;opacity:.6}.notice{padding:10px 12px;background:#fff1f2;border-radius:10px;color:#881337}
@media(max-width:560px){form.add,li{flex-direction:column;align-items:stretch}.actions{justify-content:flex-end}}
@media(prefers-color-scheme:dark){body{background:#0f172a;color:#e2e8f0}.card{background:#172033;border-color:#334155}p{color:#cbd5e1}h1{color:#fb7185}li{border-color:#334155}input[type=text]{background:#0b1220;color:#fff;border-color:#475569}.notice{background:#4c0519;color:#fecdd3}}
File: public/health.php
Endpoint liveness ini hanya memastikan PHP–Apache merespons; bukan menjamin MariaDB sehat. Kesehatan database diperiksa dari Compose dan tes CRUD.
<?php
header('Content-Type: application/json; charset=utf-8');
http_response_code(200);
echo json_encode(['status' => 'ok', 'service' => 'kosku-web']);
File: Dockerfile
Image menggunakan PHP Apache, ekstensi PDO MySQL dan mbstring, curl untuk pemeriksaan kesehatan, serta menyalin source kode aplikasi.
FROM php:8.3-apache
RUN apt-get update && apt-get install -y --no-install-recommends curl libonig-dev \
&& docker-php-ext-install pdo_mysql mbstring \
&& a2enmod rewrite \
&& rm -rf /var/lib/apt/lists/*
COPY public/ /var/www/html/
COPY src/ /var/www/src/
RUN chown -R www-data:www-data /var/www/html
EXPOSE 80
HEALTHCHECK --interval=30s --timeout=5s --start-period=20s --retries=3 \
CMD curl -fsS http://127.0.0.1/health.php || exit 1
File: .dockerignore
File rahasia dan berkas yang tidak relevan dikecualikan dari konteks build.
.git
.env
.env.*
!.env.example
backup*.sql
node_modules
vendor
README-private.md
File: .gitignore
File rahasia .env dan cadangan SQL tidak boleh di-commit ke GitHub.
.env
.env.local
backup*.sql
.DS_Store
vendor/
node_modules/
File: .env.example
Salin ke .env untuk laptop sendiri, lalu ganti kedua password. Jangan menyalin kata sandi contoh ke produksi.
APP_NAME=KosKu Tasks
DB_NAME=kosku
DB_USER=kosku_user
DB_PASSWORD=GANTI_DENGAN_PASSWORD_KUAT
DB_ROOT_PASSWORD=GANTI_DENGAN_PASSWORD_ROOT_BERBEDA
File: compose.yaml
File Compose lokal memetakan port 127.0.0.1:8081 ke container PHP port 80. MariaDB tidak membuka port host. Named volume membuat data tetap ada saat container dibuat ulang.
services:
web:
build:
context: .
dockerfile: Dockerfile
ports:
- "127.0.0.1:8081:80"
environment:
APP_NAME: ${APP_NAME:-KosKu Tasks}
DB_HOST: db
DB_PORT: 3306
DB_NAME: ${DB_NAME:-kosku}
DB_USER: ${DB_USER:-kosku_user}
DB_PASSWORD: ${DB_PASSWORD:?Isi DB_PASSWORD di .env}
depends_on:
db:
condition: service_healthy
restart: unless-stopped
db:
image: mariadb:11.4
environment:
MARIADB_DATABASE: ${DB_NAME:-kosku}
MARIADB_USER: ${DB_USER:-kosku_user}
MARIADB_PASSWORD: ${DB_PASSWORD:?Isi DB_PASSWORD di .env}
MARIADB_ROOT_PASSWORD: ${DB_ROOT_PASSWORD:?Isi DB_ROOT_PASSWORD di .env}
volumes:
- mariadb_data:/var/lib/mysql
- ./database/init.sql:/docker-entrypoint-initdb.d/01-init.sql:ro
healthcheck:
test: ["CMD", "healthcheck.sh", "--connect", "--innodb_initialized"]
interval: 10s
timeout: 5s
retries: 15
restart: unless-stopped
volumes:
mariadb_data:
File: compose.coolify.yaml
Compose Coolify tidak memakai ports: publik; domain dan proxy dipilih melalui dashboard. Database tetap privat di jaringan Compose. Kedua password wajib diisi melalui Environment Variables.
services:
web:
build:
context: .
dockerfile: Dockerfile
environment:
APP_NAME: ${APP_NAME:-KosKu Tasks}
DB_HOST: db
DB_PORT: 3306
DB_NAME: ${DB_NAME:-kosku}
DB_USER: ${DB_USER:-kosku_user}
DB_PASSWORD: ${DB_PASSWORD:?DB_PASSWORD wajib}
depends_on:
db:
condition: service_healthy
restart: unless-stopped
db:
image: mariadb:11.4
environment:
MARIADB_DATABASE: ${DB_NAME:-kosku}
MARIADB_USER: ${DB_USER:-kosku_user}
MARIADB_PASSWORD: ${DB_PASSWORD:?DB_PASSWORD wajib}
MARIADB_ROOT_PASSWORD: ${DB_ROOT_PASSWORD:?DB_ROOT_PASSWORD wajib}
volumes:
- mariadb_data:/var/lib/mysql
- ./database/init.sql:/docker-entrypoint-initdb.d/01-init.sql:ro
healthcheck:
test: ["CMD", "healthcheck.sh", "--connect", "--innodb_initialized"]
interval: 10s
timeout: 5s
retries: 15
restart: unless-stopped
volumes:
mariadb_data:
15. Jalankan CRUD lokal dan uji data persisten
Sebelum deploy ke VPS, jalankan seluruh stack di komputer agar kesalahan konfigurasi cepat ditemukan. Docker Desktop diperlukan di Windows/macOS, sedangkan Linux dapat menggunakan Docker Engine dan Compose plugin.
15.1 Siapkan environment lokal
Dari folder kosku-crud, buat .env menggunakan template dan ganti kata sandinya dengan nilai kuat yang berbeda. Jangan commit file tersebut.
cp .env.example .env
# Edit .env memakai VS Code dan ganti seluruh password contoh
docker compose -f compose.yaml config --quiet
docker compose -f compose.yaml up -d --build
docker compose -f compose.yaml ps
15.2 Uji di browser dan terminal
Kunjungi http://localhost:8081. Tambahkan tugas “Review Wireframe”, tandai selesai, tambahkan “Testing Aplikasi”, lalu hapus satu tugas. Periksa kesehatan layanan dan log bila ada masalah.
curl -i http://localhost:8081/health.php
docker compose -f compose.yaml logs --tail=100 web
docker compose -f compose.yaml logs --tail=100 db
Hasil yang diharapkan dari health check adalah HTTP 200 dan respons JSON {"status":"ok","service":"kosku-web"}. Data tugas berasal dari MariaDB, bukan array statis.
15.3 Buktikan data tetap ada setelah container direstart
Tambahkan tugas melalui browser, hentikan stack, lalu jalankan ulang tanpa menghapus volume. Buka kembali aplikasi dan periksa tugas sebelumnya masih ada.
docker compose -f compose.yaml down
docker compose -f compose.yaml up -d
# Buka lagi http://localhost:8081
docker compose down -v menghapus named volume dan dapat menghilangkan seluruh data latihan. Jangan gunakan opsi -v untuk memeriksa persistence.15.4 Jalankan CRUD dari GitHub ke Coolify
Setelah sukses lokal, lakukan git init dan commit. Buat repository kosong lewat halaman GitHub, salin URL repository milik Anda, lalu ubah placeholder URL di bawah sebelum mengeksekusi push.
git init
git add .
git commit -m "feat: PHP CRUD dengan MariaDB"
git branch -M main
git remote add origin https://github.com/AKUN_ANDA/kosku-crud.git
git push -u origin main
Di Coolify pilih Build strategy Docker Compose dan Compose Location /compose.coolify.yaml. Pastikan variabel MariaDB lengkap, lalu deploy. Gunakan domain HTTPS yang menunjuk ke service web port 80. Tambahkan tugas, lakukan commit perubahan CSS, tunggu redeploy, dan verifikasi data tugas masih ada.
16. Backup dan restore database: wajib dipraktikkan
Karena contoh MariaDB berada dalam stack Compose aplikasi, jangan mengasumsikan halaman backup database mandiri di Coolify otomatis mengelola database ini. Untuk database yang membutuhkan backup terjadwal dari dashboard, gunakan resource database MariaDB terpisah, atur kredensial dan jaringan, kemudian aktifkan menu Backups. Alternatif di bawah memperlihatkan dump dan restore secara manual pada komputer latihan.
16.1 Backup SQL dari Compose lokal
Perintah mengekspor database melalui program mariadb-dump di dalam container. Simpan dump ke luar server produksi serta batasi hak akses berkasnya.
docker compose -f compose.yaml exec -T db sh -c 'mariadb-dump -u"$MARIADB_USER" -p"$MARIADB_PASSWORD" "$MARIADB_DATABASE"' > backup-kosku.sql
ls -lh backup-kosku.sql
16.2 Uji restore pada lingkungan latihan, bukan langsung produksi
Untuk latihan, buat salinan database terpisah atau gunakan lingkungan uji. Jika memang ingin mengembalikan dump ke database saat ini, pahami bahwa tabel/record bisa tertimpa dan lakukan hanya setelah memiliki backup terbaru serta memastikan kompatibilitas skema.
cat backup-kosku.sql | docker compose -f compose.yaml exec -T db sh -c 'mariadb -u"$MARIADB_USER" -p"$MARIADB_PASSWORD" "$MARIADB_DATABASE"'
16.3 Otomasi backup melalui database resource di Coolify
Untuk sistem produksi, pertimbangkan resource MariaDB terpisah yang dapat dikelola lewat Databases → Backups → Add. Tentukan jadwal (contoh harian), retensi, dan tujuan S3-compatible di luar VPS. Jalankan Back up now, lalu unduh dan uji pemulihan pada database uji. Backup sukses tanpa restore test belum cukup untuk memastikan pemulihan.

17. CI/CD, rollback, dan verifikasi perubahan
Deployment otomatis dan pengujian otomatis bukan hal yang sama. Webhook Coolify memulai proses build setelah push; kode yang salah secara logika bisa saja lolos build. Karena itu tambahkan minimal linting atau unit test sebelum perubahan digabungkan ke branch production.
17.1 Contoh GitHub Actions lint sederhana
Buat file .github/workflows/php-check.yml pada repository. Workflow ini memeriksa sintaks dua file PHP setiap push dan pull request. Sesuaikan daftar file untuk proyek lainnya.
name: Check PHP Syntax
on:
push:
pull_request:
jobs:
php-lint:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- name: Setup PHP
uses: shivammathur/setup-php@v2
with:
php-version: '8.3'
- name: PHP syntax checks
run: |
find public src -name '*.php' -print0 | xargs -0 -n1 php -l
main dan wajibkan status check sebelum merge pull request. Jika langsung push ke main tanpa branch protection, auto-deploy dapat berjalan sebelum GitHub Actions selesai.17.2 Memicu deployment otomatis dan membaca log
Lakukan perubahan kecil yang terlihat (misalnya teks judul atau CSS), kemudian commit dan push. Pada aplikasi Coolify buka Deployment Logs dan cocokkan commit yang diterima.
git add public/style.css
git commit -m "style: perbarui tampilan KosKu"
git push origin main
17.3 Rollback versi aplikasi di Coolify
Buka aplikasi → Rollback, pilih image/commit terakhir yang sehat jika masih tersedia, lalu jalankan Roll back to this image. Periksa kembali URL publik, endpoint kesehatan, dan fungsi CRUD. Pilihan rollback mungkin tidak tersedia untuk seluruh jalur Compose/image retention; siapkan fallback deploy ulang dari commit Git yang diketahui baik.
18. Laravel dengan Docker dan Coolify: jalur penerapan nyata
Untuk Laravel, struktur aplikasinya lebih kompleks daripada PHP native karena membutuhkan Composer, document root public/, APP_KEY, migration, storage, queue, dan scheduler. Tutorial ini memberi konfigurasi dasar Laravel untuk memulai; sebelum produksi, tambah autentikasi, queue dan scheduler sesuai fitur aplikasi Anda.
18.1 Membuat proyek Laravel secara lokal
Dengan Composer dan PHP versi kompatibel, buat aplikasi Laravel baru. Versi Laravel/PHP yang dipilih harus mengikuti panduan resmi yang berlaku di waktu implementasi. Gunakan instalasi Laravel resmi dan kunci dependensi di composer.lock.
composer create-project laravel/laravel laravel-kosku
cd laravel-kosku
cp .env.example .env
php artisan key:generate
php artisan about
18.2 Contoh Dockerfile Laravel dengan Apache
Buat Dockerfile di root aplikasi. Contoh ini menyediakan PHP 8.3 dan extension umum, menginstall dependensi PHP melalui Composer stage, mengarahkan Apache DocumentRoot ke public/, dan menjalankan aplikasi sebagai image web. Untuk library lain, sesuaikan ekstensi dan versi PHP yang dibutuhkan.
FROM composer:2 AS vendor
WORKDIR /app
COPY composer.json composer.lock ./
RUN composer install --no-dev --no-interaction --prefer-dist --no-scripts --no-progress --optimize-autoloader
FROM php:8.3-apache
RUN apt-get update && apt-get install -y --no-install-recommends \
libonig-dev libzip-dev unzip curl \
&& docker-php-ext-install pdo_mysql mbstring bcmath zip \
&& a2enmod rewrite \
&& rm -rf /var/lib/apt/lists/*
ENV APACHE_DOCUMENT_ROOT=/var/www/html/public
RUN sed -ri -e 's!/var/www/html!${APACHE_DOCUMENT_ROOT}!g' /etc/apache2/sites-available/*.conf \
/etc/apache2/apache2.conf /etc/apache2/conf-available/*.conf
WORKDIR /var/www/html
COPY . .
COPY --from=vendor /app/vendor /var/www/html/vendor
RUN mkdir -p storage/framework/{cache,sessions,views} bootstrap/cache \
&& chown -R www-data:www-data storage bootstrap/cache
EXPOSE 80
HEALTHCHECK --interval=30s --timeout=5s --retries=3 CMD curl -fsS http://127.0.0.1/up || exit 1
18.3 Contoh Compose Laravel + MariaDB
Gunakan jalur Compose berbasis Git dengan dua service. Database tetap privat; APP_KEY, DB_PASSWORD, dan DB_ROOT_PASSWORD diatur di Coolify. Untuk produksi, APP_KEY dibuat satu kali dan tidak diubah tiap deploy. Hindari menjalankan key:generate setiap kontainer dibuat ulang.
services:
app:
build: .
environment:
APP_ENV: production
APP_DEBUG: "false"
APP_KEY: ${APP_KEY:?Wajib isi APP_KEY}
APP_URL: ${APP_URL:?Wajib isi APP_URL}
DB_CONNECTION: mysql
DB_HOST: db
DB_PORT: 3306
DB_DATABASE: ${DB_NAME:-laravel_kosku}
DB_USERNAME: ${DB_USER:-laravel_user}
DB_PASSWORD: ${DB_PASSWORD:?Wajib isi DB_PASSWORD}
depends_on:
db:
condition: service_healthy
restart: unless-stopped
db:
image: mariadb:11.4
environment:
MARIADB_DATABASE: ${DB_NAME:-laravel_kosku}
MARIADB_USER: ${DB_USER:-laravel_user}
MARIADB_PASSWORD: ${DB_PASSWORD:?Wajib isi DB_PASSWORD}
MARIADB_ROOT_PASSWORD: ${DB_ROOT_PASSWORD:?Wajib isi DB_ROOT_PASSWORD}
volumes:
- laravel_db:/var/lib/mysql
healthcheck:
test: ["CMD", "healthcheck.sh", "--connect", "--innodb_initialized"]
interval: 10s
timeout: 5s
retries: 15
restart: unless-stopped
volumes:
laravel_db:
18.4 Migrations, cache, storage, queue, dan scheduler
Setelah aplikasi dan database sehat, gunakan terminal aplikasi dalam Coolify untuk menjalankan migration terkontrol. Pada produksi, backup dulu, baca migration, dan jalankan php artisan migrate --force sebagai prosedur rilis terencana. File unggahan/storage pengguna harus memakai persistent volume atau object storage; jangan hanya menyimpannya di writable layer container.
php artisan migrate --force
php artisan optimize
php artisan route:list
php artisan queue:work --sleep=3 --tries=3
php artisan schedule:run
Baris queue:work dan schedule:run di atas adalah contoh perintah, bukan instruksi menjalankan keduanya sekaligus di terminal web. Untuk produksi, buat service worker tersendiri dengan proses jangka panjang dan penjadwal/cron terpisah, memakai image aplikasi yang sama serta environment dan koneksi database yang sesuai. Gunakan Redis bila aplikasi memang memerlukan cache/queue Redis.
19. Mengelola banyak proyek di satu VPS seperti PaaS
Satu instalasi Coolify dapat mengelola beberapa Application dan Service dengan domain berbeda. Agar stabil, setiap proyek sebaiknya memiliki repository, variabel, database, volume, dan alokasi resource masing-masing. Nama Project/Environment hanya membantu organisasi konfigurasi; bukan isolasi keamanan tingkat mesin virtual.
| Project | Contoh domain | Stack | Data |
|---|---|---|---|
| KosKu PHP | kosku.example.com | PHP + MariaDB | Volume mariadb_data khusus |
| Portal Laravel | portal.example.com | Laravel + DB + worker | Database dan storage sendiri |
| Dashboard React | admin.example.com | Frontend image/static | Tidak perlu DB bila hanya UI |
| Automation | flow.example.com | Tool berbasis Docker | Secrets dan volume tersendiri |
19.1 Template checklist membuat aplikasi baru
Setiap aplikasi baru mengikuti proses yang sama: buat repository, tentukan Dockerfile/Compose, set environment production, atur domain dan HTTPS, aktifkan health check, uji push otomatis, lakukan backup, tetapkan resource limit, dan dokumentasikan prosedur recovery.
19.2 Monitoring dan pemeliharaan
Gunakan Resource Metrics, Runtime Logs, Deployment Logs, health check, serta alat monitoring server. Tetapkan alarm disk, CPU, RAM, dan kapasitas volume. Update Coolify/OS secara bertahap, periksa perubahan release notes, serta uji backup sebelum pembaruan besar.
20. Troubleshooting berdasarkan gejala
Pemeriksaan efektif dimulai dari identifikasi lapisan yang gagal: GitHub webhook, build Docker, container aplikasi, koneksi database, proxy, DNS, atau HTTPS. Baca error pada lapisan tersebut sebelum mengubah konfigurasi secara acak.
| Gejala | Penyebab umum | Langkah diagnosis |
|---|---|---|
| Push tidak memicu deploy | Branch salah, Auto deploy mati, GitHub App/webhook bermasalah | Periksa Advanced → Deployment, commit branch, GitHub App dan Deployment Logs |
| Deploy gagal saat build | Dockerfile path salah, ekstensi/dependensi belum tersedia | Baca perintah pertama yang gagal pada build log dan cocokkan repo |
| Halaman 502/404 | Service atau internal port salah, container tidak sehat | Cek Domains service web port 80, Healthcheck dan Runtime Logs |
| PHP 503 / DB refused | Kredensial salah, MariaDB belum sehat, network salah | Cek DB_HOST=db, variabel, status db, dan log koneksi |
| Tugas hilang setelah redeploy | Volume dibuat baru atau pernah dihapus | Verifikasi mounted volume, nama project dan restore cadangan |
| HTTPS gagal | DNS salah, port 80/443 tertutup atau konfigurasi proxy | Cek A record, firewall, status proxy, sertifikat dan log ACME |
| Laravel 500 | APP_KEY, permission, migration, cache/config | Cek APP_DEBUG=false, log Laravel dan log container |
21. FAQ: keputusan teknis yang sering membingungkan
Pertanyaan berikut merangkum poin yang perlu dipahami sebelum menjadikan VPS sebagai platform deployment mandiri.
Apakah semua proyek harus memakai Docker Compose?
Tidak. Satu aplikasi PHP sederhana cukup Dockerfile. Compose berguna ketika ada beberapa layanan seperti database, worker, atau Redis.
Apakah GitHub Actions wajib?
Tidak untuk auto-deploy dasar. Coolify bisa menerima webhook langsung. Actions berguna sebagai quality gate atau pipeline CI tambahan.
Apakah Coolify menggantikan DevOps?
Tidak sepenuhnya. Coolify mengotomasi banyak operasi deploy, tetapi keamanan VPS, backup, kapasitas, observability, dan recovery tetap tanggung jawab operator.
Apakah port MariaDB perlu dibuka?
Tidak. Biarkan MariaDB dapat diakses hanya dari network Docker internal. Gunakan akses terkontrol jika administrasi eksternal diperlukan.
Apakah rollback mengembalikan database?
Tidak. Rollback image/kode dan restore database adalah dua prosedur berbeda.
Kenapa memakai dua file Compose?
Lokal membutuhkan port loopback untuk uji browser. Coolify memakai proxy dan domain, sehingga aplikasi tidak perlu mempublikasikan port host.
Bagaimana kalau satu VPS kehabisan RAM?
Pantau konsumsi setiap layanan, batasi resource, optimalkan image, dan pindahkan workload/database ke server lain ketika kapasitas tidak memadai.
22. Dokumentasi dan sumber resmi
Panduan mengikuti dokumentasi resmi Coolify, Docker, GitHub, PHP, MariaDB, dan Laravel. Menu pada dashboard bisa berbeda sesuai versi yang Anda gunakan; gunakan dokumentasi berikut sebagai rujukan terakhir ketika antarmuka berubah.