GHCR(GitHub Container Registry)이란 — Docker Hub와 차이, 사용법, 비용
CI 로그나 docker-compose.yml에서 ghcr.io/...로 시작하는 이미지 이름을 점점 자주 보게 됩니다. GHCR(GitHub Container Registry)은 GitHub이 운영하는 컨테이너 이미지 레지스트리입니다. 코드가 GitHub에 있다면 이미지도 같은 곳에 두는 흐름이 표준처럼 자리잡았고, 그 중심에 있는 서비스입니다.
이 글은 GHCR을 처음 만나는 분을 기준으로 네 가지 질문에 답합니다.
- GHCR이 정확히 무엇인가
- Docker Hub와 무엇이 다른가
- 비용은 얼마인가
- 어떻게 쓰는가 (push/pull, GitHub Actions)
push/pull 명령 자체가 처음이라면 도커 기초 #5 레지스트리를 먼저 읽는 걸 추천합니다. 이 글은 그중 GHCR을 정면으로 파고듭니다.
GHCR이란 #
GHCR은 GitHub Packages라는 패키지 호스팅 서비스의 컨테이너 부문입니다. 주소는 ghcr.io이고, OCI(Open Container Initiative) 표준을 따르는 레지스트리라서 도커 입장에선 Docker Hub와 똑같이 동작합니다. docker login, docker push, docker pull 흐름이 그대로이고, podman이나 Helm 차트(OCI 아티팩트) 같은 다른 도구도 쓸 수 있습니다.
이미지 이름은 이 형태입니다.
ghcr.io/OWNER/IMAGE_NAME:TAG
ghcr.io/curtis/hello-docker:1.0
───── ──────────── ───
계정/조직 이미지 이름 태그OWNER는 GitHub 계정 이름 또는 조직 이름이고, 전부 소문자여야 합니다. GitHub 계정이 MyName이라면 이미지 경로는 ghcr.io/myname/...입니다.
한 가지 개념만 잡고 가면 나머지가 쉬워집니다. GHCR에 올라간 이미지는 GitHub에서 패키지(package) 라는 독립된 단위로 관리됩니다. 코드 저장소에 종속된 게 아니라 계정/조직 아래에 존재하고, 필요하면 특정 저장소와 연결할 수 있습니다. 공개 범위(public/private)도 저장소가 아니라 패키지 단위로 정합니다.
Docker Hub와 무엇이 다른가 #
도커를 배울 때 처음 만나는 레지스트리는 보통 Docker Hub입니다. 그런데 실무에서 자기 서비스의 이미지를 둘 곳을 고를 때는 GHCR이 먼저 검토되는 경우가 많습니다. 차이를 표로 정리하면 이렇습니다.
| Docker Hub | GHCR | |
|---|---|---|
| 운영 주체 | Docker | GitHub |
공식 이미지(python, nginx 등) | 있음 | 없음 |
| pull 횟수 제한 | 미인증 100회/6시간(IP 기준), 무료 계정 200회/6시간 | 공개된 횟수 제한 없음 |
| 권한 모델 | Docker Hub 별도 계정과 조직 | GitHub 계정, 조직, 팀 권한 그대로 |
| private 저장소 | 무료 플랜은 1개, 그 이상은 유료 플랜 | 개수 제한 없음, 현재 무료 (아래 비용 절 참고) |
| GitHub Actions 연동 | 별도 시크릿 등록 필요 | GITHUB_TOKEN으로 즉시 인증 |
실무 감각으로 요약하면 이렇게 됩니다.
베이스 이미지는 Docker Hub에서 옵니다. python:3.14-slim, nginx:1.27 같은 공식 이미지는 Docker Hub에만 있습니다. GHCR을 주 레지스트리로 써도 Dockerfile의 FROM은 Docker Hub를 바라보는 경우가 대부분입니다.
pull 제한은 CI에서 체감됩니다. Docker Hub는 미인증 상태에서 IP당 6시간에 100회로 pull을 제한합니다. 문제는 CI 환경(GitHub Actions, GitLab CI 등)이 IP를 다른 사용자들과 공유한다는 점입니다. 내가 몇 번 안 받아도 같은 IP의 다른 빌드가 한도를 소진해서 toomanyrequests 에러를 만날 수 있습니다. GHCR은 공식 문서에 공개된 pull 횟수 제한이 없어서, 자기 서비스 이미지를 GHCR에 두면 이 문제에서 벗어납니다.
권한 관리가 하나로 합쳐집니다. Docker Hub를 쓰면 팀원 계정과 권한을 GitHub과 Docker Hub 두 군데서 관리해야 합니다. GHCR은 GitHub 조직과 팀 권한을 그대로 쓰기 때문에, 저장소 접근 권한이 있는 사람에게 이미지 접근 권한을 주는 일이 설정 한 번으로 끝납니다.
비용 — 지금은 무료 #
GHCR 검색에서 많이 나오는 질문이라 따로 정리합니다. 결론부터 말하면, 컨테이너 레지스트리의 스토리지와 대역폭은 현재 무료입니다. public 이미지든 private 이미지든 마찬가지입니다. GitHub 공식 문서가 “currently free"라고 명시하고 있고, 과금을 시작하게 되면 최소 1개월 전에 고지하겠다고 밝혀 두었습니다.
과금이 시작될 경우의 기준선도 문서에 있습니다. GitHub Packages의 일반 요금 체계는 private 패키지에 대해 플랜별 무료 쿼터를 두는 방식입니다.
| 플랜 | 스토리지 | 월 전송량(다운로드) |
|---|---|---|
| Free | 500MB | 1GB |
| Pro | 2GB | 10GB |
| Team | 2GB | 10GB |
| Enterprise Cloud | 50GB | 100GB |
두 가지는 과금 대상에서 빠집니다. public 패키지는 무조건 무료이고, GitHub Actions 안에서 GITHUB_TOKEN으로 받는 전송량도 과금되지 않습니다. 즉 빌드와 배포가 GitHub Actions 안에서 도는 구조라면, 과금이 시작되더라도 부담이 크지 않게 설계되어 있습니다.
숫자는 2026년 7월 문서 기준입니다. 요금 정책은 바뀔 수 있으니 결정 전에 GitHub Packages billing 문서를 한 번 확인하세요.
사용법 — 토큰 발급부터 pull까지 #
1. PAT(classic) 만들기 #
GHCR 인증에는 GitHub 비밀번호가 아니라 PAT(Personal Access Token)를 씁니다. 주의할 점 하나. GitHub에는 신형 fine-grained PAT와 구형 classic PAT가 있는데, 레지스트리 인증에는 아직 classic PAT만 쓸 수 있습니다.
GitHub → Settings → Developer settings → Personal access tokens → Tokens (classic) 에서 토큰을 만들고 권한을 체크합니다.
- push까지 한다면
write:packages - pull만 한다면
read:packages - 패키지 삭제도 한다면
delete:packages
2. 로그인 #
export CR_PAT=ghp_xxxxxxxxxxxxxxxxxx
echo $CR_PAT | docker login ghcr.io -u curtis --password-stdin
# Login Succeeded토큰을 --password-stdin으로 넘기면 셸 히스토리에 남지 않습니다.
3. 태그를 붙이고 push #
이미지 이름에 ghcr.io/계정명이 들어가야 도커가 목적지를 압니다.
docker tag hello-docker ghcr.io/curtis/hello-docker:1.0
docker push ghcr.io/curtis/hello-docker:1.04. 공개 범위 바꾸기 #
처음 push한 패키지는 기본이 private입니다. 공개하려면 GitHub의 해당 패키지 페이지 → Package settings → Change package visibility에서 public으로 바꿉니다. public이 되면 누구나 로그인 없이 pull 할 수 있습니다.
docker pull ghcr.io/curtis/hello-docker:1.05. 저장소와 연결하기 #
Dockerfile에 라벨 한 줄을 넣으면 이미지가 코드 저장소와 연결됩니다.
LABEL org.opencontainers.image.source=https://github.com/curtis/hello-docker연결하면 저장소 페이지 사이드바에 패키지가 노출되고, 저장소의 README가 패키지 페이지에 표시됩니다. 저장소 권한을 패키지에 상속시키는 기준점이 되기도 하니, 실무에서는 넣는 걸 기본으로 두면 됩니다.
GitHub Actions에서 — PAT 없이 #
GHCR을 고르는 가장 큰 이유가 이 부분입니다. GitHub Actions 안에서는 워크플로마다 자동 발급되는 GITHUB_TOKEN이 있어서, PAT를 만들고 시크릿에 등록하는 절차가 통째로 사라집니다.
permissions:
contents: read
packages: write
steps:
- uses: docker/login-action@v3
with:
registry: ghcr.io
username: ${{ github.actor }}
password: ${{ secrets.GITHUB_TOKEN }}
- uses: docker/build-push-action@v6
with:
push: true
tags: ghcr.io/${{ github.repository_owner }}/myapp:${{ github.sha }}permissions에 packages: write를 명시하는 것만 잊지 않으면 됩니다. 태그를 커밋 SHA로 찍는 이유와 :latest만 쓰면 안 되는 이유는 도커 실전 #5 태그 전략에서 자세히 다뤘습니다.
운영 팁 두 가지 #
오래된 버전 정리. CI가 커밋마다 이미지를 push하면 untagged 버전이 계속 쌓입니다. 지금은 무료라 방치해도 비용은 안 나가지만, 패키지 페이지가 지저분해지고 과금 전환 시 부담이 됩니다. actions/delete-package-versions 액션으로 오래된 버전을 주기적으로 지우는 워크플로를 하나 걸어두면 깔끔합니다.
다이제스트 고정. 태그는 같은 이름으로 다시 push 될 수 있습니다. 재현이 중요한 배포라면 ghcr.io/curtis/myapp@sha256:... 형태의 다이제스트 참조를 쓰세요. 이건 GHCR만의 이야기가 아니라 모든 레지스트리 공통입니다.
정리 — 어떤 레지스트리를 고를까 #
- 코드가 GitHub에 있고 GitHub Actions로 빌드한다 — GHCR이 기본값입니다. 인증, 권한, 비용 모두에서 마찰이 가장 적습니다.
- 공식 베이스 이미지 — Docker Hub에서 받습니다. 선택의 문제가 아니라 그곳에만 있습니다.
- AWS에 배포한다(ECS, EKS) — 같은 네트워크 안에 있는 ECR도 함께 검토할 만합니다. 전송 지연과 비용에서 유리한 경우가 있습니다.
GHCR은 “GitHub을 쓰고 있다면 고민할 이유가 별로 없는” 레지스트리입니다. 무료이고, pull 제한이 없고, 권한 관리가 GitHub에 통합되어 있습니다. 레지스트리 개념 자체가 낯설다면 도커 기초 #5부터, CI 파이프라인 전체가 궁금하다면 도커 실전 시리즈로 이어가세요.