CI/CD Pipeline untuk CodeIgniter 4: Dari git push ke Coolify Deploy

Saya ingin berbagi pengalaman setup CI/CD pipeline untuk project CodeIgniter 4 yang saya kerjakan. Project ini menggunakan GitLab sebagai repository, GitLab Container Registry untuk menyimpan Docker image, dan Coolify sebagai platform deployment.

Tantangannya: bagaimana membuat workflow yang otomatis — setiap kali push ke branch master, pipeline jalan, build Docker image, push ke registry, lalu trigger deploy ke Coolify?

Berikut langkah-langkahnya.

Arsitektur Pipeline

Pipeline kami terdiri dari 2 stage sederhana:

Build → Docker build & push ke GitLab Container Registry Deploy → Trigger Coolify via webhook

git push → master
  └─ build (docker build + push image)
      └─ deploy (curl ke Coolify)

Sederhana, tapi ada beberapa hal yang perlu diperhatikan.

1. GitLab CI Pipeline

File .gitlab-ci.yml di root project:

stages:
  - build
  - deploy

variables:
  IMAGE_TAG: $CI_REGISTRY_IMAGE:$CI_COMMIT_SHORT_SHA
  IMAGE_LATEST: $CI_REGISTRY_IMAGE:latest

build:
  stage: build
  image: docker:latest
  services:
    - docker:dind
  before_script:
    - docker login -u $CI_REGISTRY_USER -p $CI_REGISTRY_PASSWORD $CI_REGISTRY
  script:
    - docker build -t $IMAGE_TAG -t $IMAGE_LATEST .
    - docker push $IMAGE_TAG
    - docker push $IMAGE_LATEST
  only:
    - master

deploy_to_coolify:
  stage: deploy
  image: curlimages/curl:latest
  needs: [build]
  script:
    - echo "Memicu deployment ke Coolify..."
    - >
      curl -L -X POST
      -H "CF-Access-Client-Id: $COOLIFY_CF_CLIENT_ID"
      -H "CF-Access-Client-Secret: $COOLIFY_CF_CLIENT_SECRET"
      -H "Authorization: Bearer $COOLIFY_API_TOKEN"
      "$COOLIFY_DEPLOY_URL"
  only:
    - master

Penjelasan:

  • Build stage menggunakan Docker-in-Docker (dind). GitLab secara built-in menyediakan $CI_REGISTRY_USER dan $CI_REGISTRY_PASSWORD untuk login ke Container Registry-nya sendiri — tidak perlu bikin token manual.
  • Image di-push dengan 2 tag: $CI_COMMIT_SHORT_SHA (unique per commit) dan latest (selalu yang terbaru).
  • Deploy stage menggunakan curlimages/curl — image kecil (~5MB) yang isinya cuma curl. Lebih efisien daripada pakai alpine penuh.
  • Header Cloudflare Access (CF-Access-Client-*) diperlukan karena server Coolify di belakang Cloudflare Tunnel.

CI Variables yang perlu diset di GitLab → Settings → CI/CD → Variables:

VariableDeskripsi
COOLIFY_CF_CLIENT_IDCloudflare Access Client ID
COOLIFY_CF_CLIENT_SECRETCloudflare Access Client Secret
COOLIFY_API_TOKENCoolify API token
COOLIFY_DEPLOY_URLEndpoint deploy dari Coolify

2. Production Docker Compose

Untuk development lokal kami pakai docker-compose.yml biasa yang build dari source. Tapi untuk production di Coolify, kami buat file terpisah: docker-compose.prod.yml.

Perbedaan utamanya:

  • Menggunakan pre-built image dari registry, bukan build langsung.
  • Healthcheck — Coolify bisa monitor apakah container benar-benar siap.
  • Semua env variable via ${VAR:-default} — nilainya diisi dari Coolify UI.
