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.