← Kembali ke Blog

GitHub Actions CI/CD: Build dan Push Docker Image dengan Layer Caching

Setiap kali kamu push kode, CI pipeline rebuild Docker image dari awal. Install dependency jalan lagi, build tools download lagi, dan apa yang biasanya makan 3 menit di lokal jadi 8 atau 10 menit di CI. Solusinya adalah layer caching: reuse layer yang mahal dari build sebelumnya dan cuma rebuild yang berubah.

Tutorial ini jelasin workflow GitHub Actions lengkap yang build Docker image, cache layer antar run, dan push ke Docker Hub atau GitHub Container Registry. Kamu dapat YAML yang bisa langsung copy paste, plus panduan strategi caching mana yang cocok buat tim kamu.

Prerequisites

  • Repository GitHub yang sudah punya Dockerfile
  • Akun Docker Hub (kalau push ke Docker Hub) atau repo GitHub (buat GHCR, yang nggak butuh akun tambahan)
  • Paham dasar syntax YAML GitHub Actions

Pastikan Dockerfile kamu pakai multi-stage kalau ada build steps. Dockerfile single-stage di mana build tools hidup bareng runtime buang-buang cache karena setiap perubahan rebuild semuanya.

Workflow-nya

Buat file .github/workflows/docker-publish.yml di repo kamu:

name: Build and Push Docker Image

on:
  push:
    branches: [main]
  pull_request:
    branches: [main]

env:
  REGISTRY: ghcr.io
  IMAGE_NAME: ${{ github.repository }}

jobs:
  build-and-push:
    runs-on: ubuntu-latest
    permissions:
      contents: read
      packages: write

    steps:
      - name: Checkout
        uses: actions/checkout@v4

      - name: Set up Docker Buildx
        uses: docker/setup-buildx-action@v3

      - name: Log in to GitHub Container Registry
        uses: docker/login-action@v3
        with:
          registry: ghcr.io
          username: ${{ github.actor }}
          password: ${{ secrets.GITHUB_TOKEN }}

      - name: Extract metadata
        id: meta
        uses: docker/metadata-action@v5
        with:
          images: ${{ env.REGISTRY }}/${{ env.IMAGE_NAME }}
          tags: |
            type=ref,event=branch
            type=sha,prefix=

      - name: Build and push
        uses: docker/build-push-action@v6
        with:
          context: .
          push: ${{ github.event_name != 'pull_request' }}
          tags: ${{ steps.meta.outputs.tags }}
          labels: ${{ steps.meta.outputs.labels }}
          cache-from: type=gha
          cache-to: type=gha,mode=max

Satu file ini handle PR (build tanpa push), push ke main (build dan push), dan cache layer antar run. Baris cache-from dan cache-to yang bikin workflow ini cepat.

Strategi Cache

Docker Buildx support dua cache backend utama buat GitHub Actions:

GitHub Actions Cache (type=gha)

Cache ini simpan layer di Actions cache built-in GitHub. Langsung jalan tanpa setup tambahan. Scope cache per-repository default, jadi repo beda nggak share layer.

cache-from: type=gha
cache-to: type=gha,mode=max

Bagian mode=max itu penting. Tanpa ini, cuma layer image final yang di-cache. Dengan mode=max, semua intermediate build stages juga di-cache. Buat Dockerfile multi-stage, ini bedanya antara rebuild Go build yang makan 5 menit dan skip total.

Limit: GitHub Actions cache punya limit 10GB total per repository. Kalau sudah lewat, entry lama di-evict. Untuk mayoritas tim ini cukup. Kalau image kamu besar atau punya banyak branch, pertimbangkan registry cache.

Registry Cache (type=registry)

Cache ini push layer ke container registry sebagai image terpisah. Bisa pakai registry mana aja (Docker Hub, GHCR, ECR) dan nggak ada limit yang nempel ke GitHub. Kekurangannya agak lebih lambat di awal karena layer cache lewat network.

cache-from: type=registry,ref=${{ env.REGISTRY }}/${{ env.IMAGE_NAME }}:buildcache
cache-to: type=registry,ref=${{ env.REGISTRY }}/${{ env.IMAGE_NAME }}:buildcache,mode=max