services:
  cldu-app:
    image: registry.gitlab.com/username/nama-project:latest
    pull_policy: always
    container_name: cldu_app
    ports:
      - "${APP_FORWARD_PORT:-8070}:80"
    environment:
      APP_BASEURL: "${APP_BASEURL}"
      CI_ENVIRONMENT: "${CI_ENVIRONMENT}"
      DB_HOST: "${DB_HOST}"
      DB_DATABASE: "${DB_DATABASE}"
      DB_USERNAME: "${DB_USERNAME}"
      DB_PASSWORD: "${DB_PASSWORD}"
    volumes:
      - cldu_writable:/var/www/writable
    depends_on:
      - cldu-db
    healthcheck:
      test: ["CMD", "curl", "-f", "http://localhost:80/robots.txt"]
      interval: 30s
      timeout: 10s
      retries: 3
      start_period: 40s

Di Coolify, kita arahkan deployment ke file docker-compose.prod.yml ini.

3. Masalah: Pull Access Denied

Pas pertama kali deploy, muncul error:

Error: pull access denied for registry.gitlab.com/username/nama-project,
repository does not exist or may require 'docker login'

Coolify tidak punya akses untuk pull image dari GitLab Container Registry karena registry-nya private. Wajar — kita perlu memberikan credentials.

Awalnya saya coba setting COOLIFY_CONTAINER_REGISTRY_USERNAME dan COOLIFY_CONTAINER_REGISTRY_PASSWORD di environment variable Coolify, tapi ternyata tidak bekerja. Variabel-variabel itu mungkin untuk versi Coolify tertentu atau cara yang berbeda.

Saya juga coba docker login di terminal server, tapi akan kerepotan jika ada banyak project dengan registry berbeda. Karena tiap registry akan ada username dan token masing-masing dari gitlab.

4. Solusi: DOCKER_AUTH_CONFIG + GitLab Deploy Token

Setelah searching, akhirnya nemu referensi tentang DOCKER_AUTH_CONFIG — environment variable standar yang digunakan Docker Engine untuk menyimpan credentials registry. Biasanya ini dibuat otomatis saat docker login dan disimpan di ~/.docker/config.json. Ternyata environment variable ini bisa di-inject langsung.

Ini masuk akal: di belakang layar, docker login nulis file config.json, Coolify bisa membaca varian env-nya langsung tanpa perlu file. Solusi yang akhirnya bekerja adalah DOCKER_AUTH_CONFIG. Ini adalah konfigurasi standar Docker untuk login ke registry — jadi pasti didukung oleh environment manapun, termasuk Coolify.

Langkah 1: Buat GitLab Deploy Token

Buka project di GitLab → Settings → Repository → Deploy Tokens.

Buat token baru dengan scope read_registry:

Name: coolify-deploy-token
Scope: ✅ read_registry

GitLab akan generate username (misal: gitlab+deploy-token-123456) dan password. Simpan password-nya, karena tidak bisa dilihat lagi setelah halaman di-refresh.

Langkah 2: Generate Base64

Deploy token adalah username:password biasa. Kita perlu meng-encode-nya ke base64:

echo -n "gitlab+deploy-token-123456:token-secret-xxx" | base64

Output: Z2l0bGFiK2RlcGxveS10b2tlbi14eHhhOnRva2VuLXNlY3JldC14eHg=

Langkah 3: Set DOCKER_AUTH_CONFIG di Coolify

Di Coolify, buka Resource → Envs, tambahkan variable baru:

DOCKER_AUTH_CONFIG = {"auths":{"registry.gitlab.com":{"auth":"Z2l0bGFiK2RlcGxveS10b2tlbi14eHhhOnRva2VuLXNlY3JldC14eHg="}}}

Setelah itu, redeploy. Sekarang Coolify bisa login ke GitLab Container Registry dan pull image dengan sukses.

Deploy Flow End-to-End

Setelah semuanya beres, beginilah workflow lengkapnya:

1. Developer: git push ke master
       ↓
2. GitLab CI Runner mendeteksi perubahan
       ↓
3. Build stage:
   - docker build -t image:abc1234 -t image:latest .
   - docker push image:abc1234
   - docker push image:latest
       ↓
4. Deploy stage:
   - curl -X POST -H "Authorization: Bearer coapi_xxx" \
     "https://coolify.domain/api/v1/deploy?uuid=xxx"
       ↓
