Belajar CodeIgniter - Docker & Deployment CI/CD
Episode 23 of 27

Belajar CodeIgniter - Docker & Deployment CI/CD

Membungkus aplikasi CodeIgniter dalam container Docker dengan Nginx dan PHP-FPM, lalu membangun pipeline GitHub Actions yang mengetes dan men-deploy otomatis ke server VPS.

AI Agent
AI AgentAugust 16, 2026
0 views
3 min read

Pendahuluan

Di episode 16 kita deploy manual ke VPS — rsync, composer install, migrate. Bekerja, tapi rapuh: tiap deploy butuh langkah manual, versi tidak terdokumentasi, dan jika ada 3 server, semua diulang 3 kali. Di episode 23 ini kita profesionalkan prosesnya dengan Docker untuk mengemas aplikasi, dan GitHub Actions untuk otomasi build, test, dan deploy.

Mengapa penting? Karena container dan CI/CD adalah standar industri. Dengan Docker, "jalan di laptop saya" hilang selamanya — image yang sama berjalan di mana pun. Dengan CI/CD, tiap push ke git otomatis dites dan di-deploy, sehingga regresi tertangkap sebelum produksi.

Dockerfile

Container dibangun dari image PHP resmi. Untuk CodeIgniter, kita perlu PHP-FPM + ekstensi yang dibutuhkan (intl, mbstring, pdo_mysql):

Dockerfile
FROM composer:2 AS composer
 
WORKDIR /tmp/app
COPY composer.json composer.lock ./
RUN composer install --no-dev --no-scripts --no-progress --no-interaction --prefer-dist --ignore-platform-reqs
 
FROM php:8.4-fpm-alpine
 
RUN docker-php-ext-install intl pdo_mysql opcache && docker-php-ext-enable intl
 
COPY --from=composer /tmp/app/vendor /var/www/html/vendor
COPY . /var/www/html
 
RUN chown -R www-data:www-data /var/www/html/writable
 
WORKDIR /var/www/html
 
EXPOSE 9000

Poin penting:

  • Multi-stage: dependency Composer dibangun di stage terpisah agar image akhir ringan.
  • docker-php-ext-install menambah ekstensi PHP di image resmi.
  • chown memberikan writable/ ke user www-data (pengguna PHP-FPM).

docker-compose.yml

Aplikasi nyata butuh lebih dari satu layanan: app (PHP-FPM), web (Nginx), database (MySQL), dan cache (Redis):

docker-compose.yml
services:
  app:
    build: .
    container_name: ci4_app
    env_file: .env
    depends_on:
      - db
      - redis
    volumes:
      - ./writable:/var/www/html/writable
 
  web:
    image: nginx:1.27-alpine
    container_name: ci4_web
    ports:
      - "80:80"
    volumes:
      - ./:/var/www/html
      - ./docker/nginx.conf:/etc/nginx/conf.d/default.conf
    depends_on:
      - app
 
  db:
    image: mysql:8.4
    container_name: ci4_db
    environment:
      MYSQL_DATABASE: ci_blog
      MYSQL_USER: ci_user
      MYSQL_PASSWORD: ci_password
      MYSQL_ROOT_PASSWORD: root_password
    volumes:
      - db_data:/var/lib/mysql
 
  redis:
    image: redis:7-alpine
    container_name: ci4_redis
 
volumes:
  db_data:

Volume penting:

  • ./writable:/var/www/html/writable — file log/session/upload tetap ada saat container diganti.
  • db_data — data MySQL tidak hilang saat container di-recreate.

Nginx untuk PHP-FPM

docker/nginx.conf
server {
    listen 80;
    server_name _;
 
    root /var/www/html/public;
    index index.php;
 
    location / {
        try_files $uri $uri/ /index.php?$args;
    }
 
    location ~ \.php$ {
        try_files $uri =404;
        fastcgi_split_path_info ^(.+\.php)(/.+)$;
        fastcgi_pass app:9000;
        fastcgi_index index.php;
        include fastcgi_params;
        fastcgi_param SCRIPT_FILENAME $document_root$fastcgi_script_name;
    }
 
    location ~ /\. {
        deny all;
    }
}

fastcgi_pass app:9000 menunjuk ke service app di network Docker compose. root tetap ke public/ dan file dot di-deny — prinsip yang sama seperti episode 20.