Pakai ini kalau repo kamu rutin lewat limit cache 10GB, atau kalau butuh share cache antar beberapa repositori.

Pilih yang Mana

Faktor type=gha type=registry
Setup Nggak perlu setup Butuh permission push ke registry
Kecepatan Cepat (cache lokal di runner) Agak lebih lambat (fetch lewat network)
Limit size 10GB per repo Unlimited
Share antar repo Nggak bisa Bisa

Mulai dari type=gha. Pindah ke registry cache kalau sudah kena limit size atau butuh share antar repo.

Push ke Docker Hub

Kalau tim kamu pakai Docker Hub, ganti login step dan hapus env variable registry:

env:
  IMAGE_NAME: username-dockerhub-kamu/nama-repo

jobs:
  build-and-push:
    runs-on: ubuntu-latest
    steps:
      - name: Checkout
        uses: actions/checkout@v4

      - name: Set up Docker Buildx
        uses: docker/setup-buildx-action@v3

      - name: Log in to Docker Hub
        uses: docker/login-action@v3
        with:
          username: ${{ secrets.DOCKER_USERNAME }}
          password: ${{ secrets.DOCKER_PASSWORD }}

      - name: Extract metadata
        id: meta
        uses: docker/metadata-action@v5
        with:
          images: ${{ env.IMAGE_NAME }}
          tags: |
            type=ref,event=branch
            type=sha,prefix=

      - name: Build and push
        uses: docker/build-push-action@v6
        with:
          context: .
          push: true
          tags: ${{ steps.meta.outputs.tags }}
          labels: ${{ steps.meta.outputs.labels }}
          cache-from: type=gha
          cache-to: type=gha,mode=max

Simpan username dan password Docker Hub sebagai repository secrets dengan nama DOCKER_USERNAME dan DOCKER_PASSWORD. Jangan hardcode di workflow file.

Strategi Tagging

metadata-action otomatis generate tag yang masuk akal. Ini pola yang umum dipakai:

tags: |
  # nama branch (main, develop, feature/xyz)
  type=ref,event=branch
  # commit SHA (abc1234)
  type=sha,prefix=
  # semantic version pas push tag (v1.2.3 -> 1.2.3, 1.2, 1)
  type=semver,pattern={{version}}
  type=semver,pattern={{major}}.{{minor}}
  type=semver,pattern={{major}}

Kalau kamu push tag v1.2.3, action bikin tiga image tag: 1.2.3, 1.2, dan 1. Ini konvensi standar Docker buat image versioned.

Verifikasi Build

Setelah push pertama, cek tab Actions di repo kamu. Workflow run harusnya selesai dalam 30 sampai 60 detik di run berikutnya (dibanding 3 sampai 5 menit di run pertama).

Buat verifikasi image sudah ke-push:

# Untuk GHCR
docker pull ghcr.io/username-kamu/nama-repo:main

# Untuk Docker Hub
docker pull username-kamu/nama-repo:main

# Jalankan
docker run -p 8080:8080 ghcr.io/username-kamu/nama-repo:main

Masalah yang Sering Muncul

Cache nggak jalan. Pastikan kamu pakai docker/setup-buildx-action. Builder Docker default di GitHub Actions runners nggak support Buildx cache backends. Tanpa Buildx, field cache-from dan cache-to di-ignore tanpa pesan error.

PR build push image. Baris push: ${{ github.event_name != 'pull_request' }} cegah push di PR. Kalau mau push image PR ke tag terpisah buat testing, tambah entry type=ref,event=pr di tags.

Cache ke-evict karena image besar. Kalau Dockerfile kamu pull base image 2GB dan pakai mode=max, kamu cache banyak. Pertimbangkan pakai mode=min (cuma cache layer image final) atau pindah ke type=registry buat storage unlimited.

Error permission di GHCR push. Workflow butuh permission packages:write. Kalau ada branch protection rule atau org policy yang restrict permission GITHUB_TOKEN, kamu mungkin perlu adjust setting-nya.

Referensi

Butuh Bantuan Implementasi?

Saya membantu tim mendesain dan membangun infrastruktur cloud scalable, pipeline DevOps, dan sistem production-grade.

Konsultasi Gratis