|
1 | | ---- |
2 | | -title: Working with the Container registry |
3 | | -intro: 'You can store and manage Docker and OCI images in the {% data variables.product.prodname_container_registry %}.' |
4 | | -product: '{% data reusables.gated-features.packages %}' |
5 | | -redirect_from: |
6 | | - - /packages/managing-container-images-with-github-container-registry/pushing-and-pulling-docker-images |
7 | | - - /packages/guides/container-guides-for-github-packages/pushing-and-pulling-docker-images |
8 | | - - /packages/guides/pushing-and-pulling-docker-images |
9 | | - - /packages/getting-started-with-github-container-registry/about-github-container-registry |
10 | | - - /packages/managing-container-images-with-github-container-registry |
11 | | - - /packages/working-with-a-github-packages-registry/enabling-improved-container-support-with-the-container-registry |
12 | | - - /packages/getting-started-with-github-container-registry/enabling-improved-container-support |
13 | | - - /packages/guides/container-guides-for-github-packages/enabling-improved-container-support |
14 | | - - /packages/guides/enabling-improved-container-support |
15 | | -versions: |
16 | | - fpt: '*' |
17 | | - ghec: '*' |
18 | | - ghes: '*' |
19 | | -shortTitle: Container registry |
20 | | -category: |
21 | | - - Work with a package registry |
22 | | ---- |
| 1 | +## CI/CD tagging strategy for multi-environment deployments |
23 | 2 |
|
24 | | -{% data reusables.package_registry.container-registry-ghes-beta %} |
| 3 | +A consistent tagging strategy helps trace container images back to their source commits, simplify environment promotion, and improve rollback reliability. |
25 | 4 |
|
26 | | -## About the {% data variables.product.prodname_container_registry %} |
| 5 | +### Recommended tag scheme |
27 | 6 |
|
28 | | -{% data reusables.package_registry.container-registry-benefits %} |
| 7 | +Apply multiple tags to the same image digest during each build so deployments can reference images by stability level or exact provenance. |
29 | 8 |
|
30 | | -{% ifversion ghes %} |
| 9 | +| Tag pattern | Example | Purpose | |
| 10 | +|---|---|---| |
| 11 | +| Commit SHA (short) | `sha-a1b2c3d` | Immutable reference suitable for production rollbacks | |
| 12 | +| Branch name | `main`, `release-1.4` | Mutable tag that tracks the latest build for a branch | |
| 13 | +| Semantic version | `v1.4.2` | Human-readable release version | |
| 14 | +| Environment label | `staging`, `production` | Alias used during environment promotion workflows | |
31 | 15 |
|
32 | | -To use the {% data variables.product.prodname_container_registry %} on {% data variables.product.prodname_ghe_server %}, your site administrator must first configure {% data variables.product.prodname_registry %} for your instance **and** enable subdomain isolation. For more information, see [AUTOTITLE](/admin/packages/getting-started-with-github-packages-for-your-enterprise) and [AUTOTITLE](/admin/configuration/configuring-network-settings/enabling-subdomain-isolation). |
| 16 | +> [!NOTE] |
| 17 | +> Avoid relying solely on the `latest` tag in automated deployment pipelines. Because `latest` is mutable, it does not provide reliable traceability between deployments and source revisions. |
33 | 18 |
|
34 | | -{% endif %} |
| 19 | +### Applying multiple tags in a GitHub Actions workflow |
35 | 20 |
|
36 | | -{% ifversion ghec %} |
| 21 | +Use the `docker/metadata-action` action to generate image tags automatically from Git context and pass them to `docker/build-push-action`. |
37 | 22 |
|
38 | | -## URL for the {% data variables.product.prodname_container_registry %} |
39 | | - |
40 | | -If you access {% data variables.product.github %} at {% data variables.product.prodname_dotcom_the_website %}, you will publish packages to {% data reusables.package_registry.container-registry-hostname %}. Examples in this article use this URL. |
41 | | - |
42 | | -If you access {% data variables.product.github %} at another domain, such as `octocorp.ghe.com`, replace "{% data reusables.package_registry.container-registry-hostname %}" with `https://containers.SUBDOMAIN.ghe.com`, where `SUBDOMAIN` is your enterprise's unique subdomain. |
43 | | - |
44 | | -{% endif %} |
45 | | - |
46 | | -## About {% data variables.product.prodname_container_registry %} support |
47 | | - |
48 | | -The {% data variables.product.prodname_container_registry %} currently supports the following container image formats: |
49 | | - |
50 | | -* [Docker Image Manifest V2, Schema 2](https://docs.docker.com/registry/spec/manifest-v2-2/) |
51 | | -* [Open Container Initiative (OCI) Specifications](https://github.com/opencontainers/image-spec) |
52 | | - |
53 | | -When installing or publishing a Docker image, the {% data variables.product.prodname_container_registry %} supports foreign layers, such as Windows images. |
54 | | - |
55 | | -## Authenticating to the {% data variables.product.prodname_container_registry %} |
56 | | - |
57 | | -{% data reusables.package_registry.authenticate-packages %} |
58 | | - |
59 | | -### Authenticating in a {% data variables.product.prodname_actions %} workflow |
60 | | - |
61 | | -This registry supports granular permissions. {% data reusables.package_registry.authenticate_with_pat_for_v2_registry %} |
62 | | - |
63 | | -{% data reusables.package_registry.v2-actions-codespaces %} |
64 | | - |
65 | | -### Authenticating with a {% data variables.product.pat_v1 %} |
66 | | - |
67 | | -{% ifversion ghes %}Ensure that you replace `HOSTNAME` with {% data variables.location.product_location_enterprise %} hostname or IP address in the examples below.{% endif %} |
68 | | - |
69 | | -{% data reusables.package_registry.authenticate-to-container-registry-steps %} |
70 | | - |
71 | | -## Pushing container images |
72 | | - |
73 | | -This example pushes the latest version of `IMAGE_NAME`. |
74 | | - |
75 | | -```shell |
76 | | -docker push {% data reusables.package_registry.container-registry-hostname %}/NAMESPACE/IMAGE_NAME:latest |
77 | | -``` |
78 | | - |
79 | | -Replace `NAMESPACE` with the name of the personal account or organization to which you want the image to be scoped. |
80 | | - |
81 | | -This example pushes the `2.5` version of the image. |
82 | | - |
83 | | -```shell |
84 | | -docker push {% data reusables.package_registry.container-registry-hostname %}/NAMESPACE/IMAGE_NAME:2.5 |
85 | | -``` |
86 | | - |
87 | | -{% data reusables.package_registry.publishing-user-scoped-packages %} You can link a published package to a repository using the user interface or command line. For more information, see [AUTOTITLE](/packages/learn-github-packages/connecting-a-repository-to-a-package). |
88 | | - |
89 | | -When you push a container image from the command line, the image is not linked to a repository by default. This is the case even if you tag the image with a namespace that matches the name of the repository, such as `{% ifversion fpt or ghec %}ghcr.io{% elsif ghes %}{% data reusables.package_registry.container-registry-example-hostname %}{% endif %}/octocat/my-repo:latest`. |
90 | | - |
91 | | -The easiest way to connect a repository to a container package is to publish the package from a workflow using `${% raw %}{{secrets.GITHUB_TOKEN}}{% endraw %}`, as the repository that contains the workflow is linked automatically. Note that the `GITHUB_TOKEN` will not have permission to push the package if you have previously pushed a package to the same namespace, but have not connected the package to the repository. |
92 | | - |
93 | | -To connect a repository when publishing an image from the command line, and to ensure your `GITHUB_TOKEN` has appropriate permissions when using a GitHub Actions workflow, we recommend adding the label `org.opencontainers.image.source` to your `Dockerfile`. For more information, see “[Labelling container images](#labelling-container-images)” in this article and “[AUTOTITLE](/packages/managing-github-packages-using-github-actions-workflows/publishing-and-installing-a-package-with-github-actions).” |
94 | | - |
95 | | -## Pulling container images |
96 | | - |
97 | | -### Pull by digest |
98 | | - |
99 | | -To ensure you're always using the same image, you can specify the exact container image version you want to pull by the `digest` SHA value. |
100 | | - |
101 | | -1. To find the digest SHA value, use `docker inspect` or `docker pull` and copy the SHA value after `Digest:` |
102 | | - |
103 | | - ```shell |
104 | | - docker inspect {% data reusables.package_registry.container-registry-hostname %}/NAMESPACE/IMAGE_NAME |
105 | | - ``` |
106 | | - |
107 | | - Replace `NAMESPACE` with the name of the personal account or organization to which the image is scoped. |
108 | | -1. Remove image locally as needed. |
109 | | - |
110 | | - ```shell |
111 | | - docker rmi {% data reusables.package_registry.container-registry-hostname %}/NAMESPACE/IMAGE_NAME:latest |
112 | | - ``` |
113 | | - |
114 | | -1. Pull the container image with `@YOUR_SHA_VALUE` after the image name. |
115 | | - |
116 | | - ```shell |
117 | | - docker pull {% data reusables.package_registry.container-registry-hostname %}/NAMESPACE/IMAGE_NAME@sha256:82jf9a84u29hiasldj289498uhois8498hjs29hkuhs |
118 | | - ``` |
119 | | - |
120 | | -### Pull by name |
121 | | - |
122 | | -```shell |
123 | | -docker pull {% data reusables.package_registry.container-registry-hostname %}/NAMESPACE/IMAGE_NAME |
| 23 | +```yaml |
| 24 | +jobs: |
| 25 | + build-and-push: |
| 26 | + runs-on: ubuntu-latest |
| 27 | + |
| 28 | + permissions: |
| 29 | + contents: read |
| 30 | + packages: write |
| 31 | + |
| 32 | + steps: |
| 33 | + - name: Check out repository |
| 34 | + uses: actions/checkout@v4 |
| 35 | + |
| 36 | + - name: Log in to the Container registry |
| 37 | + uses: docker/login-action@v3 |
| 38 | + with: |
| 39 | + registry: ghcr.io |
| 40 | + username: ${{ github.actor }} |
| 41 | + password: ${{ secrets.GITHUB_TOKEN }} |
| 42 | + |
| 43 | + - name: Extract Docker metadata |
| 44 | + id: meta |
| 45 | + uses: docker/metadata-action@v5 |
| 46 | + with: |
| 47 | + images: ghcr.io/${{ github.repository }} |
| 48 | + tags: | |
| 49 | + type=sha,prefix=sha-,format=short |
| 50 | + type=ref,event=branch |
| 51 | + type=semver,pattern={{version}} |
| 52 | + type=raw,value=staging,enable=${{ github.ref == 'refs/heads/main' }} |
| 53 | +
|
| 54 | + - name: Build and push Docker image |
| 55 | + uses: docker/build-push-action@v5 |
| 56 | + with: |
| 57 | + context: . |
| 58 | + push: true |
| 59 | + tags: ${{ steps.meta.outputs.tags }} |
| 60 | + labels: ${{ steps.meta.outputs.labels }} |
124 | 61 | ``` |
125 | 62 |
|
126 | | -Replace `NAMESPACE` with the name of the personal account or organization to which the image is scoped. |
| 63 | +All tags generated during the workflow reference the same image digest. This allows deployments to promote existing images between environments without rebuilding the container image. |
127 | 64 |
|
128 | | -### Pull by name and version |
| 65 | +### Verifying tag-to-digest traceability |
129 | 66 |
|
130 | | -Docker CLI example showing an image pulled by its name and the `1.14.1` version tag: |
| 67 | +After pushing an image, you can verify that multiple tags reference the same digest. |
131 | 68 |
|
132 | | -```shell |
133 | | -$ docker pull {% data reusables.package_registry.container-registry-hostname %}/NAMESPACE/IMAGE_NAME:1.14.1 |
134 | | -> 5e35bd43cf78: Pull complete |
135 | | -> 0c48c2209aab: Pull complete |
136 | | -> fd45dd1aad5a: Pull complete |
137 | | -> db6eb50c2d36: Pull complete |
138 | | -> Digest: sha256:ae3b135f133155b3824d8b1f62959ff8a72e9cf9e884d88db7895d8544010d8e |
139 | | -> Status: Downloaded newer image for {% data reusables.package_registry.container-registry-hostname %}/NAMESPACE/IMAGE_NAME/release:1.14.1 |
140 | | -> {% data reusables.package_registry.container-registry-hostname %}/NAMESPACE/IMAGE_NAME/release:1.14.1 |
| 69 | +```bash |
| 70 | +docker buildx imagetools inspect ghcr.io/NAMESPACE/IMAGE_NAME:staging |
| 71 | +docker buildx imagetools inspect ghcr.io/NAMESPACE/IMAGE_NAME:sha-a1b2c3d |
141 | 72 | ``` |
142 | 73 |
|
143 | 74 | Replace `NAMESPACE` with the name of the personal account or organization to which the image is scoped. |
144 | 75 |
|
145 | | -### Pull by name and latest version |
146 | | - |
147 | | -```shell |
148 | | -$ docker pull {% data reusables.package_registry.container-registry-hostname %}/NAMESPACE/IMAGE_NAME:latest |
149 | | -> latest: Pulling from NAMESPACE/IMAGE_NAME |
150 | | -> Digest: sha256:b3d3e366b55f9a54599220198b3db5da8f53592acbbb7dc7e4e9878762fc5344 |
151 | | -> Status: Downloaded newer image for {% data reusables.package_registry.container-registry-hostname %}/NAMESPACE/IMAGE_NAME:latest |
152 | | -> {% data reusables.package_registry.container-registry-hostname %}/NAMESPACE/IMAGE_NAME:latest |
153 | | -``` |
154 | | - |
155 | | -Replace `NAMESPACE` with the name of the personal account or organization to which the image is scoped. |
156 | | - |
157 | | -## Building container images |
158 | | - |
159 | | -This example builds the `hello_docker` image: |
160 | | - |
161 | | -```shell |
162 | | -docker build -t hello_docker . |
163 | | -``` |
164 | | - |
165 | | -## Tagging container images |
166 | | - |
167 | | -1. Find the ID for the Docker image you want to tag. |
168 | | - |
169 | | - ```shell |
170 | | - $ docker images |
171 | | - > REPOSITORY TAG IMAGE ID CREATED SIZE |
172 | | - > {% data reusables.package_registry.container-registry-hostname %}/my-org/hello_docker latest 38f737a91f39 47 hours ago 91.7MB |
173 | | - > hello-world latest fce289e99eb9 16 months ago 1.84kB |
174 | | - ``` |
175 | | - |
176 | | -1. Tag your Docker image using the image ID and your desired image name and hosting destination. |
177 | | - |
178 | | - ```shell |
179 | | - docker tag 38f737a91f39 {% data reusables.package_registry.container-registry-hostname %}/NAMESPACE/NEW_IMAGE_NAME:latest |
180 | | - ``` |
181 | | - |
182 | | -Replace `NAMESPACE` with the name of the personal account or organization to which you want the image to be scoped. |
183 | | - |
184 | | -## Labelling container images |
185 | | - |
186 | | -{% data reusables.package_registry.about-annotation-keys %} Values for supported keys will appear on the package page for the image. |
187 | | - |
188 | | -For most images, you can use Docker labels to add the annotation keys to an image. For more information, see [LABEL](https://docs.docker.com/engine/reference/builder/#label) in the official Docker documentation and [Pre-Defined Annotation Keys](https://github.com/opencontainers/image-spec/blob/main/annotations.md#pre-defined-annotation-keys) in the `opencontainers/image-spec` repository. |
189 | | - |
190 | | -For multi-arch images, you can add a description to the image by adding the appropriate annotation key to the `annotations` field in the image's manifest. For more information, see [Adding a description to multi-arch images](#adding-a-description-to-multi-arch-images). |
191 | | - |
192 | | -The following annotation keys are supported in the {% data variables.product.prodname_container_registry %}. |
193 | | - |
194 | | -Key | Description |
195 | | -------|------------ |
196 | | -| `org.opencontainers.image.source` | The URL of the repository associated with the package. For more information, see [AUTOTITLE](/packages/learn-github-packages/connecting-a-repository-to-a-package#connecting-a-repository-to-a-container-image-using-the-command-line). |
197 | | -| `org.opencontainers.image.description` | A text-only description limited to 512 characters. This description will appear on the package page, below the name of the package. |
198 | | -| `org.opencontainers.image.licenses` | An SPDX license identifier such as "MIT," limited to 256 characters. The license will appear on the package page, in the "Details" sidebar. For more information, see [SPDX License List](https://spdx.org/licenses/). |
199 | | - |
200 | | -To add a key as a Docker label, we recommend using the `LABEL` instruction in your `Dockerfile`. For example, if you're the user `octocat` and you own `my-repo`, and your image is distributed under the terms of the MIT license, you would add the following lines to your `Dockerfile`: |
201 | | - |
202 | | -```dockerfile |
203 | | -LABEL org.opencontainers.image.source=https://{% ifversion fpt or ghec %}github.com{% else %}HOSTNAME{% endif %}/octocat/my-repo |
204 | | -LABEL org.opencontainers.image.description="My container image" |
205 | | -LABEL org.opencontainers.image.licenses=MIT |
206 | | -``` |
207 | | - |
208 | | -{% data reusables.package_registry.auto-inherit-permissions-note %} |
209 | | - |
210 | | -Alternatively, you can add labels to an image at buildtime with the `docker build` command. |
211 | | - |
212 | | -```shell |
213 | | -$ docker build \ |
214 | | - --label "org.opencontainers.image.source=https://{% ifversion fpt or ghec %}github.com{% else %}HOSTNAME{% endif %}/octocat/my-repo" \ |
215 | | - --label "org.opencontainers.image.description=My container image" \ |
216 | | - --label "org.opencontainers.image.licenses=MIT" |
217 | | -``` |
218 | | - |
219 | | -### Adding a description to multi-arch images |
220 | | - |
221 | | -A multi-arch image is an image that supports multiple architectures. It works by referencing a list of images, each supporting a different architecture, within a single manifest. |
222 | | - |
223 | | -The description that appears on the package page for a multi-arch image is obtained from the `annotations` field in the image's manifest. Like Docker labels, annotations provide a way to associate metadata with an image, and support pre-defined annotation keys. For more information, see [Annotations](https://github.com/opencontainers/image-spec/blob/main/annotations.md) in the `opencontainers/image-spec` repository. |
224 | | - |
225 | | -To provide a description for a multi-arch image, set a value for the `org.opencontainers.image.description` key in the `annotations` field of the manifest, as follows. |
226 | | - |
227 | | -```json |
228 | | -"annotations": { |
229 | | - "org.opencontainers.image.description": "My multi-arch image" |
230 | | -} |
231 | | -``` |
232 | | - |
233 | | -For example, the following {% data variables.product.prodname_actions %} workflow step builds and pushes a multi-arch image. The `outputs` parameter sets the description for the image. |
234 | | - |
235 | | -```yaml |
236 | | -{% data reusables.actions.actions-not-certified-by-github-comment %} |
237 | | - |
238 | | -- name: Build and push Docker image |
239 | | - uses: docker/build-push-action@f2a1d5e99d037542a71f64918e516c093c6f3fc4 |
240 | | - with: |
241 | | - context: . |
242 | | - file: ./Dockerfile |
243 | | - platforms: {% raw %}${{ matrix.platforms }}{% endraw %} |
244 | | - push: true |
245 | | - outputs: type=image,name=target,annotation-index.org.opencontainers.image.description=My multi-arch image |
246 | | -``` |
247 | | -
|
248 | | -## Troubleshooting |
249 | | -
|
250 | | -* The {% data variables.product.prodname_container_registry %} has a 10 GB size limit for each layer. |
251 | | -* The {% data variables.product.prodname_container_registry %} has a 10 minute timeout limit for uploads. |
| 76 | +Both commands should report the same `Digest` value. If the digest values differ, the tags reference different image versions. |
0 commit comments