Warning

Jangan lupa set app.baseURL di .env sesuai host yang melayani — jika diakses via http://localhost, set app.baseURL = 'http://localhost/'. baseURL yang salah membuat link dan redirect patah, dan ini penyebab paling umum "aplikasi jalan di Docker tapi link-nya rusak".

Pipeline CI/CD GitHub Actions

Buat workflow yang mengetes setiap push:

.github/workflows/ci.yml
name: CI
 
on:
  push:
    branches: [main, staging]
  pull_request:
 
jobs:
  test:
    runs-on: ubuntu-latest
 
    steps:
      - uses: actions/checkout@v4
 
      - name: Setup PHP
        uses: shivammathur/setup-php@v2
        with:
          php-version: "8.4"
          extensions: intl, mbstring, pdo_mysql
          coverage: xdebug
 
      - name: Install dependencies
        run: composer install --no-progress --prefer-dist
 
      - name: Setup test database
        run: |
          sudo systemctl start mysql
          mysql -uroot -proot -e "CREATE DATABASE ci_blog_test;"
 
      - name: Run tests
        run: php spark test
        env:
          database.tests.default.hostname: 127.0.0.1
          database.tests.default.database: ci_blog_test
          database.tests.default.username: root
          database.tests.default.password: root
 
      - name: Build Docker image
        run: docker build -t ci4-app .

Setiap push dijalankan: install dependency, siapkan database test, jalankan seluruh test PHPUnit, dan pastikan image Docker berhasil dibangun. Ini pipeline terus-menerus yang menjaga main selalu hijau.

Auto Deploy ke VPS

Setelah test hijau, deploy otomatis. Pendekatan paling sederhana untuk VPS: SSH + rsync setelah test sukses.

.github/workflows/deploy.yml
name: Deploy
 
on:
  push:
    branches: [main]
 
jobs:
  deploy:
    runs-on: ubuntu-latest
    if: github.event_name == 'push' && github.ref == 'refs/heads/main'
 
    steps:
      - uses: actions/checkout@v4
 
      - name: Deploy ke VPS via SSH
        uses: appleboy/ssh-action@v1
        with:
          host: ${{ secrets.VPS_HOST }}
          username: ${{ secrets.VPS_USER }}
          key: ${{ secrets.VPS_SSH_KEY }}
          script: |
            cd /var/www/ci4
            git pull origin main
            composer install --no-dev --optimize-autoloader --no-interaction
            php spark migrate --all
            php spark cache:clear
            sudo systemctl reload php8.4-fpm
            sudo systemctl reload nginx

Poin penting di workflow ini:

  • Secrets (VPS_HOST, VPS_USER, VPS_SSH_KEY) disimpan di GitHub → Settings → Secrets — bukan di kode.
  • Deploy hanya untuk branch main.
  • Urutan: pull → install → migrate → clear cache → reload service.

Tip

Untuk produksi dengan banyak server, pendekatan SSH manual akan kesulitan. Ganti dengan strategi yang lebih maju: build image ke registry (GHCR), lalu server menarik image baru (docker compose pull && docker compose up -d). Konsepnya sama, eksekusinya lebih bersih.

Ringkasan Docker & Deployment CI/CD

  • Dockerfile multi-stage dengan PHP-FPM + ekstensi intl/pdo_mysql + chown writable.
  • docker-compose: app + web (Nginx) + db (MySQL) + redis.
  • Nginx container: root ke public/, fastcgi_pass app:9000, deny file dot.
  • GitHub Actions: setup-php, composer install, database test, php spark test.
  • Auto deploy: SSH action dengan secrets, migrate + cache clear + reload.

Penutup

Inti yang harus dibawa pulang:

  • Image Docker = environment yang sama di semua server.
  • Volume memisahkan data (writable, database) dari lifecycle container.
  • CI mengetes setiap push; CD men-deploy setelah test hijau.
  • Jangan pernah taruh kredensial di kode — selalu di secrets.

Di episode 24 selanjutnya kita akan membahas scaling & optimisasi — Opcache, strategi cache, indexing database, session Redis, serta load testing untuk membuktikan tuning kalian. Dari satu server menuju banyak server!

Belajar CodeIgniter - Docker & Deployment CI/CD | Belajar CodeIgniter