Episode ini membedah perbedaan mendasar artifacts vs cache, cara mengonfigurasi cache dengan key, paths, dan policy (pull-push, pull, push), membuat cache key dinamis berbasis lock file, lalu menghubungkan runner ke object storage untuk distributed caching.

Di episode 7 kita membahas artifacts — cara menyimpan hasil build dan mentransfernya antar job. Banyak yang mengira artifacts juga mempercepat pipeline, padahal ia bekerja untuk tujuan yang berbeda. Realitanya: pipeline kalian masih lambat setiap kali menjalankan npm install atau mvn package, karena job mengunduh dan menginstal ulang semua dependensi dari nol meskipun isinya tidak berubah. Satu perubahan kode kecil bisa berarti menit-menit terbuang hanya untuk menunggu node_modules dibangun lagi.
Episode ini membahas cache — mekanisme GitLab untuk menyimpan dependensi sementara sehingga job berikutnya tidak perlu membangun ulang dari awal. Kalian akan belajar membedakan kapan memakai artifacts dan kapan memakai cache, mengonfigurasi cache dengan benar, hingga menyiapkan distributed caching dengan object storage agar cache tetap berguna meski job kalian berjalan di runner yang berbeda-beda.
Kebingungan terbesar pemula GitLab CI adalah menganggap artifacts dan cache sama. Padahal keduanya punya tujuan yang bertolak belakang:
node_modules, ~/.m2, cache pip — yang selalu dibutuhkan job untuk bekerja cepat. Konsepnya seperti gudang bahan baku yang diisi sekali dan dipakai ulang oleh banyak proses.| Aspek | Artifacts | Cache |
|---|---|---|
| Isi | Output akhir (binary, zip, laporan) | Dependensi sementara (node_modules, .m2) |
| Tujuan | Dipakai job lain atau diunduh user | Mempercepat job berikutnya |
| Garansi | Selalu tersedia setelah job selesai | Best-effort, bisa hilang kapan saja |
| Transfer | Otomatis antar stage (default) | Hanya dipakai job yang mendeklarasikan cache yang sama |
| Sifat | Diunduh sekali, disimpan permanen (sesuai expire_in) | Zip arsip dependensi, ditimpa di tiap job |
Aturan memilihnya sederhana: kalian butuh hasilnya untuk diproses atau diunduh → artifacts. Kalian butuh mempercepat instalasi dependensi → cache. Keduanya sering dipakai bersama — misalnya job build men-cache node_modules (agar npm ci cepat) sekaligus menghasilkan dist/ sebagai artifacts.
build_app:
stage: build
image: node:20-alpine
cache:
key: npm-cache
paths:
- node_modules/
script:
- npm ci
- npm run build
artifacts:
paths:
- dist/Di sini key: npm-cache memberi nama unik pada arsip cache. Semua job dengan key yang sama berbagi satu arsip cache. paths menentukan direktori mana yang dicache — dalam hal ini node_modules/. Ketika build_app selesai, node_modules/ di-upload ke cache; ketika job lain dengan key npm-cache dijalankan, isinya diunduh kembali sebelum script dieksekusi.
Tip
Untuk npm, selalu pasangkan cache dengan npm ci — bukan npm install. npm ci menghapus node_modules yang ada lalu menginstal sesuai lockfile, jadi ia mengikuti isi cache dengan benar, menghasilkan build yang reproducible. Cache ini bekerja paling baik bila ia isi lockfile yang sama; itu sebabnya cache key dinamis sangat penting — lihat bagian berikutnya.
Masalah dengan key statis (npm-cache): kalau package-lock.json berubah, cache lama yang berisi dependensi usang tetap dipakai — instalasi bisa gagal karena versi paket tidak cocok. Solusinya adalah cache key dinamis yang otomatis berubah saat dependensi berubah:
build_app:
stage: build
image: node:20-alpine
cache:
key:
files:
- package-lock.json
paths:
- node_modules/
script:
- npm ci
- npm run buildDengan key: files: [package-lock.json], GitLab menghitung hash dari isi lock file dan menjadikannya key cache. Selama package-lock.json tidak berubah, hash tetap sama dan cache dipakai — begitu lock file berubah (misalnya ada dependensi baru), hash berubah dan GitLab otomatis membuat cache baru. Dengan begini, kecocokan cache dijamin tanpa perlu manual bump versi key.
Untuk project Java/Maven, polanya identik — hanya lock file-nya yang berbeda:
test_maven:
stage: test
image: maven:3.9-eclipse-temurin-21
variables:
MAVEN_OPTS: "-Dmaven.repo.local=$CI_PROJECT_DIR/.m2/repository"
cache:
key:
files:
- pom.xml
paths:
- .m2/repository/
script:
- mvn verifyPerhatikan MAVEN_OPTS yang memindahkan repository lokal Maven ke dalam workspace project ($CI_PROJECT_DIR/.m2/repository) — penting agar paths: .m2/repository/ bisa menangkapnya, karena ~/.m2 default berada di luar workspace yang bisa di-cache. Ini salah satu jebakan klasik caching Maven yang sering membuat cache tidak pernah terpakai.
Keyword policy: mengontrol kapan cache diunduh dan di-upload. Tiga nilainya:
pull-push (default) — cache diunduh di awal job dan di-upload kembali di akhir. Paling umum dipakai.pull — hanya mengunduh, tidak meng-upload. Cocok untuk job yang memakai cache tapi tidak ingin menimpa isinya (misalnya job test paralel yang hanya membaca).push — hanya meng-upload, tidak mengunduh. Cocok untuk job yang membangun cache baru setelah dependensi berubah, sementara job lainnya cukup pull.test_app:
stage: test
image: node:20-alpine
cache:
key:
files:
- package-lock.json
paths:
- node_modules/
policy: pull
script:
- npm testKombinasi klasik: satu job install dengan policy: push yang memperbarui cache ketika lock file berubah, dan banyak job test dengan policy: pull yang hanya memakai cache — sehingga tidak terjadi balapan menimpa cache antar job paralel.
Default-nya, cache disimpan lokal di mesin runner. Masalahnya muncul begitu kalian punya lebih dari satu runner: cache yang dibuat runner A tidak tersedia di runner B. Untuk tim yang pakai banyak runner (apalagi auto-scaling), solusinya adalah distributed cache — menyimpan cache di object storage bersama (S3, MinIO, GCS) yang diakses semua runner.
Konfigurasinya dilakukan di file config.toml mesin runner:
[[runners]]
name = "docker-runner"
executor = "docker"
[runners.cache]
Type = "s3"
Path = "gitlab-cache"
Shared = true
[runners.cache.s3]
ServerAddress = "s3.amazonaws.com"
BucketName = "my-ci-cache"
AccessKey = "AKIAXXXX"
SecretKey = "S3CRET"Dengan Shared = true, semua runner yang terhubung ke bucket yang sama saling berbagi cache. ServerAddress bisa diganti ke endpoint MinIO internal (http://minio:9000) bila kalian self-hosted — pola umum di perusahaan yang tidak ingin data cache keluar ke cloud publik. Setelah ini, key yang sama akan selalu menghasilkan cache hit apa pun runner yang mengeksekusi job — pipeline paralel lintas runner jadi konsisten dan cepat.
Warning
Ingat: cache adalah best-effort. GitLab tidak menjamin cache selalu tersedia — bisa dihapus, tidak ter-build karena gagal, atau kadaluarsa. Pipeline kalian harus tetap berjalan meski cache kosong. Jangan pernah menaruh dependensi yang tidak bisa diunduh ulang di dalam cache, dan jangan menyimpan secret di dalamnya — cache bisa saja dilihat oleh job lain yang berbagi key.
Pada episode 8 ini kalian telah memahami cache secara menyeluruh:
cache: key:, paths: mendefinisikan cache; key: files: membuat key dinamis dari lock file (package-lock.json, pom.xml) sehingga cache otomatis segar saat dependensi berubah.policy: pull-push / pull / push mengontrol kapan cache diunduh dan di-upload, mencegah balapan antar job paralel.Dengan cache yang tepat, npm ci dan mvn verify yang tadinya memakan menit bisa turun jadi hitungan detik. Tapi ada satu lagi faktor yang memperlambat pipeline tanpa kalian sadari: struktur stage yang linear. Di episode 9 kita akan membahas Directed Acyclic Graph (DAG) pipelines dengan needs: — bagaimana membuat job berjalan begitu dependensinya selesai, tanpa menunggu seluruh stage rampung, dan menghemat hingga 50 persen lebih waktu pipeline. Sampai jumpa di episode 9!