Suggest an editImprove this articleRefine the answer for “How to push and pull images on Docker Hub?”. Your changes go to moderation before they’re published.Approval requiredContentWhat you’re changing🇺🇸EN🇺🇦UAPreviewTitle (EN)Short answer (EN)**Push and pull on Docker Hub** comes down to four commands: `docker login` once, `docker tag` to give your image a `username/repo:tag` name, `docker push` to upload, `docker pull` to download. ```bash $ docker login -u youruser # one-time auth $ docker tag myapp:1.0 youruser/myapp:1.0 $ docker push youruser/myapp:1.0 $ docker pull youruser/myapp:1.0 # from another machine ``` **Key:** the image name must start with your Docker Hub username (or organization). Use a personal access token, not your account password. Public repos are free; private ones beyond the free tier need a paid plan.Shown above the full answer for quick recall.Answer (EN)Image**Pushing and pulling images on Docker Hub** is the basic workflow for sharing images: tag an image with your namespace, log in once, push it, then pull from anywhere. ## Theory ### TL;DR - Four commands cover 99 percent of cases: `docker login`, `docker tag`, `docker push`, `docker pull`. - Image name format: `<namespace>/<repo>:<tag>` where `<namespace>` is your Docker Hub username or organization. No prefix means "the official `library` namespace" - you cannot push there. - Authenticate with a **personal access token (PAT)**, not your account password. PATs scope (read-only / read-write / read-write-delete) and revoke cleanly. - Credentials live in `~/.docker/config.json` after `docker login`. On macOS / Windows, behind a credential helper. - Public repos: unlimited count, free. Private repos beyond the free tier need a paid plan. Anonymous pulls are rate-limited (100 per 6h per IP); authenticated pulls are higher. ### Quick example ```bash # 1. Build a local image $ docker build -t myapp:1.0 . # 2. Tag it with your Docker Hub namespace $ docker tag myapp:1.0 youruser/myapp:1.0 # 3. Authenticate (one time, until you logout) $ docker login -u youruser Password: **** # paste your PAT here, not your account password Login Succeeded # 4. Push $ docker push youruser/myapp:1.0 The push refers to repository [docker.io/youruser/myapp] adc4d6e8f1c2: Pushed 8a3f2d1c9b8e: Pushed 1.0: digest: sha256:9f8e7d6c... size: 1247 # From another machine $ docker pull youruser/myapp:1.0 ``` That is the complete loop. Build, tag, login, push - then pull from any host that has Docker installed. ### Image naming and tagging The full Docker Hub form: `[docker.io/]<namespace>/<repo>:<tag>`. - `docker.io/` is the implicit default registry. You almost never type it. - `<namespace>` = your Docker Hub username or an organization you belong to. - `<repo>` = the repository name. Created automatically on first push. - `<tag>` = a label for this build (`v1.0`, `2026-04-30`, `pr-1247`, `latest`). Defaults to `latest` if omitted. Examples: ```bash youruser/myapp:1.0 # personal namespace, version tag mycompany/api:2026-04-30 # org namespace, date tag mycompany/api # implies :latest - avoid for production nginx:1.27 # official library/nginx, library/ implicit ``` The `docker tag` command does not move bytes; it only adds a name pointing at the same image ID. One image can have many tags. ### Authentication: use a PAT, not a password Docker Hub supports two ways to log in: ```bash # Old way - account password (works, but bad practice) $ docker login -u youruser Password: <account password> # Modern way - personal access token $ docker login -u youruser Password: <paste PAT from https://hub.docker.com/settings/security> ``` PATs let you: - **Scope** the token (read-only / read-write / read-write-delete-public-repo). - **Revoke** without changing your account password. - Use multiple tokens per machine (CI runner, laptop, server) and rotate independently. In CI, tokens are stored as repository secrets. Never commit a token to git, never paste one into a Dockerfile. ### Where credentials live After `docker login`, credentials land in `~/.docker/config.json`: ```json { "auths": { "https://index.docker.io/v1/": { "auth": "base64(username:PAT)" } } } ``` On macOS and Windows, Docker uses the OS keychain (`credsStore`: `osxkeychain`, `desktop`) - the file just points at the keychain, not the raw token. Linux without a credential helper stores the base64-encoded token directly in the file. **`chmod 600 ~/.docker/config.json`** if you do that. ### Common mistakes **Pushing without your namespace prefix** ```bash # WRONG: tries to push into the official library/ namespace $ docker push myapp:1.0 requested access to the resource is denied # RIGHT: include your namespace $ docker tag myapp:1.0 youruser/myapp:1.0 $ docker push youruser/myapp:1.0 ``` The error message is unhelpful; the cause is always a missing namespace. **Logging in with your account password instead of a PAT** ```bash $ docker login -u youruser Password: <account password> Login Succeeded ``` It works, but if your password leaks, the attacker has full account access. PATs limit blast radius and revoke independently. **Pushing `:latest` and forgetting to push a versioned tag** ```bash # WRONG: only :latest is up there $ docker tag myapp:1.0 youruser/myapp:latest $ docker push youruser/myapp:latest # RIGHT: push both, so people can pin to a specific version $ docker tag myapp:1.0 youruser/myapp:1.0 $ docker tag myapp:1.0 youruser/myapp:latest $ docker push youruser/myapp:1.0 $ docker push youruser/myapp:latest # Or in one go: docker push --all-tags youruser/myapp ``` If consumers can only pull `:latest`, they cannot pin a specific version - and one day your `:latest` will surprise them. **Hitting anonymous pull rate limits in CI** ``` ERROR: toomanyrequests: You have reached your pull rate limit. ``` Docker Hub rate-limits anonymous IPs to 100 pulls per 6 hours, and a shared CI IP can burn through that fast. Authenticate the CI runner (`docker login` with a token) or pull through a registry mirror (Google Artifact Registry, AWS ECR Pull-through cache). ### Real-world usage - **GitHub Actions:** `docker/login-action@v3` reads a username + PAT from secrets, then `docker/build-push-action@v5` builds and pushes in one step. Standard pattern across thousands of repos. - **Local laptop -> shared dev environment:** push your branch image to a private Docker Hub repo, then have your colleague `docker pull` and run it without rebuilding. - **Open-source distribution:** tools like `tini`, `dive`, and many CLIs publish their official images to Docker Hub under a verified-publisher namespace. - **CI/CD pipelines on smaller teams:** PR builds tagged `myapp:pr-1247` get pushed to a Docker Hub private repo, the staging environment pulls them, integration tests run. ### Follow-up questions **Q:** What is the difference between `docker push` and `docker push --all-tags`? **A:** Plain `docker push youruser/myapp:1.0` pushes one tag. `docker push --all-tags youruser/myapp` pushes every local tag of that repository. Useful when you have tagged the same image with both a version and `latest`. **Q:** How do I push a private image? **A:** Same commands. The repo is created on first push; you set its visibility to private from the Docker Hub web UI (or via the API). Free Docker Hub accounts include one private repo; more requires a paid plan. **Q:** Can I push images for multiple architectures (amd64 + arm64)? **A:** Yes, with `docker buildx`. `docker buildx build --platform linux/amd64,linux/arm64 -t youruser/myapp:1.0 --push .` builds both architectures and uploads them as a manifest list under one tag. Consumers automatically pull the right one for their CPU. **Q:** How do I delete an image from Docker Hub? **A:** Tag deletion is from the web UI or via `curl` against the Docker Hub API with a PAT that has the `delete` scope. There is no `docker delete-tag` CLI. Repository deletion is also web-UI-only. **Q:** (Senior) How do you avoid embedding push credentials in CI logs and history? **A:** Use the official login action (`docker/login-action`) which reads from masked secrets and never echoes them. For self-hosted CI, pass the token via `--password-stdin`: `echo $DOCKER_TOKEN | docker login -u $DOCKER_USER --password-stdin`. Never use `docker login -u user -p $TOKEN` - that puts the token in shell history and process listings (`ps aux` would show it during the call). ## Examples ### Push from a GitHub Actions workflow ```yaml name: build-and-push on: push: branches: [main] jobs: publish: runs-on: ubuntu-latest steps: - uses: actions/checkout@v4 - uses: docker/login-action@v3 with: username: ${{ secrets.DOCKERHUB_USERNAME }} password: ${{ secrets.DOCKERHUB_TOKEN }} - uses: docker/build-push-action@v5 with: push: true tags: | youruser/myapp:latest youruser/myapp:${{ github.sha }} ``` Every push to `main` produces an image tagged with `latest` and the commit SHA. Pin production deploys to the SHA tag for reproducibility. ### Pull rate limits in CI - the fix ```bash # Symptom: build job fails ERROR: toomanyrequests: You have reached your pull rate limit. # Fix in your CI: log in before any pull - name: Docker login uses: docker/login-action@v3 with: username: ${{ secrets.DOCKERHUB_USERNAME }} password: ${{ secrets.DOCKERHUB_TOKEN }} - name: Build run: docker build -t myapp . ``` Authenticated pulls have a much higher limit. The login step itself is free. ### Push the same image to two registries ```bash # Tag once, push twice - useful for redundancy or migration $ docker build -t myapp:1.0 . $ docker tag myapp:1.0 youruser/myapp:1.0 $ docker tag myapp:1.0 ghcr.io/youruser/myapp:1.0 $ docker login docker.io $ docker push youruser/myapp:1.0 $ docker login ghcr.io $ docker push ghcr.io/youruser/myapp:1.0 ``` Same image bytes, two registries, two namespaces. Consumers pull from whichever they prefer.For the reviewerNote to the moderator (optional)Visible only to the moderator. Helps review go faster.