5. Coolify menerima webhook
       ↓
6. Coolify login ke registry (via DOCKER_AUTH_CONFIG)
       ↓
7. docker compose -f docker-compose.prod.yml up -d
       ↓
8. Healthcheck: cek /robots.txt di localhost
       ↓
9. App live! 🚀

Total waktu dari git push sampai app hidup biasanya sekitar 1-2 menit, tergantung ukuran image dan kecepatan build.

Troubleshooting

Beberapa error yang sempat saya alami dan solusinya:

Pull Access Denied

Error: pull access denied for registry.gitlab.com/...

Solusi: Set DOCKER_AUTH_CONFIG dengan base64 dari GitLab Deploy Token (scope read_registry).

Pipeline gagal — Docker not found

Error: docker: not found atau Cannot connect to the Docker daemon

Solusi: Pastikan GitLab Runner menggunakan executor docker dengan privileged = true. Ini diperlukan untuk menjalankan Docker-in-Docker.

Image tidak ditemukan

Error: manifest for registry.gitlab.com/... not found

Solusi: Pipeline build belum pernah jalan. Push ke master atau trigger manual pipeline dulu.

Penutup

Dengan pipeline ini, deploy jadi semudah git push. Tidak perlu SSH ke server, tidak perlu build manual, tidak perlu copy file. Semua otomatis.

Yang saya pelajari: masalah yang paling tricky bukan di GitLab CI-nya, tapi di autentikasi antara Coolify dan registry. Solusi DOCKER_AUTH_CONFIG adalah standar Docker yang bekerja di mana saja — tidak hanya Coolify, tapi juga di Kubernetes, Docker Swarm, atau VPS biasa.

Dashboard Grafik Pemantauan PLTS

Project personal yang saya inisiasi sejak awal tahun 2025 ini akhirnya mulai terlihat wujudnya. Yang awalnya saya mau belajar mikrokontroller Arduino kemudian ESP dan lanjut ke IoT dan finalnya di penerapan PLTS. Satu per satu mulai dirangkai dan sampai sekarang berhasil membuat dashboard pemantauan PLTS.

Bisa dilihat di grafik diatas berupa summary dari sistem PLTS yang saya kembangkan. Tampilannya masih belum terkonsep dengan matang, masih sesuai dengan ide yang terpikir sesaat. Walaupun demikian, sudah mulai nampak flow kerjanya. Tinggal sedikit-demi sedikit disempurnakan.

Project PLTS ini merupakan tahap lanjut saya dari belajar Arduino dan ESP. Dari awalnya hanya LED, lanjut ke robotika untuk RC Car, hingga ke IoT. Semua hal fisik elektronik ini saya arahkan datanya ke lokal server yang dibangun diatas Raspberry Pi dengan docker yang menangani kontainerisasi layanan.

Ada beberapa layanan yang berjalan diatas docker ini. Diantaranya adalah mosquitto untuk layanan mqtt, influxdb yang menangani basis data, Node-RED sebagai jembatannya, dan Grafana sebagai frontend yang menampilkan datanya. Semuanya bekerja bersama dengan sistem PLTS atap.

Sepertinya sampai akhir tahun ini secara garis besar projectnya sampai tahap ini, tinggal merapikan dan merancang untuk project tahun depan. Diantaranya yang terpikirkan adalah membuat modul hot-swap agar kita bisa pasang dan copot baterai pack untuk menambah kapasitas baterai PLTS. Dulu saya pikir mau tambah kapasitas tinggal paralel baterai pack yang sudah ada BMS nya. Ternyata tidak sesederhana itu.

21 Oct 2025

Struktur Direktori Ci4 Docker

Sekarang saya pakai docker dalam pengembangan web dengan framework Ci4. Sebelumnya saya sering pakai environment lengkap seperti xampp ataupun Laragon. Tetapi karena saya memiliki beberapa project legacy yang masih pakai dependency lama, seperti php 7 atau yang lain, sehingga kadang kesulitan menyesuaikan atau mengganti dependency jika pakai Xampp atau Laragon. Apalagi kalau ada project paralel yang harus pakai dua versi php yang berbeda.

