docker push

docker push uploads a local image to a registry so other machines can pull and run it. It is the publishing step in the image workflow: build an image, give it a registry-qualified tag, authenticate if needed, then push it. Understanding what actually gets pushed helps you avoid broken deployments, overwritten tags, leaked secrets, and confusing repository names.

Overview: How docker push Works

A Docker image is a read-only template made from stacked filesystem layers plus a configuration object. The layers contain files produced by the Dockerfile, and the configuration records metadata such as the default command, environment variables, working directory, exposed ports, labels, and platform. A registry stores these pieces as content-addressed blobs and manifests. The manifest is the registry document that says which layer digests and config digest make up an image.

docker push does not push a container. A container is a running or stopped instance of an image with its own thin writable layer. That writable layer is local container state and is not uploaded by docker push. If you changed files inside a running container and want those changes in an image, you need a proper Dockerfile rebuild or, less preferably, docker commit before pushing.

The image name decides where the push goes. A name like hello-push:1.0 is only a local Docker Hub-style name under library, and normal users cannot push to that official namespace. A publishable name usually includes a namespace or registry host, such as YOUR_DOCKERHUB_USERNAME/hello-push:1.0, ghcr.io/acme/hello-push:1.0, or registry.example.com/platform/hello-push:1.0. The tag after the colon is the remote label that will point to the pushed manifest.

When you push, Docker contacts the registry, checks authentication and permissions, asks which layer blobs already exist, uploads only missing blobs, uploads the config object, uploads the manifest, and finally assigns the tag to that manifest. Existing layers are skipped because layers are identified by digest. This is why pushing a small change after a rebuild is often faster than the first push: unchanged base image and dependency layers may already exist in the registry.

Tags are mutable unless the registry or your release policy makes them immutable. Pushing my-api:1.0 today and pushing different content to the same tag tomorrow means new pulls may get different bytes under the same name. That can be useful for a moving tag like staging, but production releases are usually safer with unique version tags and recorded digests.

Syntax

The general command form is:

docker push [OPTIONS] NAME[:TAG]
Part Meaning
NAME The image repository to publish, optionally including a registry host and namespace, such as YOUR_DOCKERHUB_USERNAME/hello-push or registry.example.com/team/api.
:TAG The tag to publish. If omitted, Docker uses :latest, which is usually too vague for release work.
-a or --all-tags Push all local tags for the named repository. Use carefully because it can publish tags you did not mean to release.
-q or --quiet Suppress verbose progress output and print less detail.
docker login Not part of docker push, but normally required before pushing to a private registry or to your account on a public registry.

Most pushes follow this pattern: build or pull an image, tag it with the destination repository, log in, and push the destination tag.

docker build -t hello-push:1.0 .
docker tag hello-push:1.0 registry.example.com/training/hello-push:1.0
docker login registry.example.com
docker push registry.example.com/training/hello-push:1.0

Examples

Example 1: Build and Push a Small Image

FROM node:20-alpine
WORKDIR /app
RUN printf 'console.log("hello from pushed image")\n' > server.js
CMD ["node", "server.js"]
docker build -t registry.example.com/training/hello-push:1.0 .
docker login registry.example.com
docker push registry.example.com/training/hello-push:1.0

Output:

Login Succeeded
The push refers to repository [registry.example.com/training/hello-push]
8b1a2f4c9d31: Pushed
2f3c4d5e6a7b: Mounted from library/node
1.0: digest: sha256:9b1c2d3e4f506172839405a6b7c8d9e00112233445566778899aabbccddeeff0 size: 856

The Dockerfile uses node:20-alpine, a specific base image tag, instead of node or latest. During the push, Docker uploaded any missing layers for your image. A base layer may show as Mounted from or be skipped because the registry already has it. The final digest identifies the manifest that the remote 1.0 tag now points to.

Example 2: Retag a Local Image for Docker Hub

docker tag hello-push:1.0 YOUR_DOCKERHUB_USERNAME/hello-push:1.0
docker push YOUR_DOCKERHUB_USERNAME/hello-push:1.0

Output:

The push refers to repository [docker.io/YOUR_DOCKERHUB_USERNAME/hello-push]
8b1a2f4c9d31: Layer already exists
1.0: digest: sha256:9b1c2d3e4f506172839405a6b7c8d9e00112233445566778899aabbccddeeff0 size: 856

docker tag does not rebuild or copy the image. It adds another local name pointing to the same image ID, but this time the name includes a Docker Hub namespace you control. The push publishes that tag to Docker Hub. Replace YOUR_DOCKERHUB_USERNAME with your real Docker Hub username, without angle brackets, so the command remains normal shell syntax.

Example 3: Push Multiple Tags Deliberately

docker tag hello-push:1.0 registry.example.com/training/hello-push:stable
docker push registry.example.com/training/hello-push:1.0
docker push registry.example.com/training/hello-push:stable

Output:

The push refers to repository [registry.example.com/training/hello-push]
1.0: digest: sha256:9b1c2d3e4f506172839405a6b7c8d9e00112233445566778899aabbccddeeff0 size: 856
The push refers to repository [registry.example.com/training/hello-push]
stable: digest: sha256:9b1c2d3e4f506172839405a6b7c8d9e00112233445566778899aabbccddeeff0 size: 856

