Build and push container images to the container registry
- Tier: Free, Premium, Ultimate
- Offering: GitLab.com, GitLab Self-Managed, GitLab Dedicated
Before you can build and push container images, you must authenticate with the container registry.
Use Docker commands
You can use Docker commands to build and push container images to your container registry:
Authenticate with the container registry.
Run the Docker command to build or push. For example:
To build:
docker build -t registry.example.com/group/project/image .To push:
docker push registry.example.com/group/project/image
Use GitLab CI/CD
Use GitLab CI/CD to build, push, test, and deploy container images from the container registry.
Configure your .gitlab-ci.yml file
You can configure your .gitlab-ci.yml file to build and push container images to the container registry.
If multiple jobs require authentication, put the authentication command in the
before_script.Before building, use
docker build --pullto fetch changes to base images. It takes slightly longer, but it ensures your image is up-to-date.Before each
docker run, do an explicitdocker pullto fetch the image that was just built. This step is especially important if you are using multiple runners that cache images locally.If you use the Git SHA in your image tag, each job is unique and you should never have a stale image. However, it’s still possible to have a stale image if you rebuild a given commit after a dependency has changed.
Don’t build directly to the
latesttag because multiple jobs may be happening simultaneously.
Use a Docker-in-Docker container image
You can use your own Docker-in-Docker (DinD) container images with the container registry or Dependency Proxy.
Use DinD to build, test, and deploy containerized applications from your CI/CD pipeline.
Prerequisites:
- Set up Docker-in-Docker.
Use this approach when you want to use images stored in your GitLab container registry.
In your .gitlab-ci.yml file:
- Update
imageandservicesto point to your registry. - Add a service alias.
Your .gitlab-ci.yml should look similar to this:
build:
image: $CI_REGISTRY/group/project/docker:24.0.5
services:
- name: $CI_REGISTRY/group/project/docker:24.0.5-dind
alias: docker
stage: build
script:
- docker build -t my-docker-image .
- docker run my-docker-image /script/to/run/testsUse this approach to cache images from external registries like Docker Hub for faster builds and to avoid rate limits.
In your .gitlab-ci.yml file:
- Update
imageandservicesto use the Dependency Proxy prefix. - Add a service alias.
Your .gitlab-ci.yml should look similar to this:
build:
image: ${CI_DEPENDENCY_PROXY_GROUP_IMAGE_PREFIX}/docker:24.0.5
services:
- name: ${CI_DEPENDENCY_PROXY_GROUP_IMAGE_PREFIX}/docker:24.0.5-dind
alias: docker
stage: build
script:
- docker build -t my-docker-image .
- docker run my-docker-image /script/to/run/testsIf you forget to set the service alias, the container image can’t find the dind service,
and an error like the following is shown:
error during connect: Get http://docker:2376/v1.39/info: dial tcp: lookup docker on 192.168.0.1:53: no such hostContainer registry examples with GitLab CI/CD
If you’re using DinD on your runners, your .gitlab-ci.yml file should look similar to this:
build:
image: docker:24.0.5
stage: build
services:
- docker:24.0.5-dind
script:
- echo "$CI_REGISTRY_PASSWORD" | docker login $CI_REGISTRY -u $CI_REGISTRY_USER --password-stdin
- docker build -t $CI_REGISTRY/group/project/image:latest .
- docker push $CI_REGISTRY/group/project/image:latestYou can use CI/CD variables in your .gitlab-ci.yml file. For example:
build:
image: docker:24.0.5
stage: build
services:
- docker:24.0.5-dind
variables:
IMAGE_TAG: $CI_REGISTRY_IMAGE:$CI_COMMIT_REF_SLUG
script:
- echo "$CI_REGISTRY_PASSWORD" | docker login $CI_REGISTRY -u $CI_REGISTRY_USER --password-stdin
- docker build -t $IMAGE_TAG .
- docker push $IMAGE_TAGIn the previous example:
$CI_REGISTRY_IMAGEresolves to the address of the registry tied to this project.$IMAGE_TAGis a custom variable that combines the registry address with$CI_COMMIT_REF_SLUG, the image tag. The$CI_COMMIT_REF_NAMEpredefined variable resolves to the branch or tag name and can contain forward slashes. Image tags cannot contain forward slashes. Use$CI_COMMIT_REF_SLUGinstead.
The following example splits CI/CD tasks into four pipeline stages, including two tests that run in parallel.
The build is stored in the container registry and used by subsequent stages that download the container image when needed. When you push changes to the main branch, the pipeline tags the image as latest and deploys it using an application-specific deploy script:
default:
image: docker:24.0.5
services:
- docker:24.0.5-dind
before_script:
- echo "$CI_REGISTRY_PASSWORD" | docker login $CI_REGISTRY -u $CI_REGISTRY_USER --password-stdin
stages:
- build
- test
- release
- deploy
variables:
# Use TLS https://docs.gitlab.com/ee/ci/docker/using_docker_build.html#tls-enabled
DOCKER_HOST: tcp://docker:2376
DOCKER_TLS_CERTDIR: "/certs"
CONTAINER_TEST_IMAGE: $CI_REGISTRY_IMAGE:$CI_COMMIT_REF_SLUG
CONTAINER_RELEASE_IMAGE: $CI_REGISTRY_IMAGE:latest
build:
stage: build
script:
- docker build --pull -t $CONTAINER_TEST_IMAGE .
- docker push $CONTAINER_TEST_IMAGE
test1:
stage: test
script:
- docker pull $CONTAINER_TEST_IMAGE
- docker run $CONTAINER_TEST_IMAGE /script/to/run/tests
test2:
stage: test
script:
- docker pull $CONTAINER_TEST_IMAGE
- docker run $CONTAINER_TEST_IMAGE /script/to/run/another/test
release-image:
stage: release
script:
- docker pull $CONTAINER_TEST_IMAGE
- docker tag $CONTAINER_TEST_IMAGE $CONTAINER_RELEASE_IMAGE
- docker push $CONTAINER_RELEASE_IMAGE
rules:
- if: $CI_COMMIT_BRANCH == "main"
deploy:
stage: deploy
script:
- ./deploy.sh
rules:
- if: $CI_COMMIT_BRANCH == "main"
environment: productionThe previous example explicitly calls docker pull. If you prefer to implicitly pull the container image using image:,
and use either the Docker or Kubernetes executor,
make sure that pull_policy is set to always.