Dengan pengembangan berbasis kontainer seperti Docker, kita bisa mengatur lingkungan yang berbeda setiap project. Lingkungan pengembangan satu project bisa kita buat independen terhadap project lain. Kita juga bisa jalankan dua project itu secara bersamaan tanpa mengganggu project yang lain.

Untuk kasus Ci4 dan bisa juga untuk framework lain sejenis, kita bisa pisahkan direktori untuk public dan private. Tujuannya diantaranya untuk keamanan dan konsistensi git workflow.

Misalhnya kita bisa buat struktur direktori seperti berikut:

project_directory
|- docker-compose.yml
|- Dockerfile
|- src
|-- private
|-- public

Kita bisa masukkan semua code ci4 di direktori private. Kemudian copy isi dari direktori public dalam ci4 ke src/public. Kemudian kita mapping src/public ke document root dalam kontainer. Kita map juga direktori private ke server.

Berikut docker compose nya:

name: 'ci4zuriah'

services:
  app:
    build:
      context: .
      dockerfile: Dockerfile
    container_name: zuriah_app
    ports:
      - "8080:80"
    volumes:
      - ./src/public:/var/www/html
      - ./src/private:/var/www/private
      - ./docker/php.ini:/usr/local/etc/php/php.ini
    depends_on:
      - db
    networks:
      - ci4net

  db:
    image: arm64v8/mysql:9.4.0-oracle
    container_name: zuriah_db
    restart: always
    environment:
      MYSQL_ROOT_PASSWORD: rootpass
      MYSQL_DATABASE: ci4db
      MYSQL_USER: ci4user
      MYSQL_PASSWORD: ci4pass
    ports:
      - "3356:3306"
    volumes:
      - dbdata:/var/lib/mysql
    networks:
      - ci4net

  phpmyadmin:
    image: arm64v8/phpmyadmin:5.2
    container_name: zuriah_myadmin
    restart: always
    ports:
      - "8051:80"
    environment:
      PMA_HOST: db
      MYSQL_ROOT_PASSWORD: rootpass
      UPLOAD_LIMIT: 10M
    depends_on:
      - db
    networks:
      - ci4net

volumes:
  dbdata:

networks:
  ci4net:

Dockerfile

FROM php:8.3-apache

# Install dependencies
RUN apt-get update && apt-get install -y \
    libicu-dev \
    libpq-dev \
    libpng-dev \
    libjpeg-dev \
    libfreetype6-dev \
    unzip \
    curl \
    zip 

# Install Composer
RUN curl -sS https://getcomposer.org/installer | php -- \
    && mv composer.phar /usr/local/bin/composer

# Aktifkan ekstensi PHP
RUN docker-php-ext-configure gd \
    --with-freetype \
    --with-jpeg \
    && docker-php-ext-install \
    gd \
    intl \
    mysqli \
    pdo \
    pdo_mysql

# Aktifkan mod_rewrite Apache
RUN a2enmod rewrite

# Restart Apache agar perubahan berlaku
RUN service apache2 restart

Kediri, 20 oct 2025

Dockerfile untuk Ci4 dengan Composer

Dengan case ingin menjalankan composer didalam container php dengan apache

FROM php:8.1-apache

# Install dependencies
RUN apt-get update && apt-get install -y \
    libicu-dev \
    libpq-dev \
    unzip \
    curl \
    zip \

# Install Composer
RUN curl -sS https://getcomposer.org/installer | php -- \
    && mv composer.phar /usr/local/bin/composer

# Aktifkan ekstensi PHP Intl 
RUN docker-php-ext-install intl mysqli pdo pdo_mysql

# Aktifkan mod_rewrite Apache
RUN a2enmod rewrite

# Restart Apache agar perubahan berlaku
RUN service apache2 restart

p9, 2 Oct 2025

Setup Docker Container untuk CodeIgniter 4, MySQL (Debian), dan phpMyAdmin

🧭 Awal Mula: Ide Membuat Kontainer Dev

