How to Push Image to Registry
How to Push Image to Registry Pushing a Docker image to a registry is a foundational skill in modern software development, DevOps, and cloud-native infrastructure. Whether you're deploying microservices, automating CI/CD pipelines, or managing containerized applications across environments, the ability to securely and efficiently push images to a registry ensures consistency, scalability, and repr
How to Push Image to Registry
Pushing a Docker image to a registry is a foundational skill in modern software development, DevOps, and cloud-native infrastructure. Whether you're deploying microservices, automating CI/CD pipelines, or managing containerized applications across environments, the ability to securely and efficiently push images to a registry ensures consistency, scalability, and reproducibility. This guide provides a comprehensive, step-by-step walkthrough of how to push an image to a registrycovering public platforms like Docker Hub, private registries like Harbor or Amazon ECR, and enterprise solutions like Google Container Registry (GCR) or Azure Container Registry (ACR). Youll learn not only the mechanics of the push command, but also the underlying concepts, security considerations, and industry best practices that separate novice users from seasoned practitioners.
By the end of this tutorial, you will understand how to authenticate, tag, and upload container images to any major registry, troubleshoot common errors, and implement automated workflows that integrate seamlessly into your development lifecycle. This knowledge is essential for engineers working with Kubernetes, Jenkins, GitHub Actions, GitLab CI, or any modern orchestration platform that relies on container images as its deployment unit.
Step-by-Step Guide
Prerequisites
Before you begin pushing images to a registry, ensure you have the following components installed and configured:
- Docker Engine installed on your local machine or build server. Verify installation by running
docker --version. - A container image built locally. If you dont have one, create a simple image using a Dockerfile.
- Access to a container registrypublic (e.g., Docker Hub) or private (e.g., AWS ECR, Google GCR, Azure ACR, Harbor).
- Authentication credentials for the registry. This may include a username/password, access token, or IAM role.
- Network connectivity to the registry endpoint. Some registries require specific ports or proxy configurations.
Step 1: Build Your Container Image
Before pushing, you must have a valid Docker image. Create a simple Dockerfile to illustrate the process:
FROM alpine:latest
RUN apk add --no-cache curl
COPY . /app
WORKDIR /app
CMD ["echo", "Hello from containerized app"]
Save this as Dockerfile in your project directory. Then, build the image using the docker build command:
docker build -t my-app:v1 .
The -t flag assigns a tag to the image. The format is repository-name:tag. In this case, my-app is the repository name and v1 is the version tag. The dot (.) at the end tells Docker to use the current directory as the build context.
To verify the image was built successfully, run:
docker images
You should see your image listed with the repository name, tag, image ID, creation time, and size.
Step 2: Log In to Your Registry
Most registries require authentication before you can push images. The login process varies depending on the registry provider.
Logging into Docker Hub
If youre using Docker Hub, the default public registry, authenticate with:
docker login
Youll be prompted to enter your Docker Hub username and password (or personal access token, which is recommended for security). After successful authentication, Docker stores your credentials in ~/.docker/config.json.
Logging into Amazon ECR
Amazon Elastic Container Registry (ECR) requires a temporary authentication token generated via AWS CLI. First, ensure you have the AWS CLI installed and configured with valid credentials:
aws configure
Then, retrieve the login command for your region (e.g., us-east-1):
aws ecr get-login-password --region us-east-1 | docker login --username AWS --password-stdin 123456789012.dkr.ecr.us-east-1.amazonaws.com
Replace 123456789012 with your AWS account ID and us-east-1 with your target region. This command retrieves a temporary password and passes it to Docker for authentication.
Logging into Google Container Registry (GCR)
For Google Cloud, use the gcloud CLI:
gcloud auth configure-docker gcr.io
This configures Docker to use your Google Cloud credentials for authentication with GCR. If you're using Artifact Registry instead of GCR, replace gcr.io with your region-specific endpoint (e.g., us-central1-docker.pkg.dev).
Logging into Azure Container Registry (ACR)
Azure requires you to enable admin access or use service principal credentials. First, ensure youre logged into the Azure CLI:
az login
Then, retrieve the login server and credentials for your registry:
az acr login --name myregistry
Replace myregistry with your ACR name. This command authenticates Docker using the registrys admin credentials or a service principal.
Logging into Harbor or Other Private Registries
For self-hosted registries like Harbor, use:
docker login your-harbor-domain.com
Enter your Harbor username and password (or API token if two-factor authentication is enabled). Ensure the registrys SSL certificate is trusted by your system or add it to your Docker daemons trusted certificate store.
Step 3: Tag Your Image for the Registry
After logging in, you must tag your local image with the full registry path. Docker uses the image name to determine where to push it. The format is:
registry-domain.com/namespace/repository:tag
For example, to push to Docker Hub:
docker tag my-app:v1 username/my-app:v1
To push to Amazon ECR:
docker tag my-app:v1 123456789012.dkr.ecr.us-east-1.amazonaws.com/my-app:v1
To push to Google Artifact Registry:
docker tag my-app:v1 us-central1-docker.pkg.dev/my-project/my-repo/my-app:v1
To push to Azure Container Registry:
docker tag my-app:v1 myregistry.azurecr.io/my-app:v1
To push to Harbor:
docker tag my-app:v1 your-harbor-domain.com/myproject/my-app:v1
Use docker images again to confirm the new tagged image appears in your list. Youll now see two entries: one with the short name and one with the full registry path.
Step 4: Push the Image to the Registry
Once tagged, push the image using the docker push command:
docker push username/my-app:v1
For ECR:
docker push 123456789012.dkr.ecr.us-east-1.amazonaws.com/my-app:v1
For GCR:
docker push us-central1-docker.pkg.dev/my-project/my-repo/my-app:v1
For ACR:
docker push myregistry.azurecr.io/my-app:v1
For Harbor:
docker push your-harbor-domain.com/myproject/my-app:v1
Docker will begin uploading layers of your image. Each layer is compressed and uploaded individually. If a layer already exists on the registry (due to previous pushes), Docker skips itthis makes subsequent pushes faster and more efficient.
Upon successful upload, youll see output similar to:
The push refers to repository [username/my-app]
f5a7b9d1c3e2: Pushed
a1b2c3d4e5f6: Pushed
v1: digest: sha256:abc123def456ghi789... size: 1234
The digest is a cryptographic hash of the image manifest. It uniquely identifies your image and is critical for reproducible deployments.
Step 5: Verify the Push
After pushing, verify the image exists in the registry:
- Docker Hub: Visit https://hub.docker.com/repositories and navigate to your repository.
- Amazon ECR: Go to the AWS Console > ECR > Repositories and locate your image.
- Google Artifact Registry: Use the Google Cloud Console > Artifact Registry > Repositories.
- Azure Container Registry: Navigate to your ACR in the Azure Portal > Repositories.
- Harbor: Log in to your Harbor UI and browse the project repository.
Alternatively, use the registrys CLI tools:
For ECR:
aws ecr list-images --repository-name my-app --region us-east-1
For GCR:
gcloud container images list-tags us-central1-docker.pkg.dev/my-project/my-repo/my-app
For ACR:
az acr repository show-tags --name myregistry --repository my-app --output table
For Harbor (via API):
curl -u username:password https://your-harbor-domain.com/v2/myproject/my-app/tags/list
These commands confirm the image exists and show its tags and metadata.
Best Practices
Use Semantic Versioning for Tags
Never use the latest tag in production unless you have a strict rollback and audit policy. Instead, adopt semantic versioning (e.g., v1.2.3, 1.2.3-beta). This ensures reproducibility and enables rollbacks. Tools like GitLab CI, GitHub Actions, or Jenkins can automatically tag images using commit hashes or Git tags.
Minimize Image Size
Smaller images reduce push/pull times and improve security by reducing the attack surface. Use multi-stage builds, choose minimal base images (e.g., alpine, distroless), and remove unnecessary files during build. For example:
FROM golang:alpine AS builder
WORKDIR /app
COPY . .
RUN go build -o myapp .
FROM alpine:latest
RUN apk --no-cache add ca-certificates
COPY --from=builder /app/myapp /usr/local/bin/myapp
CMD ["myapp"]
This reduces the final image size from hundreds of MB to under 10 MB.
Sign Images with Cosign or Notary
Image signing ensures integrity and authenticity. Use Sigstores cosign to sign your images:
cosign sign --key cosign.key your-registry.com/myapp:v1
Verify signatures during deployment:
cosign verify --key cosign.pub your-registry.com/myapp:v1
Many orchestration platforms (e.g., Kubernetes with Kyverno or OPA) can enforce signed images as a policy.
Use Digests for Immutable Deployments
Instead of referencing myapp:v1, reference the digest: myapp@sha256:abc123.... Digests are immutableonce pushed, they cannot be changed. This prevents tag mutation attacks where a malicious actor re-tags a vulnerable image as v1.
To get the digest after pushing:
docker inspect --format='{{index .RepoDigests 0}}' your-registry.com/myapp:v1
Use this digest in your Kubernetes manifests, Helm charts, or deployment scripts.
Limit Registry Access with RBAC
Never use admin credentials for automated pipelines. Create dedicated service accounts with least-privilege permissions. In ECR, use IAM policies. In ACR, use Azure RBAC roles. In Harbor, assign project-level roles (Developer, Maintainer, Guest). Rotate credentials regularly.
Scan Images for Vulnerabilities
Pushing vulnerable images defeats the purpose of containerization. Integrate image scanning into your pipeline:
- Docker Hub: Automatic scanning for public images.
- Trivy: Open-source scanner:
trivy image your-registry.com/myapp:v1 - Clair: Used by Harbor and GitLab.
- Amazon Inspector: For ECR images.
- Google Container Analysis: For GCR.
Fail builds if critical vulnerabilities are found.
Automate with CI/CD Pipelines
Manually pushing images is error-prone and unscalable. Automate with CI/CD:
GitHub Actions example:
name: Build and Push Image
on:
push:
branches: [ main ]
jobs:
build-and-push:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- uses: docker/setup-buildx-action@v3
- uses: docker/login-action@v3
with:
registry: your-registry.com
username: ${{ secrets.REGISTRY_USERNAME }}
password: ${{ secrets.REGISTRY_PASSWORD }}
- uses: docker/build-push-action@v5
with:
context: .
file: ./Dockerfile
tags: your-registry.com/myapp:${{ github.sha }}
push: true
This pushes the image using the Git commit SHA as the tagensuring traceability and immutability.
Monitor Registry Usage and Quotas
Public registries like Docker Hub have rate limits. Private registries have storage quotas. Monitor usage with:
- Docker Hub: https://hub.docker.com/settings/quotas
- ECR: CloudWatch metrics
- ACR: Usage metrics in Azure Portal
- Harbor: Built-in analytics dashboard
Set alerts for quota thresholds and implement image cleanup policies (e.g., delete images older than 30 days).
Tools and Resources
Core Tools
- Docker CLI The standard tool for building, tagging, and pushing images.
- Docker Buildx Enables multi-platform builds and caching. Essential for cross-architecture deployments (e.g., ARM64, AMD64).
- Podman Docker-compatible alternative that doesnt require a daemon. Useful in rootless environments.
- Skopeo Tool for copying images between registries without requiring Docker. Useful for air-gapped environments.
- Oras OCI Artifact Registry client for pushing non-container artifacts (e.g., Helm charts, OPA policies).
Registry Platforms
- Docker Hub Free tier available; best for open-source and small teams.
- Amazon ECR Integrated with AWS services; pay-per-use pricing.
- Google Artifact Registry Unified registry for containers, Helm, and npm; supports regional replication.
- Azure Container Registry Deep integration with Azure Kubernetes Service (AKS).
- Harbor Open-source, on-premises registry with vulnerability scanning, role-based access, and replication.
- GitHub Container Registry (GHCR) Free private registry integrated with GitHub repositories.
- GitLab Container Registry Built into GitLab CI; automatically tagged with pipeline metadata.
Security and Compliance Tools
- Trivy Open-source vulnerability scanner with CI/CD integration.
- Clair Static analysis tool for container images; used by Harbor and Quay.
- Notary Legacy image signing tool (being replaced by Cosign).
- Cosign Modern, Sigstore-based image signing and verification tool.
- Kyverno Kubernetes policy engine that can enforce signed images and registry allowlists.
- OPA/Gatekeeper Open Policy Agent for enforcing registry and image policies in Kubernetes.
Documentation and Learning Resources
- Docker Documentation
- Amazon ECR Docs
- Google Artifact Registry Docs
- Azure Container Registry Docs
- Harbor Documentation
- Cosign GitHub Repo
- Trivy GitHub Repo
Real Examples
Example 1: Pushing to Docker Hub from a CI Pipeline
Scenario: Youre building a Node.js microservice and want to push it to Docker Hub on every commit to the main branch.
Dockerfile:
FROM node:18-alpine
WORKDIR /app
COPY package*.json ./
RUN npm ci --only=production
COPY . .
EXPOSE 3000
CMD ["node", "server.js"]
GitHub Actions Workflow:
name: Build and Push to Docker Hub
on:
push:
branches: [ main ]
jobs:
docker:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- name: Set up Docker Buildx
uses: docker/setup-buildx-action@v3
- name: Login to Docker Hub
uses: docker/login-action@v3
with:
username: ${{ secrets.DOCKERHUB_USERNAME }}
password: ${{ secrets.DOCKERHUB_TOKEN }}
- name: Build and push
uses: docker/build-push-action@v5
with:
context: .
file: ./Dockerfile
tags: yourusername/my-node-app:${{ github.sha }}
push: true
After this runs, the image is pushed to yourusername/my-node-app with the Git commit SHA as the tag. You can now deploy this exact image to Kubernetes using:
image: yourusername/my-node-app:sha256:abc123...
Example 2: Pushing to ECR with AWS CodeBuild
Scenario: Youre using AWS CodeBuild to build and push images to ECR for deployment on ECS.
buildspec.yml:
version: 0.2
phases:
pre_build:
commands:
- echo Logging in to Amazon ECR...
- $(aws ecr get-login-password --region us-east-1 | docker login --username AWS --password-stdin 123456789012.dkr.ecr.us-east-1.amazonaws.com)
build:
commands:
- echo Building the Docker image...
- docker build -t my-ecr-app .
post_build:
commands:
- echo Pushing the Docker image...
- docker tag my-ecr-app:latest 123456789012.dkr.ecr.us-east-1.amazonaws.com/my-ecr-app:latest
- docker push 123456789012.dkr.ecr.us-east-1.amazonaws.com/my-ecr-app:latest
- echo Push completed
This workflow integrates seamlessly with ECS task definitions and ensures your containers are always pulled from a trusted, private registry.
Example 3: Pushing to Harbor with Image Signing
Scenario: Your organization requires signed images for compliance. Youre using Harbor as your internal registry.
After building and tagging:
docker build -t harbor.company.com/project/myapp:v1 .
docker push harbor.company.com/project/myapp:v1
cosign sign --key cosign.key harbor.company.com/project/myapp:v1
Now, your Kubernetes cluster uses Kyverno to block any unsigned images:
apiVersion: kyverno.io/v1
kind: ClusterPolicy
metadata:
name: require-signed-images
spec:
rules:
- name: check-image-signature
match:
resources:
kinds:
- Pod
validate:
message: "Image must be signed with cosign"
deny:
conditions:
- key: "{{ request.object.spec.containers[].image }}"
operator: NotIn
value: ["cosign:verified"]
This ensures only signed, trusted images are deployed.
FAQs
What happens if I push an image with the same tag twice?
If you push an image with the same tag (e.g., myapp:v1) multiple times, the registry will overwrite the previous image. The digest will change, and any system referencing the old digest will no longer be able to pull it unless its retained by the registrys retention policy. Always use immutable tags (e.g., commit hashes) for production.
Can I push images without Docker installed?
Yes. Tools like buildah, podman, and skopeo can build and push images without requiring the Docker daemon. Skopeo can even copy images directly between registries (e.g., Docker Hub ? ECR) without downloading them locally.
Why is my push failing with unauthorized: authentication required?
This typically means:
- Youre not logged in to the registry.
- Your credentials expired (common with AWS ECR tokens).
- Youre trying to push to a repository you dont have write access to.
- Youre using the wrong registry URL (e.g., Docker Hub URL for ECR).
Run docker logout and re-login. Verify your registry URL and permissions.
How do I delete an image from a registry?
Most registries dont allow deletion via Docker CLI. Use the registrys native tools:
- ECR:
aws ecr delete-image --repository-name myapp --image-imageTag v1 - ACR:
az acr repository delete --name myregistry --image myapp:v1 - Harbor: Use the UI or API to delete tags or repositories.
Be cautiousdeletion is often irreversible.
Whats the difference between a tag and a digest?
A tag is a human-readable label (e.g., v1.2.3) that can be changed or reassigned. A digest is a SHA-256 hash of the image manifest and is immutable. Use tags for development and digests for production deployments.
Can I push to multiple registries at once?
Yes. Use Docker Buildx to build and push to multiple registries in one command:
docker buildx build --push --platform linux/amd64,linux/arm64 \
-t username/myapp:v1 \
-t your-harbor-domain.com/project/myapp:v1 \
.
This builds a multi-platform image and pushes it to both Docker Hub and Harbor simultaneously.
How do I handle rate limits on Docker Hub?
Docker Hub imposes anonymous and authenticated pull limits. To avoid throttling:
- Use a paid plan for higher limits.
- Cache images locally or in your CI runner.
- Use a private registry for internal images.
- Use
docker pullonly when necessary in CI pipelines.
Conclusion
Pushing an image to a registry is more than a technical commandits a critical step in the modern software delivery pipeline. Mastering this process ensures your applications are deployed consistently, securely, and at scale. From choosing the right registry and tagging strategy to implementing image signing and automated pipelines, every decision impacts reliability and security.
This guide has provided a complete, practical roadmapfrom building your first image to pushing it to Docker Hub, ECR, or Harbor with best practices in mind. You now understand how to authenticate, tag, verify, and automate the push process, while avoiding common pitfalls like mutable tags, unsecured credentials, and unscanned vulnerabilities.
As container adoption continues to grow, the ability to manage images effectively will become even more essential. Whether youre a developer, DevOps engineer, or platform architect, the skills outlined here form the foundation of cloud-native operations. Implement these practices in your workflows today, and youll build systems that are not only functionalbut trustworthy, auditable, and resilient.