CodeBuildのDocker Hub 429エラーをECR Public Galleryで回避してみた
CodeBuildでdocker build実行時にDocker Hubの429エラー(Too Many Requests)が発生する問題を、DockerfileのARG機能とECR Public Galleryを組み合わせて解決しました。
Docker Hub認証情報が利用できる場合はECR Pull Through Cacheが正攻法ですが、認証情報の管理を最小化したかったため、今回はビルド引数でローカル開発とCIのレジストリを切り替える方式を採用しました。
すぐに試せるCloudFormationテンプレートも掲載しています。
発生した問題
CodePipelineのBuildステージで、docker build実行時に以下のエラーが発生しビルドが失敗しました。
ERROR: failed to resolve source metadata for docker.io/library/node:24-alpine:
429 Too Many Requests
toomanyrequests: You have reached your unauthenticated pull rate limit.
Docker Hubは匿名ユーザーに対して 6時間あたり100回 のpull制限を設けています。CodeBuildはAWSが管理する共有IPアドレスからDocker Hubにアクセスするため、他のユーザーとレートリミットを共有してしまい、制限に達しやすい状況に陥ることがあります。
解決策
DockerfileのARG機能を使い、--build-argでレジストリのプレフィックスを切り替えて、ECR Public Galleryを利用します。
Dockerfile
ARG REGISTRY=""
FROM ${REGISTRY}node:24-alpine
ローカル開発(Docker Hub)
# REGISTRY未指定 → デフォルト "" → Docker Hubからpull
docker build -t myapp .
CI環境(ECR Public)
# --build-arg で ECR Public のプレフィックスを注入
docker build --build-arg REGISTRY="public.ecr.aws/docker/library/" -t myapp .
手順はこれだけです。事前にイメージをpullしておく「pre-pull」や、リポジトリ名のタグ付け替え(docker tag)作業も不要で、Dockerの標準機能(ARG)だけで解決できます
仕組みの図解
【ローカル開発】REGISTRY="" (デフォルト)
docker build → FROM node:24-alpine → Docker Hub
【CI環境】REGISTRY="public.ecr.aws/docker/library/"
docker build --build-arg REGISTRY=... → FROM public.ecr.aws/docker/library/node:24-alpine → ECR Public
- Dockerの標準機能(ARG)だけで実現、シェルスクリプトのハック不要
- ベースイメージの種類(node, python, nginx等)を問わず汎用的に使える
- Nodeのバージョン変更時もbuildspecの修正は不要です
--platform指定やマルチステージビルドでも安全に動作
やってみた
デモ用のCloudFormationテンプレートで、ECRリポジトリ・CodeBuild・CodePipelineを一括作成し、ビルド・プッシュを確認します。
ファイル構成
demo-ecr-ratelimit/
├── template.yaml # CloudFormation (ECR + CodeBuild + CodePipeline)
└── Dockerfile # ARG REGISTRY="" + FROM ${REGISTRY}node:24-alpine
Dockerfile
ARG REGISTRY=""
FROM ${REGISTRY}node:24-alpine
RUN echo "Hello from node:$(node -v)" > /hello.txt
CMD ["cat", "/hello.txt"]
CloudFormation テンプレート解説
| リソース | 説明 |
|---|---|
| ECRRepository | アプリイメージ保存用。ライフサイクルポリシーで最新3イメージを保持 |
| CodeBuildProject | ARM64ビルド環境。--build-arg REGISTRY でECR Publicを指定 |
| Pipeline | Source(S3)→ Build の2ステージ構成 |
| IAM Roles | CodeBuild用、CodePipeline用 |
buildspecの核心部分はこれだけです。
build:
commands:
# ★ --build-arg で ECR Public のプレフィックスを注入
- docker build --build-arg REGISTRY="public.ecr.aws/docker/library/" -t ${IMAGE_NAME} .
デプロイ
REGION="us-west-2"
# スタック作成
aws cloudformation deploy \
--stack-name demo-ecr-ratelimit \
--template-file template.yaml \
--capabilities CAPABILITY_IAM \
--region ${REGION}
# ソースバケット名を取得
BUCKET=$(aws cloudformation describe-stacks \
--stack-name demo-ecr-ratelimit \
--query 'Stacks[0].Outputs[?OutputKey==`SourceBucket`].OutputValue' \
--output text --region ${REGION})
# Dockerfileをzip化してアップロード
zip source.zip Dockerfile
aws s3 cp source.zip s3://${BUCKET}/source.zip --region ${REGION}
# パイプライン実行
aws codepipeline start-pipeline-execution \
--name demo-ecr-ratelimit-pipeline \
--region ${REGION}
動作確認
CodeBuildのログで、ECR Public経由でイメージが取得されていることを確認できます。
[Container] Running command docker build --build-arg REGISTRY="public.ecr.aws/docker/library/" -t ${IMAGE_NAME} .
#2 [internal] load metadata for public.ecr.aws/docker/library/node:24-alpine
#2 DONE 0.4s ← ECR Publicから取得(Docker Hubではない)
#4 [1/2] FROM public.ecr.aws/docker/library/node:24-alpine@sha256:01743339...
#4 resolve public.ecr.aws/docker/library/node:24-alpine done
#4 DONE 3.0s ← レートリミットなしで安定pull
Hello from node:v24.14.1
Pushed 123456789012.dkr.ecr.us-west-2.amazonaws.com/demo-ecr-ratelimit-app:20260409105429
FROM の解決先が public.ecr.aws になっており、Docker Hubへのアクセスが発生していません。
バージョン変更の自動追従を確認
Dockerfileを node:23-alpine に変更して再実行しました。buildspecは一切変更していません。
ARG REGISTRY=""
- FROM ${REGISTRY}node:24-alpine
+ FROM ${REGISTRY}node:23-alpine
#2 [internal] load metadata for public.ecr.aws/docker/library/node:23-alpine
#2 DONE 0.3s ← 自動的に23を取得
Hello from node:v23.11.1
| 実行 | Dockerfile | buildspec変更 | 結果 |
|---|---|---|---|
| 1回目 | FROM ${REGISTRY}node:24-alpine |
- | Hello from node:v24.14.1 |
| 2回目 | FROM ${REGISTRY}node:23-alpine |
変更なし | Hello from node:v23.11.1 |
補足: ECR Public で利用可能なバージョンの確認方法
# 特定バージョンの存在確認
docker manifest inspect public.ecr.aws/docker/library/node:23-alpine
# ECR Public Gallery のWebページでも確認可能
# https://gallery.ecr.aws/docker/library/node
撤去
aws ecr delete-repository \
--repository-name demo-ecr-ratelimit-app \
--force --region ${REGION}
aws cloudformation delete-stack \
--stack-name demo-ecr-ratelimit \
--region ${REGION}
Docker Hub レートリミットの回避方法比較
| 方法 | メリット | デメリット |
|---|---|---|
| ARG + ECR Public(本記事) | 無料、Docker標準機能、汎用的 | Dockerfileに ARG REGISTRY="" の追加が必要 |
| ECR Pull Through Cache | 自動キャッシュ、透過的、正攻法 | Secrets ManagerにDocker Hub認証情報の登録が必要 |
| CodeBuildをVPC内で実行(NAT Gateway経由) | EIP固定で他ユーザーとレートリミットを共有しない | NAT Gateway費用(約$45/月〜)、VPC構築が必要 |
| Docker Hub有料プラン | レートリミット撤廃 | 月額費用が発生 |
| Secrets ManagerでDocker Hub認証 | 200 pulls/6hに緩和 | 認証情報の管理が必要、完全な回避ではない |
| プライベートECRにコピー | 完全にDocker Hub非依存 | イメージ更新の運用が必要 |
まとめ
CodeBuildでDocker Hubのレートリミット(429)を回避する方法として、DockerfileのARG機能とECR Public Galleryの組み合わせを紹介しました。Dockerの標準機能だけで実現でき、ベースイメージの種類やバージョンを問わず汎用的に使えます。CodeBuildでDocker Hubのレートリミットにお困りの方は、ぜひお試しください。
参考情報
- Advice for customers dealing with Docker Hub rate limits, and a Coming Soon announcement | AWS Blog
- Resolve "error pulling image configuration: toomanyrequests" errors in CodeBuild | AWS re:Post
- Amazon ECR Public Gallery
- Docker Hub rate limiting
- ECR Pull Through Cache - Docker Hub support
CloudFormation テンプレート全文
クリックで展開
AWSTemplateFormatVersion: "2010-09-09"
Description: >
Demo: Docker Hub Rate Limit回避 - ARG REGISTRY + ECR Public で安全にCI/CDを実現
Resources:
ECRRepository:
Type: AWS::ECR::Repository
Properties:
RepositoryName: !Sub "${AWS::StackName}-app"
LifecyclePolicy:
LifecyclePolicyText: |
{"rules":[{"rulePriority":1,"selection":{"tagStatus":"any","countType":"imageCountMoreThan","countNumber":3},"action":{"type":"expire"}}]}
ArtifactBucket:
Type: AWS::S3::Bucket
DeletionPolicy: Delete
Properties:
VersioningConfiguration:
Status: Enabled
CodeBuildServiceRole:
Type: AWS::IAM::Role
Properties:
AssumeRolePolicyDocument:
Version: "2012-10-17"
Statement:
- Effect: Allow
Principal:
Service: codebuild.amazonaws.com
Action: sts:AssumeRole
Policies:
- PolicyName: Policy
PolicyDocument:
Version: "2012-10-17"
Statement:
- Effect: Allow
Action:
- logs:CreateLogGroup
- logs:CreateLogStream
- logs:PutLogEvents
Resource: !Sub "arn:aws:logs:${AWS::Region}:${AWS::AccountId}:log-group:/aws/codebuild/*"
- Effect: Allow
Action: ecr:GetAuthorizationToken
Resource: "*"
- Effect: Allow
Action:
- ecr:BatchCheckLayerAvailability
- ecr:PutImage
- ecr:InitiateLayerUpload
- ecr:UploadLayerPart
- ecr:CompleteLayerUpload
Resource: !GetAtt ECRRepository.Arn
- Effect: Allow
Action:
- s3:GetObject
- s3:GetObjectVersion
- s3:PutObject
Resource: !Sub "${ArtifactBucket.Arn}/*"
CodeBuildProject:
Type: AWS::CodeBuild::Project
Properties:
Name: !Sub "${AWS::StackName}-build"
ServiceRole: !GetAtt CodeBuildServiceRole.Arn
TimeoutInMinutes: 10
Environment:
Type: ARM_CONTAINER
ComputeType: BUILD_GENERAL1_SMALL
Image: aws/codebuild/amazonlinux-aarch64-standard:3.0
PrivilegedMode: true
EnvironmentVariables:
- Name: ECR_REPOSITORY_URI
Value: !Sub "${AWS::AccountId}.dkr.ecr.${AWS::Region}.amazonaws.com/${ECRRepository}"
- Name: IMAGE_NAME
Value: !Sub "${AWS::StackName}-app"
Source:
Type: CODEPIPELINE
BuildSpec: |
version: 0.2
phases:
pre_build:
commands:
- aws ecr get-login-password --region $AWS_REGION | docker login --username AWS --password-stdin ${ECR_REPOSITORY_URI}
build:
commands:
# ★ --build-arg で ECR Public のプレフィックスを注入
- docker build --build-arg REGISTRY="public.ecr.aws/docker/library/" -t ${IMAGE_NAME} .
- docker run --rm ${IMAGE_NAME}
post_build:
commands:
- IMAGE_TAG=$(date +%Y%m%d%H%M%S)
- docker tag ${IMAGE_NAME}:latest ${ECR_REPOSITORY_URI}:${IMAGE_TAG}
- docker push ${ECR_REPOSITORY_URI}:${IMAGE_TAG}
- echo "Pushed ${ECR_REPOSITORY_URI}:${IMAGE_TAG}"
Artifacts:
Type: CODEPIPELINE
CodePipelineServiceRole:
Type: AWS::IAM::Role
Properties:
AssumeRolePolicyDocument:
Version: "2012-10-17"
Statement:
- Effect: Allow
Principal:
Service: codepipeline.amazonaws.com
Action: sts:AssumeRole
Policies:
- PolicyName: Policy
PolicyDocument:
Version: "2012-10-17"
Statement:
- Effect: Allow
Action:
- s3:GetObject
- s3:GetObjectVersion
- s3:GetBucketVersioning
- s3:PutObject
Resource:
- !Sub "${ArtifactBucket.Arn}"
- !Sub "${ArtifactBucket.Arn}/*"
- Effect: Allow
Action:
- codebuild:BatchGetBuilds
- codebuild:StartBuild
Resource: !GetAtt CodeBuildProject.Arn
Pipeline:
Type: AWS::CodePipeline::Pipeline
Properties:
Name: !Sub "${AWS::StackName}-pipeline"
RoleArn: !GetAtt CodePipelineServiceRole.Arn
PipelineType: V2
ArtifactStore:
Type: S3
Location: !Ref ArtifactBucket
Stages:
- Name: Source
Actions:
- Name: S3Source
ActionTypeId:
Category: Source
Owner: AWS
Provider: S3
Version: "1"
Configuration:
S3Bucket: !Ref ArtifactBucket
S3ObjectKey: source.zip
PollForSourceChanges: false
OutputArtifacts:
- Name: SourceCode
- Name: Build
Actions:
- Name: Build
ActionTypeId:
Category: Build
Owner: AWS
Provider: CodeBuild
Version: "1"
Configuration:
ProjectName: !Ref CodeBuildProject
InputArtifacts:
- Name: SourceCode
Outputs:
SourceBucket:
Value: !Ref ArtifactBucket
PipelineName:
Value: !Ref Pipeline
ECRRepositoryUri:
Value: !Sub "${AWS::AccountId}.dkr.ecr.${AWS::Region}.amazonaws.com/${ECRRepository}"