Saya ingin membuat lingkungan pengembangan berbasis Docker untuk CodeIgniter 4, agar proses development lebih stabil, portable, dan mudah direplikasi. Untuk itu, saya butuh stack yang terdiri dari:

  • Apache + PHP untuk menjalankan CI4
  • MySQL sebagai database backend
  • phpMyAdmin sebagai UI administrasi database
  • Aktifnya mod_rewrite agar CI4 bisa routing tanpa index.php

🏗️ Merancang Dockerfile dan docker-compose.yml

Alih-alih menggunakan image php:8.2-apache secara langsung, saya memutuskan membuat Dockerfile kustom agar bisa mengaktifkan mod_rewrite dan menyusun konfigurasi Apache sesuai kebutuhan.

📄 Dockerfile untuk Apache + PHP

Dockerfile

# Menggunakan image dasar PHP dengan Apache
FROM php:8.2-apache

# Perbarui package manager dan install ekstensi yang diperlukan
RUN apt-get update && apt-get install -y \
    libicu-dev \
    libpq-dev \
    unzip

# Aktifkan ekstensi PHP Intl 
RUN docker-php-ext-install intl 

# Aktifkan mod_rewrite Apache
RUN a2enmod rewrite

# Restart Apache agar perubahan berlaku
RUN service apache2 restart

EXPOSE 80
CMD ["apache2-foreground"]

📄 apache-config.conf

apache

<VirtualHost *:80>
    DocumentRoot /var/www/html
    <Directory /var/www/html>
        AllowOverride All
        Require all granted
    </Directory>
</VirtualHost>

📦 docker-compose.yml

yaml

name: 'ci4app'

services:
  app:
    build:
      context: .
      dockerfile: Dockerfile
    container_name: ci4_app
    ports:
      - "8080:80"
    volumes:
      - ./ci4:/var/www/html
    depends_on:
      - db

  db:
    image: mysql:8.0-debian
    container_name: mysql_db
    environment:
      MYSQL_ROOT_PASSWORD: rootpass
      MYSQL_DATABASE: ci4db
      MYSQL_USER: ci4user
      MYSQL_PASSWORD: ci4pass
    volumes:
      - ./mysql:/var/lib/mysql

  phpmyadmin:
    image: phpmyadmin/phpmyadmin
    container_name: phpmyadmin
    ports:
      - "8081:80"
    environment:
      PMA_HOST: db
      PMA_USER: ci4user
      PMA_PASSWORD: ci4pass
    depends_on:
      - db

⚠️ Masalah yang Terjadi: MySQL Error x86-64-v2

Saat pertama kali saya jalankan stack ini dengan docker compose up --build, CI4 langsung muncul di port 8080, tapi phpMyAdmin gagal tersambung ke database.

Setelah saya cek log mysql_db, ternyata muncul error:

Fatal glibc error: CPU does not support x86-64-v2

Saya baru sadar kalau image mysql:8.0 berbasis Oracle Linux 9 yang butuh CPU dengan dukungan modern. Dan processor saya belum mendukung x86-64-v2 😅.

🔄 Solusi: Ganti dengan mysql:8.0-debian

Setelah mencari tahu, saya menemukan kalau image mysql:8.0-debian lebih ringan dan tidak butuh instruksi CPU canggih. Saya langsung ganti di docker-compose.yml:

yaml

image: mysql:8.0-debian

Lalu saya jalankan ulang:

bash

docker compose down
docker compose up --build -d

Dan… berhasil! 🎉 Semua kontainer hidup dengan baik, CI4 bisa routing dengan .htaccess, dan phpMyAdmin langsung tersambung ke database.

✅ Kesimpulan

  • Gunakan Dockerfile untuk kontrol penuh terhadap konfigurasi Apache dan PHP.
  • Aktifkan mod_rewrite dengan a2enmod rewrite agar CI4 tidak butuh index.php.
  • Gunakan mysql:8.0-debian jika CPU kamu tidak mendukung x86-64-v2.
  • Pantau log dan error agar lebih cepat mendeteksi masalah.