{"meta":{"title":"Publishing Docker images","intro":"In this tutorial, you'll learn how to publish Docker images to a registry, such as Docker Hub or GitHub Packages, as part of your continuous integration (CI) workflow.","product":"GitHub Actions","breadcrumbs":[{"href":"/en/actions","title":"GitHub Actions"},{"href":"/en/actions/tutorials","title":"Tutorials"},{"href":"/en/actions/tutorials/publish-packages","title":"Publish packages"},{"href":"/en/actions/tutorials/publish-packages/publish-docker-images","title":"Publish Docker images"}],"documentType":"article"},"body":"# Publishing Docker images\n\nIn this tutorial, you'll learn how to publish Docker images to a registry, such as Docker Hub or GitHub Packages, as part of your continuous integration (CI) workflow.\n\n## Introduction\n\nThis guide shows you how to create a workflow that performs a Docker build, and then publishes Docker images to Docker Hub or GitHub Packages. With a single workflow, you can publish images to a single registry or to multiple registries.\n\n> \\[!NOTE]\n> If you want to push to another third-party Docker registry, the example in the [Publishing images to GitHub Packages](#publishing-images-to-github-packages) section can serve as a good template.\n\n## Prerequisites\n\nWe recommend that you have a basic understanding of workflow configuration options and how to create a workflow file. For more information, see [Writing workflows](/en/actions/learn-github-actions).\n\nYou might also find it helpful to have a basic understanding of the following:\n\n* [Using secrets in GitHub Actions](/en/actions/security-guides/using-secrets-in-github-actions)\n* [Use GITHUB\\_TOKEN for authentication in workflows](/en/actions/security-guides/automatic-token-authentication)\n* [Working with the Container registry](/en/packages/working-with-a-github-packages-registry/working-with-the-container-registry)\n\n## About image configuration\n\nThis guide assumes that you have a complete definition for a Docker image stored in a GitHub repository. For example, your repository must contain a *Dockerfile*, and any other files needed to perform a Docker build to create an image.\n\nYou can use pre-defined annotation keys to add metadata including a description, a license, and a source repository to your container image. For more information, see [Working with the Container registry](/en/packages/working-with-a-github-packages-registry/working-with-the-container-registry#labelling-container-images).\n\nIn this guide, we will use the Docker `build-push-action` action to build the Docker image and push it to one or more Docker registries. For more information, see [`build-push-action`](https://github.com/marketplace/actions/build-and-push-docker-images).\n\n## Publishing images to Docker Hub\n\n> \\[!NOTE]\n> Docker Hub normally imposes rate limits on both push and pull operations which will affect jobs on self-hosted runners. However, GitHub-hosted runners are not subject to these limits based on an agreement between GitHub and Docker.\n\nEach time you create a new release on GitHub, you can trigger a workflow to publish your image. The workflow in the example below runs when the `release` event triggers with the `published` activity type.\n\nIn the example workflow below, we use the Docker `login-action` and `build-push-action` actions to build the Docker image and, if the build succeeds, push the built image to Docker Hub.\n\nTo push to Docker Hub, you will need to have a Docker Hub account, and have a Docker Hub repository created. For more information, see [Pushing a Docker container image to Docker Hub](https://docs.docker.com/docker-hub/quickstart/#step-3-build-and-push-an-image-to-docker-hub) in the Docker documentation.\n\nThe `login-action` options required for Docker Hub are:\n\n* `username` and `password`: This is your Docker Hub username and password. We recommend storing your Docker Hub username and password as secrets so they aren't exposed in your workflow file. For more information, see [Using secrets in GitHub Actions](/en/actions/security-guides/using-secrets-in-github-actions).\n\nThe `metadata-action` option required for Docker Hub is:\n\n* `images`: The namespace and name for the Docker image you are building/pushing to Docker Hub.\n\nThe `build-push-action` options required for Docker Hub are:\n\n* `tags`: The tag of your new image in the format `DOCKER-HUB-NAMESPACE/DOCKER-HUB-REPOSITORY:VERSION`. You can set a single tag as shown below, or specify multiple tags in a list.\n* `push`: If set to `true`, the image will be pushed to the registry if it is built successfully.\n\n```yaml copy\n# This workflow uses actions that are not certified by GitHub.\n# They are provided by a third-party and are governed by\n# separate terms of service, privacy policy, and support\n# documentation.\n\n# GitHub recommends pinning actions to a commit SHA.\n# To get a newer version, you will need to update the SHA.\n# You can also reference a tag or branch, but the action may change without warning.\n\nname: Publish Docker image\n\non:\n  release:\n    types: [published]\n\njobs:\n  push_to_registry:\n    name: Push Docker image to Docker Hub\n    runs-on: ubuntu-latest\n    permissions:\n      packages: write\n      contents: read\n      attestations: write\n      id-token: write\n    steps:\n      - name: Check out the repo\n        uses: actions/checkout@v6\n\n      - name: Log in to Docker Hub\n        uses: docker/login-action@f4ef78c080cd8ba55a85445d5b36e214a81df20a\n        with:\n          username: ${{ secrets.DOCKER_USERNAME }}\n          password: ${{ secrets.DOCKER_PASSWORD }}\n\n      - name: Extract metadata (tags, labels) for Docker\n        id: meta\n        uses: docker/metadata-action@9ec57ed1fcdbf14dcef7dfbe97b2010124a938b7\n        with:\n          images: my-docker-hub-namespace/my-docker-hub-repository\n\n      - name: Build and push Docker image\n        id: push\n        uses: docker/build-push-action@3b5e8027fcad23fda98b2e3ac259d8d67585f671\n        with:\n          context: .\n          file: ./Dockerfile\n          push: true\n          tags: ${{ steps.meta.outputs.tags }}\n          labels: ${{ steps.meta.outputs.labels }}\n\n      - name: Generate artifact attestation\n        uses: actions/attest@v4\n        with:\n          subject-name: index.docker.io/my-docker-hub-namespace/my-docker-hub-repository\n          subject-digest: ${{ steps.push.outputs.digest }}\n          push-to-registry: true\n```\n\nThe above workflow checks out the GitHub repository, uses the `login-action` to log in to the registry, and then uses the `build-push-action` action to: build a Docker image based on your repository's `Dockerfile`; push the image to Docker Hub, and apply a tag to the image.\n\nIn the last step, it generates an artifact attestation for the image, which increases supply chain security. For more information, see [Using artifact attestations to establish provenance for builds](/en/actions/security-guides/using-artifact-attestations-to-establish-provenance-for-builds).\n\n## Publishing images to GitHub Packages\n\nEach time you create a new release on GitHub, you can trigger a workflow to publish your image. The workflow in the example below runs when a change is pushed to the `release` branch.\n\nIn the example workflow below, we use the Docker `login-action`, `metadata-action`, and `build-push-action` actions to build the Docker image, and if the build succeeds, push the built image to GitHub Packages.\n\nThe `login-action` options required for GitHub Packages are:\n\n* `registry`: Must be set to `ghcr.io`.\n* `username`: You can use the `${{ github.actor }}` context to automatically use the username of the user that triggered the workflow run. For more information, see [Contexts reference](/en/actions/learn-github-actions/contexts#github-context).\n* `password`: You can use the automatically-generated `GITHUB_TOKEN` secret for the password. For more information, see [Use GITHUB\\_TOKEN for authentication in workflows](/en/actions/security-guides/automatic-token-authentication).\n\nThe `metadata-action` option required for GitHub Packages is:\n\n* `images`: The namespace and name for the Docker image you are building.\n\nThe `build-push-action` options required for GitHub Packages are:\n\n* `context`: Defines the build's context as the set of files located in the specified path.\n* `push`: If set to `true`, the image will be pushed to the registry if it is built successfully.\n* `tags` and `labels`: These are populated by output from `metadata-action`.\n\n> \\[!NOTE]\n>\n> * This workflow uses actions that are not certified by GitHub. They are provided by a third-party and are governed by separate terms of service, privacy policy, and support documentation.\n> * GitHub recommends pinning actions to a commit SHA. To get a newer version, you will need to update the SHA. You can also reference a tag or branch, but the action may change without warning.\n\n```yaml annotate copy\n#\nname: Create and publish a Docker image\n\n# Configures this workflow to run every time a change is pushed to the branch called `release`.\non:\n  push:\n    branches: ['release']\n\n# Defines two custom environment variables for the workflow. These are used for the Container registry domain, and a name for the Docker image that this workflow builds.\nenv:\n  REGISTRY: ghcr.io\n  IMAGE_NAME: ${{ github.repository }}\n\n# There is a single job in this workflow. It's configured to run on the latest available version of Ubuntu.\njobs:\n  build-and-push-image:\n    runs-on: ubuntu-latest\n    # Sets the permissions granted to the `GITHUB_TOKEN` for the actions in this job.\n    permissions:\n      contents: read\n      packages: write\n      attestations: write\n      id-token: write\n      #\n    steps:\n      - name: Checkout repository\n        uses: actions/checkout@v6\n      # Uses the `docker/login-action` action to log in to the Container registry registry using the account and password that will publish the packages. Once published, the packages are scoped to the account defined here.\n      - name: Log in to the Container registry\n        uses: docker/login-action@65b78e6e13532edd9afa3aa52ac7964289d1a9c1\n        with:\n          registry: ${{ env.REGISTRY }}\n          username: ${{ github.actor }}\n          password: ${{ secrets.GITHUB_TOKEN }}\n      # This step uses [docker/metadata-action](https://github.com/docker/metadata-action#about) to extract tags and labels that will be applied to the specified image. The `id` \"meta\" allows the output of this step to be referenced in a subsequent step. The `images` value provides the base name for the tags and labels.\n      - name: Extract metadata (tags, labels) for Docker\n        id: meta\n        uses: docker/metadata-action@9ec57ed1fcdbf14dcef7dfbe97b2010124a938b7\n        with:\n          images: ${{ env.REGISTRY }}/${{ env.IMAGE_NAME }}\n      # This step uses the `docker/build-push-action` action to build the image, based on your repository's `Dockerfile`. If the build succeeds, it pushes the image to GitHub Packages.\n      # It uses the `context` parameter to define the build's context as the set of files located in the specified path. For more information, see [Usage](https://github.com/docker/build-push-action#usage) in the README of the `docker/build-push-action` repository.\n      # It uses the `tags` and `labels` parameters to tag and label the image with the output from the \"meta\" step.\n      - name: Build and push Docker image\n        id: push\n        uses: docker/build-push-action@f2a1d5e99d037542a71f64918e516c093c6f3fc4\n        with:\n          context: .\n          push: true\n          tags: ${{ steps.meta.outputs.tags }}\n          labels: ${{ steps.meta.outputs.labels }}\n      \n      # This step generates an artifact attestation for the image, which is an unforgeable statement about where and how it was built. It increases supply chain security for people who consume the image. For more information, see [Using artifact attestations to establish provenance for builds](/actions/security-guides/using-artifact-attestations-to-establish-provenance-for-builds).\n      - name: Generate artifact attestation\n        uses: actions/attest@v4\n        with:\n          subject-name: ${{ env.REGISTRY }}/${{ env.IMAGE_NAME}}\n          subject-digest: ${{ steps.push.outputs.digest }}\n          push-to-registry: true\n      \n```\n\nThe above workflow is triggered by a push to the \"release\" branch. It checks out the GitHub repository, and uses the `login-action` to log in to the Container registry. It then extracts labels and tags for the Docker image. Finally, it uses the `build-push-action` action to build the image and publish it on the Container registry.\n\n## Publishing images to Docker Hub and GitHub Packages\n\nIn a single workflow, you can publish your Docker image to multiple registries by using the `login-action` and `build-push-action` actions for each registry.\n\nThe following example workflow uses the steps from the previous sections ([Publishing images to Docker Hub](#publishing-images-to-docker-hub) and [Publishing images to GitHub Packages](#publishing-images-to-github-packages)) to create a single workflow that pushes to both registries.\n\n```yaml copy\n# This workflow uses actions that are not certified by GitHub.\n# They are provided by a third-party and are governed by\n# separate terms of service, privacy policy, and support\n# documentation.\n\n# GitHub recommends pinning actions to a commit SHA.\n# To get a newer version, you will need to update the SHA.\n# You can also reference a tag or branch, but the action may change without warning.\n\nname: Publish Docker image\n\non:\n  release:\n    types: [published]\n\njobs:\n  push_to_registries:\n    name: Push Docker image to multiple registries\n    runs-on: ubuntu-latest\n    permissions:\n      packages: write\n      contents: read\n    steps:\n      - name: Check out the repo\n        uses: actions/checkout@v6\n\n      - name: Log in to Docker Hub\n        uses: docker/login-action@f4ef78c080cd8ba55a85445d5b36e214a81df20a\n        with:\n          username: ${{ secrets.DOCKER_USERNAME }}\n          password: ${{ secrets.DOCKER_PASSWORD }}\n\n      - name: Log in to the Container registry\n        uses: docker/login-action@65b78e6e13532edd9afa3aa52ac7964289d1a9c1\n        with:\n          registry: ghcr.io\n          username: ${{ github.actor }}\n          password: ${{ secrets.GITHUB_TOKEN }}\n\n      - name: Extract metadata (tags, labels) for Docker\n        id: meta\n        uses: docker/metadata-action@9ec57ed1fcdbf14dcef7dfbe97b2010124a938b7\n        with:\n          images: |\n            my-docker-hub-namespace/my-docker-hub-repository\n            ghcr.io/${{ github.repository }}\n\n      - name: Build and push Docker images\n        id: push\n        uses: docker/build-push-action@3b5e8027fcad23fda98b2e3ac259d8d67585f671\n        with:\n          context: .\n          push: true\n          tags: ${{ steps.meta.outputs.tags }}\n          labels: ${{ steps.meta.outputs.labels }}\n```\n\nThe above workflow checks out the GitHub repository, uses the `login-action` twice to log in to both registries and generates tags and labels with the `metadata-action` action.\nThen the `build-push-action` action builds and pushes the Docker image to Docker Hub and the Container registry.\n\n> \\[!NOTE]\n> When pushing to multiple registries:\n>\n> * Image digests may differ between registries, making attestation verification difficult.\n> * To maintain a consistent digest and allow a single attestation to verify all copies, push to one registry first and use a tool like [`crane copy`](https://github.com/google/go-containerregistry/blob/main/cmd/crane/doc/crane_copy.md) to replicate the image elsewhere.\n> * If you choose to build and push to each registry separately instead, you must generate a distinct attestation for each one to ensure your artifacts remain verifiable.\n\n## Hands-on practice\n\nPractice publishing Docker images with the [Publishing Docker images](https://github.com/skills/publish-docker-images) GitHub Skills exercise.\n\nIn this exercise, you will learn how to:\n\n* Authenticate to GitHub Packages using the `GITHUB_TOKEN`.\n* Build and publish container images to the Container registry (`ghcr.io`).\n* Use official Docker actions, such as `docker/login-action`, `docker/build-push-action`, and `docker/setup-buildx-action`.\n* Generate tags automatically with `docker/metadata-action` based on branches, pull requests, and releases.\n* Create features, pull requests, and releases with proper container versioning."}