Both tags point to the same manifest digest. The version tag 1.0 is a good candidate for an immutable release. The channel tag stable can intentionally move later, but only as part of a release process. Pushing each tag explicitly is clearer than pushing every local tag by accident.

Example 4: Push All Local Tags for One Repository

docker push --all-tags registry.example.com/training/hello-push

Output:

The push refers to repository [registry.example.com/training/hello-push]
1.0: digest: sha256:9b1c2d3e4f506172839405a6b7c8d9e00112233445566778899aabbccddeeff0 size: 856
stable: digest: sha256:9b1c2d3e4f506172839405a6b7c8d9e00112233445566778899aabbccddeeff0 size: 856

--all-tags publishes every local tag under the exact repository name. It is useful for mirroring or controlled release automation, but it is risky on a developer laptop where temporary tags like test, debug, or old may exist.

How It Works Step by Step

  1. The Docker CLI sends the push request to the Docker daemon through the current Docker context.
  2. The daemon parses the image reference. If the registry host is omitted, Docker uses Docker Hub. If the tag is omitted, Docker uses latest.
  3. Docker checks local image metadata for the requested tag. If no local image has that name, the push fails before any upload.
  4. The daemon authenticates to the registry using credentials from docker login and the configured credential helper.
  5. Docker reads the image manifest, config object, and layer digests from the local image store.
  6. The registry is asked which blobs it already has. Existing blobs are reused; missing layer blobs are uploaded.
  7. After the blobs exist remotely, Docker uploads the image config and manifest. For multi-platform publishing, build tooling such as Buildx can publish an image index that points to multiple platform-specific manifests.
  8. The registry updates the tag so it points to the uploaded manifest. Future docker pull commands for that tag resolve through the registry to this content, unless the tag is later moved.

Remember that pushed image layers are immutable content, but tags are labels. Deleting a local image after pushing does not delete it from the registry. Deleting a remote tag in the registry does not remove running containers that already pulled the image.

Common Mistakes

Pushing an Image Without a Registry Namespace

docker push hello-push:1.0

This tries to push to Docker Hub’s default library namespace, which is reserved for official images. The fix is to tag the image for a namespace or registry you control:

docker tag hello-push:1.0 YOUR_DOCKERHUB_USERNAME/hello-push:1.0
docker push YOUR_DOCKERHUB_USERNAME/hello-push:1.0

Using latest as a Production Release

docker push registry.example.com/team/api:latest

latest is only a tag name. It can move every time someone pushes it, which makes rollback and audits harder. Prefer unique release tags, and record the digest printed by the push:

docker push registry.example.com/team/api:1.4.2

Expecting Runtime Data to Be Pushed

docker exec api sh -c 'printf new-data > /app/data.txt'
docker push registry.example.com/team/api:1.4.2

The pushed image does not include changes made in a running container’s writable layer. Put required files in the Dockerfile build context and rebuild the image, or store runtime data in a named volume, database, or object store. Volumes are managed separately from images and are not uploaded by docker push.

Baking Secrets into Layers Before Pushing

FROM node:20-alpine
WORKDIR /app
ENV API_TOKEN=changeme
COPY . .
CMD ["node", "server.js"]

Never put real credentials in a Dockerfile, copied file, or build layer. A later RUN rm does not erase the value from earlier layers. Use runtime environment variables from your orchestrator, Docker secrets, mounted files, or a dedicated secret store.

Publishing Every Local Tag Accidentally

docker push --all-tags registry.example.com/team/api

This may publish throwaway local tags. Check docker image ls registry.example.com/team/api first, or push only the exact release tag you intend to publish.

Best Practices

  • Tag images with a registry and namespace before pushing, such as registry.example.com/team/service:1.4.2.
  • Use explicit version tags for releases. Avoid relying on an implicit or explicit latest tag for production.
  • Record the digest printed by docker push so deployments and audits can identify exact image content.
  • Treat release tags as immutable. Publish a new tag for changed code instead of moving an old release tag.
  • Use moving channel tags such as staging or stable only when your release process expects them to move.
  • Authenticate with short-lived tokens or CI-provided credentials where possible. Do not place passwords or tokens in Dockerfiles.
  • Keep images small with careful Dockerfiles, .dockerignore, and multi-stage builds so pushes are faster and less risky.
  • Push from CI for official releases so the pushed image comes from a repeatable build, not an untracked developer workstation.
  • Remember that EXPOSE in an image is only metadata. It does not publish a port when someone later runs the image; docker run -p or Compose ports: does that.

Practice Exercises

  1. Build a tiny image called hello-push:0.1.0, retag it for a registry namespace you control, and identify the exact command that would push it. Expected end state: the destination image name includes a namespace and a version tag.
  2. Create two local tags for one image: an immutable version tag and a moving channel tag. Decide which one should be used by production and explain why.
  3. Inspect a pushed image’s output digest from your registry or terminal logs. Hint: compare the human tag with the sha256 digest and describe which one is safer for exact rollback.

Summary

  • docker push publishes local image content to a registry repository under a tag.
  • It uploads image layers, config, and manifests, not containers, volumes, or runtime writable-layer changes.
  • The destination image name controls the registry, namespace, repository, and tag that receive the push.
  • Docker uploads only missing blobs because layers are content-addressed by digest.
  • Tags are movable labels unless your registry policy or release process prevents movement.
  • Use registry-qualified names, explicit release tags, least-privilege credentials, and recorded digests for reliable distribution.