✦ For everyone, free.

Practical knowledge for real and everyday life

Home

19.1.4.3 Push Private Registry

A focused guide to Push Private Registry, connecting core concepts with practical Docker and container operations.

Pushing images to a private registry follows the same mechanics as pushing to Docker Hub, but requires authentication and the inclusion of the registry hostname in the image name. Private registries are used to keep images internal, enforce access control, reduce external dependency, and improve pull speeds within a network.

Authenticating to a Private Registry

Before pushing, Docker must authenticate with the private registry. The docker login command handles this:

docker login myregistry.example.com

Docker prompts for a username and password. On success, credentials are stored in ~/.docker/config.json (or the system credential store if configured). From that point, all push and pull commands to that registry use the stored credentials automatically.

For automated environments such as CI/CD pipelines, credentials are typically passed directly:

echo "$REGISTRY_PASSWORD" | docker login myregistry.example.com -u "$REGISTRY_USER" --password-stdin

Using --password-stdin avoids exposing the password in shell history or process listings.

Tagging an Image for a Private Registry

The image name must include the registry hostname and optional port as a prefix. Without this prefix, Docker assumes Docker Hub as the destination.

docker tag myapp:1.0.0 myregistry.example.com/myteam/myapp:1.0.0

The full format is:

<registry-host>[:<port>]/<repository-path>:<tag>

For a registry running on a non-standard port:

docker tag myapp:1.0.0 myregistry.example.com:5000/myteam/myapp:1.0.0

Pushing the Tagged Image

Once tagged, the push command uploads the image layers and manifest to the private registry:

docker push myregistry.example.com/myteam/myapp:1.0.0

Docker uploads only the layers not already present in the registry. If the base layers are already stored (because a previous image used the same base), only the new or changed layers are transferred, keeping the operation efficient.

Using a Local Private Registry (Docker Registry Image)

Docker provides an official registry image that can be run locally for testing or on-premises use:

docker run -d -p 5000:5000 --name local-registry registry:2

Tagging and pushing to this local instance:

docker tag myapp:latest localhost:5000/myapp:latest
docker push localhost:5000/myapp:latest

By default, this registry is insecure (HTTP). Docker daemon blocks pushes to insecure registries unless explicitly configured to allow them. To permit an insecure registry, add it to /etc/docker/daemon.json:

{
  "insecure-registries": ["localhost:5000"]
}

Then restart the Docker daemon.

Pushing to Cloud-Hosted Private Registries

Amazon ECR

Amazon Elastic Container Registry (ECR) requires obtaining a temporary authentication token before pushing:

aws ecr get-login-password --region us-east-1 | docker login --username AWS --password-stdin 123456789012.dkr.ecr.us-east-1.amazonaws.com

Tag and push:

docker tag myapp:1.0.0 123456789012.dkr.ecr.us-east-1.amazonaws.com/myapp:1.0.0
docker push 123456789012.dkr.ecr.us-east-1.amazonaws.com/myapp:1.0.0
Google Artifact Registry

Google Artifact Registry uses gcloud to configure Docker credentials:

gcloud auth configure-docker us-central1-docker.pkg.dev

Then push:

docker tag myapp:1.0.0 us-central1-docker.pkg.dev/my-project/my-repo/myapp:1.0.0
docker push us-central1-docker.pkg.dev/my-project/my-repo/myapp:1.0.0
Azure Container Registry

Azure requires logging in through the Azure CLI or with admin credentials:

az acr login --name myacrregistry

Then push:

docker tag myapp:1.0.0 myacrregistry.azurecr.io/myapp:1.0.0
docker push myacrregistry.azurecr.io/myapp:1.0.0

Self-Hosted Registry with TLS

For a production self-hosted registry, TLS is required. The registry is typically started with certificate paths mounted:

docker run -d \
  -p 443:443 \
  --name secure-registry \
  -v /certs:/certs \
  -e REGISTRY_HTTP_ADDR=0.0.0.0:443 \
  -e REGISTRY_HTTP_TLS_CERTIFICATE=/certs/domain.crt \
  -e REGISTRY_HTTP_TLS_KEY=/certs/domain.key \
  registry:2

Pushing to this registry is identical in form:

docker tag myapp:1.0.0 myregistry.example.com/myapp:1.0.0
docker push myregistry.example.com/myapp:1.0.0

As long as the TLS certificate is trusted by the Docker daemon (either a CA-signed cert or the CA added to the system trust store), no additional configuration is needed.

Verifying the Push

After a push completes, the registry UI (if available) or the registry API can confirm the image exists:

curl -u user:password https://myregistry.example.com/v2/myteam/myapp/tags/list

This returns a JSON object listing all available tags for the repository:

{"name":"myteam/myapp","tags":["1.0.0","latest"]}

Logging Out

After pushing in a shared or automated environment, logging out removes stored credentials:

docker logout myregistry.example.com

This removes the entry for that registry from ~/.docker/config.json, preventing credential exposure if the environment is reused.

Push Diagram

Local Docker Host docker login (authenticate) Private Registry auth push