Referensi cepat container registry. Autentikasi, strategi tagging, vulnerability scanning, mirroring, dan cleanup di Docker Hub, GHCR, Harbor, dan ECR.
Registry adalah tempat menyimpan dan mendistribusikan image. Empat pilihan ini menempati posisi yang tidak sama persis:
| Registry | Host | Autentikasi | Kuota pull anonim | Unik utama |
|---|---|---|---|---|
| Docker Hub | docker.io | akun atau PAT | ada, terbatas per 6 jam | katalog publik terbesar |
| GHCR | ghcr.io | PAT classic atau fine-grained | public image tanpa limit ketat | integrasi GitHub Actions |
| Harbor | domain sendiri | user, robot account | tergantung setup | self-hosted, RBAC, replication |
| ECR | <account>.dkr.ecr.<region>.amazonaws.com | IAM, token 12 jam | tidak ada (perlu auth) | VPC, lifecycle policy, scan |
Pilihan kasar: Docker Hub untuk image publik dan open source. GHCR kalau pipeline-mu hidup di GitHub Actions. ECR kalau infrastruktur di AWS. Harbor kalau kamu butuh kontrol penuh dan kepatuhan (compliance) data di server sendiri.
Sebelum bicara registry, kamu harus lancar membaca referensi image:
ghcr.io/galpratama/myapp:v1.4.2
\_____/ \_________/ \___/ \_____/
registry namespace/repo tagAturan penting:
ubuntu:24.04), Docker menganggap docker.io/library/ubuntu:24.04docker pull myapp sama dengan myapp:latest, tag default yang selalu adalatest menunjuk image A, besok image Bmyapp@sha256:abc123... selalu menunjuk layer yang sama selamanyaKarena tag bisa berpindah, deployment produksi sebaiknya memakai digest. Tag untuk manusia membaca, digest untuk mesin memastikan.
docker login # interaktif, pakai username dan password
docker login -u galpratama # minta password secara aman
echo $DOCKER_HUB_PAT | docker login -u galpratama --password-stdinSelalu pakai --password-stdin di CI. Password yang ditulis sebagai argumen command line masuk ke process list dan shell history, dan itu celah yang gampang bocor.
Docker Hub memakai Personal Access Token (bukan password akun) untuk CLI sejak 2024. Buat dari Account Settings, Docker PAT, Generate new token. Scopenya bertingkat: read, write, atau read/write/delete.
Login disimpan di ~/.docker/config.json. Perhatikan satu hal: secara default Docker menyimpan token base64 (bukan enkripsi). Di mesin bersama, aktifkan credential helper:
{
"credsStore": "pass",
"credHelpers": {
"public.ecr.aws": "ecr-login",
"<account>.dkr.ecr.us-east-1.amazonaws.com": "ecr-login"
}
}credHelpers memetakan registry tertentu ke helper khusus, misal amazon-ecr-credential-helper yang menyegarkan token ECR otomatis sebelum kedaluwarsa 12 jam.
GHCR memakai Personal Access Token GitHub dengan scope paket:
read:packages: pull image dan baca metadatawrite:packages: push dan pulldelete:packages: hapus versi imageecho $CR_PAT | docker login ghcr.io -u galpratama --password-stdin
docker tag myapp:1.4.2 ghcr.io/galpratama/myapp:1.4.2
docker push ghcr.io/galpratama/myapp:1.4.2Di GitHub Actions, jangan pakai PAT kalau bisa. GITHUB_TOKEN bawaan job cukup untuk push ke package milik repo itu:
permissions:
contents: read
packages: write
steps:
- uses: actions/checkout@v4
- name: Login ke GHCR
run: echo "${{ secrets.GITHUB_TOKEN }}" | docker login ghcr.io -u ${{ github.actor }} --password-stdin
- name: Build dan push
run: |
docker build -t ghcr.io/${{ github.repository }}:latest .
docker push ghcr.io/${{ github.repository }}:latestGITHUB_TOKEN hangus saat job selesai, jadi jauh lebih aman dari PAT yang umur panjang.
ECR tidak memakai password tetap. Kamu minta token dari IAM lewat AWS CLI, lalu token itu dipakai sebagai password docker login. Token berlaku 12 jam.
aws ecr get-login-password --region us-east-1 \
| docker login --username AWS --password-stdin \
111122223333.dkr.ecr.us-east-1.amazonaws.comUsername selalu string AWS. Perhatikan juga: registry ECR bersifat per region, jadi us-east-1 dan ap-southeast-1 adalah dua registry terpisah dengan login terpisah.
Di CI atau mesin dev, pasang amazon-ecr-credential-helper supaya tidak perlu login manual:
# macOS
brew install amazon-ecr-credential-helper
# lalu di ~/.docker/config.json
{
"credHelpers": {
"111122223333.dkr.ecr.us-east-1.amazonaws.com": "ecr-login"
}
}Push pertama ke ECR butuh repository yang sudah ada (atau set imageTagMutability dan scanOnPush lewat CLI):
aws ecr create-repository \
--repository-name myapp \
--image-scanning-configuration scanOnPush=true \
--region us-east-1Harbor install paling umum lewat Helm di Kubernetes. Dua fitur yang membedakan dari registry polos:
Login seperti registry biasa, pakai robot account sebagai username:
docker login harbor.example.com -u robot$myapp+ciPerhatikan format username robot: robot$<project>+<nama>. Tanda dolar dan plus memang bagian dari namanya.
Tiga gaya yang umum, dari yang paling berisiko ke paling aman:
| Strategi | Contoh | Masalah |
|---|---|---|
latest saja | myapp:latest | tidak tahu versi apa yang jalan, rollback mustahil |
| tag versi saja | myapp:1.4.2 | tidak ada alias yang menunjuk versi stabil |
| semver + sha + moving alias | myapp:1.4.2, myapp:sha-abc1234, myapp:1.4 | butuh disiplin push beberapa tag |
Praktik yang baik:
1.4.2), atau sha-<commit> untuk build dari branchlatest, 1.4, stable) dipakai untuk kenyamanan manusia, bukan untuk deploymentmyapp@sha256:...FROM node:22-alpine@sha256:... supaya build ulang tidak diam-diam pindah baseContoh pipeline build multi-tag:
VERSION=$(git describe --tags --always)
SHA=$(git rev-parse --short=7 HEAD)
docker tag myapp harbor.example.com/team/myapp:${VERSION}
docker tag myapp harbor.example.com/team/myapp:sha-${SHA}
docker tag myapp harbor.example.com/team/myapp:latest
docker push harbor.example.com/team/myapp:${VERSION}
docker push harbor.example.com/team/myapp:sha-${SHA}
docker push harbor.example.com/team/myapp:latestImage menggabungkan base OS dan puluhan dependensi. Scan membandingkan isi layer dengan database CVE.
# Trivy: scanner standalone, paling umum dipakai di CI
trivy image myapp:1.4.2
trivy image --severity HIGH,CRITICAL --exit-code 1 myapp:1.4.2 # gagal build kalau ada temuan
trivy image --ignore-unfixed myapp:1.4.2 # abaikan CVE tanpa patch
# Docker Scout
docker scout cves myapp:1.4.2
# Grype
grype myapp:1.4.2Di GitHub Actions:
- name: Scan image
run: |
trivy image --severity CRITICAL --exit-code 1 \
ghcr.io/${{ github.repository }}:sha-${{ github.sha }}Per registry:
scanOnPush=true, hasil dilihat di console atau aws ecr describe-image-scan-findings. Ada juga enhanced scanning berbayar yang pakai Amazon Inspector.Aturan praktis scan: jalankan di CI pada setiap push, gagal build hanya untuk CRITICAL yang punya fix (pakai --ignore-unfixed), dan jadwalkan scan ulang berkala karena database CVE bertambah tiap hari. Image yang aman kemarin bisa punya temuan baru hari ini tanpa kodenya berubah.
Tim kamu mungkin campur: server amd64, laptop M-series arm64. Satu tag yang sama bisa melayani keduanya lewat manifest list (image index di istilah OCI). Docker pick arsitektur yang cocok saat pull.
# sekali setup per mesin
docker buildx create --name builder --use
# build dua arsitektur sekaligus, push langsung
docker buildx build \
--platform linux/amd64,linux/arm64 \
-t ghcr.io/galpratama/myapp:1.4.2 \
--push .
# cek arsitektur apa saja yang tersedia dalam satu tag
docker buildx imagetools inspect ghcr.io/galpratama/myapp:1.4.2Contoh nyata: build Raspberry Pi (arm64) gagal karena tim hanya build amd64. Dengan manifest list, tag 1.4.2 punya dua manifest di dalamnya dan tiap perangkat menerima versi yang benar tanpa tag tambahan seperti -arm64.
Untuk image yang sudah jadi, docker manifest bisa menggabungkan tanpa rebuild:
docker manifest create myapp:1.4.2 \
myapp:1.4.2-amd64 myapp:1.4.2-arm64
docker manifest push myapp:1.4.2Dua pola berbeda yang sering tertukar:
docker.io lewat mirror lokal. Tarik pertama mengambil dari upstream, tarik berikutnya lokal. Menghemat kuota Docker Hub dan mempercepat build.Mirror lokal dengan registry pull-through (misal pull-through cache bawaan Distribution):
docker run -d --name registry-mirror -p 5000:5000 \
-e REGISTRY_PROXY_REMOTEURL=https://registry-1.docker.io \
registry:2Lalu arahkan Docker daemon ke mirror, edit /etc/docker/daemon.json:
{
"registry-mirrors": ["http://your-mirror.example.com:5000"]
}Restart daemon (systemctl restart docker) dan mirror aktif. Docker tetap mencoba upstream kalau mirror gagal, jadi ini tidak membuat single point of failure.
Untuk kepatuhan atau ketersediaan lintas region, Harbor replication menyalin repo dari registry sumber (Docker Hub, GHCR, ECR, atau Harbor lain) ke Harbor lokal berdasarkan filter nama, tag, dan label.
Registry tanpa kebijakan retensi tumbuh tanpa batas. Setiap build baru menambah layer, dan tanpa cleanup tag lama biaya simpan naik terus.
Docker Hub dan GHCR: hapus manual dari web UI, atau pakai API. Untuk GHCR ada juga gh api untuk menghapus versi package:
# list versi package lama lalu hapus (GHCR, butuh delete:packages)
gh api user/packages/container/myapp/versions \
--paginate --jq '.[].id' | tail -n +20 | \
xargs -I{} gh api -X DELETE user/packages/container/myapp/versions/{}ECR punya lifecycle policy yang jauh lebih rapi, langsung di registry:
aws ecr put-lifecycle-policy --repository-name myapp --region us-east-1 --cli-input-json '{
"rules": [
{
"ruleId": "keep-last-30",
"description": "Simpan 30 image berdasarkan tag",
"selection": {
"tagStatus": "tagged",
"tagPrefixList": ["v"],
"countType": "imageCountMoreThan",
"countNumber": 30
},
"action": { "type": "expire" }
},
{
"ruleId": "drop-untagged-7d",
"description": "Hapus untagged lebih dari 7 hari",
"selection": {
"tagStatus": "untagged",
"countType": "sinceImagePushed",
"countNumber": 7,
"countUnit": "days"
},
"action": { "type": "expire" }
}
]
}'Harbor memakai retention rule per project lewat UI: keep last N by count, atau by days, dengan filter tag atau label. Ada juga garbage collection terjadwal untuk benar-benar menghapus layer yang tidak lagi direferensikan.
Prinsip yang sama di mana pun: simpan sedikit versi terbaru, hapus untagged agresif (itu biasanya sisa build yang gagal), dan jalankan garbage collection di jam sepi karena prosesnya bisa lama.
toomanyrequests, inilah penyebabnya. Solusinya: login sebelum build, atau pakai mirror.--password-stdin atau credential helper, tidak pernah di command linelatestteam/myapp.1.4.2 atau latest.Login atau daftar akun gratis untuk membaca cheat sheet ini.