Here’s a complete step-by-step guide for:
✅ Building a Docker image
✅ Storing (pushing) it to GitLab’s Container Registry
✅ Using GitLab CI/CD pipeline with GitLab SaaS 18.x
🚀 Use Case
You want to:
- Build a Docker image from your code
- Push it to GitLab’s Container Registry
- Use it later for deployment or as base image
🧱 Prerequisites
- A GitLab project on GitLab.com (SaaS)
- GitLab CI/CD enabled
.gitlab-ci.yml
file created- Dockerfile at the root of your project
📝 Step 1: Create a Dockerfile
Here’s an example Dockerfile
for a Java app:
# syntax=docker/dockerfile:1
FROM openjdk:17-jdk-slim
COPY ./target/myapp.jar /app/myapp.jar
CMD ["java", "-jar", "/app/myapp.jar"]
Code language: PHP (php)
Make sure your JAR is built to ./target/myapp.jar
via Maven or Gradle.
📦 Step 2: Understand Your GitLab Container Registry URL
GitLab automatically enables a private container registry for every project:
registry.gitlab.com/<namespace>/<project>
Code language: HTML, XML (xml)
📌 Example:
registry.gitlab.com/yourusername/myapp
You can view it in:
Project → Deploy → Container Registry
- ✅
<namespace>
can be:- A personal username (
yourusername
) - A GitLab group (
yourgroup
) - A GitLab subgroup path (
yourgroup/subgroup
)
- A personal username (
- ✅
<project>
is always the project name
⚙️ Step 3: Configure .gitlab-ci.yml
Here’s a full working example:
stages:
- build
- dockerize
- push
variables:
IMAGE_TAG: $CI_REGISTRY_IMAGE:$CI_COMMIT_SHORT_SHA
# Build your app (optional if using compiled files)
build-job:
stage: build
image: maven:3.8.7-openjdk-17
script:
- mvn clean package -DskipTests
dockerize-job:
stage: dockerize
image: docker:latest
services:
- docker:dind
before_script:
- echo $CI_JOB_TOKEN | docker login -u gitlab-ci-token --password-stdin $CI_REGISTRY
script:
- docker build -t $IMAGE_TAG .
- docker push $IMAGE_TAG
only:
- main
Code language: PHP (php)
🧠 Explanation of Important Variables
Variable | Purpose |
---|---|
$CI_REGISTRY | Points to registry.gitlab.com |
$CI_REGISTRY_IMAGE | Your project-specific Docker registry path |
$CI_JOB_TOKEN | GitLab’s built-in token for authenticating CI jobs |
gitlab-ci-token | Required user for registry auth inside CI/CD |
🔐 Step 4: CI/CD Authentication
GitLab uses:
- Username:
gitlab-ci-token
- Password:
$CI_JOB_TOKEN
Docker login is done via:
echo $CI_JOB_TOKEN | docker login -u gitlab-ci-token --password-stdin $CI_REGISTRY
Code language: PHP (php)
🔍 Step 5: View Container in Registry
After pushing:
- Go to your GitLab project
- Navigate to Deploy > Container Registry
- You’ll see the image like:
registry.gitlab.com/yourusername/myapp:abcdef1
🛠️ Step 6: Pull and Use the Image Anywhere
From any machine (with Docker installed):
docker login registry.gitlab.com
docker pull registry.gitlab.com/yourusername/myapp:<tag>
Code language: HTML, XML (xml)
🔁 Optional: Use latest
Tag and Versioning
variables:
IMAGE_TAG: $CI_REGISTRY_IMAGE:latest
Code language: PHP (php)
Or for versioning:
IMAGE_TAG: $CI_REGISTRY_IMAGE:${CI_COMMIT_REF_NAME//\//-}
Code language: PHP (php)
✅ Summary
Step | Description |
---|---|
1 | Write a Dockerfile |
2 | Identify your GitLab container registry path |
3 | Configure .gitlab-ci.yml to build & push |
4 | Use docker login with CI_JOB_TOKEN |
5 | View in Deploy → Container Registry |
Great observation! The line:
services:
- docker:dind
Code language: CSS (css)
is critical when using Docker inside GitLab CI/CD pipelines — especially when building or pushing Docker images.
🔍 What is docker:dind
?
**dind**
stands for Docker-in-Docker.docker:dind
is a Docker image that runs the Docker daemon as a background service inside your CI/CD job.- This allows the main job container to use Docker commands (
docker build
,docker push
, etc.), even though it’s running in an isolated environment.
🔧 Why is It Needed?
GitLab CI/CD runs your job steps inside a container (e.g., docker:latest
), which doesn’t have the Docker daemon running by default.
So you need to add:
services:
- docker:dind
Code language: CSS (css)
This launches a background Docker daemon, which your main container can talk to (usually via the socket on localhost
or Docker host networking).
🧪 Example Use in Context
dockerize-job:
stage: dockerize
image: docker:latest # Main container with Docker CLI
services:
- docker:dind # Side container with Docker daemon
script:
- docker build -t $IMAGE_TAG .
- docker push $IMAGE_TAG
Code language: PHP (php)
docker:latest
→ contains the Docker CLIdocker:dind
→ provides the Docker daemon to executedocker
commands
🔐 Important: Secure Docker-in-Docker
For security and performance:
- GitLab runs Docker-in-Docker in privileged mode automatically when using their Docker executor.
- You may explicitly add (if self-hosted):
tags:
- docker
variables:
DOCKER_TLS_CERTDIR: ""
Code language: JavaScript (javascript)
This disables TLS cert enforcement (GitLab-specific tweak for dind
).
⚠️ Alternatives to docker:dind
- Kaniko: A rootless, safer Docker image builder (good for Kubernetes-based runners)
- Buildah + Podman: For more secure container builds without daemon
- GitLab CI with remote Docker host
✅ Summary
docker:dind | Explanation |
---|---|
Docker-in-Docker | Runs Docker daemon inside CI/CD service |
Required for | docker build , docker push , etc. in pipeline |
Used alongside | Docker CLI image (docker:latest ) |
Daemon runs in | Background, accessible to main job container |
I’m a DevOps/SRE/DevSecOps/Cloud Expert passionate about sharing knowledge and experiences. I have worked at Cotocus. I share tech blog at DevOps School, travel stories at Holiday Landmark, stock market tips at Stocks Mantra, health and fitness guidance at My Medic Plus, product reviews at TrueReviewNow , and SEO strategies at Wizbrand.
Do you want to learn Quantum Computing?
Please find my social handles as below;
Rajesh Kumar Personal Website
Rajesh Kumar at YOUTUBE
Rajesh Kumar at INSTAGRAM
Rajesh Kumar at X
Rajesh Kumar at FACEBOOK
Rajesh Kumar at LINKEDIN
Rajesh Kumar at WIZBRAND