Docker Build
The docker-build jobs are used to build Docker container images using Docker-in-Docker (DinD) or Docker Compose. These jobs provide different build strategies for various use cases.
Overview
The docker-build jobs include:
.docker-build: Standard Docker build using Docker-in-Docker.docker-build-vm-setup: Docker build with VM setup that extracts files from the container.docker-compose-build: Docker Compose build for multi-container applications.docker-build-multi: Build multiple images from a directory structure
Variables
The following variables can be configured:
| Variable | Description | Default | Required |
|---|---|---|---|
DOCKER_BUILD_IMAGE | Docker image to use for builds | 'docker:27' | No |
DOCKER_BUILD_SERVICE | Docker-in-Docker service version | '27-dind' | No |
DOCKER_COMPOSE_IMAGE | Docker Compose image | 'docker:27' | No |
DOCKER_COMPOSE_SERVICE | Docker Compose service version | '27-dind' | No |
APP_ENVIRONMENT | Target environment (develop, staging, production) | - | Yes |
STORAGE_USER_ID | User ID for storage permissions | - | Yes |
VERSION | Image version tag (auto-generated for develop) | - | No |
GitLab CI/CD Variables
The jobs use the following built-in GitLab CI/CD variables:
CI_REGISTRY_USER: GitLab registry usernameCI_REGISTRY_PASSWORD: GitLab registry passwordCI_REGISTRY: GitLab container registry URLCI_REGISTRY_IMAGE: The address of the container registry tied to the projectCI_COMMIT_REF_NAME: Branch or tag nameCI_PIPELINE_ID: Unique ID of the current pipelineCI_COMMIT_SHORT_SHA: First 8 characters of the commit revisionCI_PROJECT_NAME: Project name
Job Types
.docker-build
Standard Docker build job that builds and pushes a single Docker image.
Usage
variables:
DOCKER_BUILD_IMAGE: 'docker:27'
DOCKER_BUILD_SERVICE: '27-dind'
APP_ENVIRONMENT: 'develop'
STORAGE_USER_ID: '1000'
.docker-build:
image: $DOCKER_BUILD_IMAGE
services:
- docker:$DOCKER_BUILD_SERVICE
before_script:
- docker login -u $CI_REGISTRY_USER -p $CI_REGISTRY_PASSWORD $CI_REGISTRY
script:
- |-
if [[ $APP_ENVIRONMENT = 'develop' ]]; then
export VERSION=$CI_COMMIT_REF_NAME-$CI_PIPELINE_ID
echo "Application image = $CI_REGISTRY_IMAGE:${VERSION}"
fi
if [[ $APP_ENVIRONMENT = 'staging' ]]; then
echo "Application image = $CI_REGISTRY_IMAGE:${VERSION}"
fi
if [[ $APP_ENVIRONMENT = 'production' ]]; then
echo "Application image = $CI_REGISTRY_IMAGE:${VERSION}"
fi
export CONTAINER_IMAGE=$CI_REGISTRY_IMAGE:$VERSION
docker build --build-arg APP_UID=$STORAGE_USER_ID --build-arg ENVIRONMENT=$APP_ENVIRONMENT -t ${CONTAINER_IMAGE} .
echo "pushing image ${CONTAINER_IMAGE}"
docker push ${CONTAINER_IMAGE}
echo "pushed image ${CONTAINER_IMAGE}"
echo "VERSION=$VERSION" > .env.version
cat .env.version
Version Tagging
- develop:
{CI_COMMIT_REF_NAME}-{CI_PIPELINE_ID}(e.g.,main-12345) - staging: Uses
$VERSIONvariable (must be set) - production: Uses
$VERSIONvariable (must be set)
Build Arguments
APP_UID: Set to$STORAGE_USER_IDfor file permissionsENVIRONMENT: Set to$APP_ENVIRONMENT
Output
Creates a .env.version file containing the VERSION variable for use in subsequent jobs.
.docker-build-vm-setup
Docker build job that extracts files from the built container for VM deployment.
Usage
.docker-build-vm-setup:
image: $DOCKER_BUILD_IMAGE
services:
- docker:$DOCKER_BUILD_SERVICE
before_script:
- docker login -u $CI_REGISTRY_USER -p $CI_REGISTRY_PASSWORD $CI_REGISTRY
script:
- |-
if [[ $APP_ENVIRONMENT = 'develop' ]]; then
export VERSION=$CI_COMMIT_REF_NAME-$CI_PIPELINE_ID
echo "[DEVELOP] Application image = $CI_REGISTRY_IMAGE:${VERSION}"
fi
if [[ $APP_ENVIRONMENT = 'staging' ]]; then
source .env.version
echo "[STAGING] Application image = $CI_REGISTRY_IMAGE:${VERSION}"
fi
if [[ $APP_ENVIRONMENT = 'production' ]]; then
source .env.version
echo "[PRODUCTION] Application image = $CI_REGISTRY_IMAGE:${VERSION}"
fi
docker run --name deploy -d $CI_REGISTRY_IMAGE:$VERSION sleep infinity
mkdir -p dockerimage && docker cp deploy:/app ./dockerimage/
Differences from .docker-build
- For staging/production, it sources
.env.versionfrom a previous job - Runs the container and extracts files from
/appto./dockerimage/ - Used for VM-based deployments where you need the application files
.docker-compose-build
Docker Compose build job for multi-container applications.
Usage
variables:
DOCKER_COMPOSE_IMAGE: 'docker:27'
DOCKER_COMPOSE_SERVICE: '27-dind'
.docker-compose-build:
image: $DOCKER_COMPOSE_IMAGE
services:
- name: docker:$DOCKER_COMPOSE_SERVICE
command: ["--tls=false"]
variables:
DOCKER_TLS_CERTDIR: ""
DOCKER_HOST: tcp://docker:2375
before_script:
- docker login -u "$CI_REGISTRY_USER" -p "$CI_REGISTRY_PASSWORD" "$CI_REGISTRY"
script: |
if [[ $APP_ENVIRONMENT = 'develop' ]]; then
export VERSION=$CI_COMMIT_REF_NAME-$CI_PIPELINE_ID
echo "Application image = $CI_REGISTRY_IMAGE:${VERSION}"
cp .env.dev .env
fi
if [[ $APP_ENVIRONMENT = 'staging' ]]; then
echo "Application image = $CI_REGISTRY_IMAGE:${VERSION}"
cp .env.staging .env
fi
if [[ $APP_ENVIRONMENT = 'production' ]]; then
echo "Application image = $CI_REGISTRY_IMAGE:${VERSION}"
cp .env.production .env
fi
export CONTAINER_IMAGE=$CI_REGISTRY_IMAGE:$VERSION
# use the plugin:
docker compose build --build-arg APP_UID="$STORAGE_USER_ID" app
docker tag ${CI_PROJECT_NAME}-app:latest ${CONTAINER_IMAGE}
docker push "${CONTAINER_IMAGE}"
rm .env
echo "pushed image ${CONTAINER_IMAGE}"
echo "VERSION=$VERSION" > .env.version
cat .env.version
Features
- Uses Docker Compose to build services defined in
docker-compose.yml - Automatically copies environment-specific
.envfiles (.env.dev,.env.staging,.env.production) - Builds the
appservice with build arguments - Tags and pushes the built image
- Disables TLS for Docker-in-Docker communication
Environment Files
The job automatically selects the appropriate environment file:
- develop: Copies
.env.devto.env - staging: Copies
.env.stagingto.env - production: Copies
.env.productionto.env
.docker-build-multi
Builds multiple Docker images from a directory structure.
Usage
.docker-build-multi:
image: $DOCKER_BUILD_IMAGE
services:
- docker:$DOCKER_BUILD_SERVICE
variables:
IMAGES_ROOT: "images"
IMAGES: ""
before_script:
- docker login -u "$CI_REGISTRY_USER" -p "$CI_REGISTRY_PASSWORD" "$CI_REGISTRY"
script: |
# ... (see full script in the job definition)
Variables
- IMAGES_ROOT: Root folder where per-image directories live (default:
"images") - IMAGES: Space-separated list of image folders to build. If empty, builds ALL subdirectories in
IMAGES_ROOT
Behavior
- Auto-detection: If
IMAGESis empty, automatically detects all subdirectories inIMAGES_ROOT - Selective build: If
IMAGESis specified, only builds those directories - Version tagging: Uses the same versioning logic as
.docker-build - Image naming: Images are tagged as
{CI_REGISTRY_IMAGE}/{image-name}:{VERSION}
Example Directory Structure
images/
├── api/
│ └── Dockerfile
├── worker/
│ └── Dockerfile
└── frontend/
└── Dockerfile
This would build three images:
{CI_REGISTRY_IMAGE}/api:{VERSION}{CI_REGISTRY_IMAGE}/worker:{VERSION}{CI_REGISTRY_IMAGE}/frontend:{VERSION}
Usage Examples
Build all images:
build-all:
extends: .docker-build-multi
variables:
IMAGES_ROOT: "images"
IMAGES: "" # Empty = build all
Build specific images:
build-selected:
extends: .docker-build-multi
variables:
IMAGES_ROOT: "images"
IMAGES: "api frontend" # Only build api and frontend
Complete Pipeline Example
variables:
DOCKER_BUILD_IMAGE: 'docker:27'
DOCKER_BUILD_SERVICE: '27-dind'
APP_ENVIRONMENT: 'develop'
STORAGE_USER_ID: '1000'
stages:
- build
- deploy
.docker-build:
image: $DOCKER_BUILD_IMAGE
services:
- docker:$DOCKER_BUILD_SERVICE
before_script:
- docker login -u $CI_REGISTRY_USER -p $CI_REGISTRY_PASSWORD $CI_REGISTRY
script:
- |-
if [[ $APP_ENVIRONMENT = 'develop' ]]; then
export VERSION=$CI_COMMIT_REF_NAME-$CI_PIPELINE_ID
echo "Application image = $CI_REGISTRY_IMAGE:${VERSION}"
fi
export CONTAINER_IMAGE=$CI_REGISTRY_IMAGE:$VERSION
docker build --build-arg APP_UID=$STORAGE_USER_ID --build-arg ENVIRONMENT=$APP_ENVIRONMENT -t ${CONTAINER_IMAGE} .
docker push ${CONTAINER_IMAGE}
echo "VERSION=$VERSION" > .env.version
build:
extends: .docker-build
stage: build
artifacts:
reports:
dotenv: .env.version
only:
- branches
Prerequisites
- Docker-in-Docker (DinD) service must be available
- GitLab Container Registry must be enabled
- A
Dockerfile(ordocker-compose.ymlfor compose builds) must exist - For
.docker-build-vm-setup: Previous job must create.env.version - For
.docker-compose-build: Environment-specific.env.*files must exist - For
.docker-build-multi: Directory structure withDockerfilein each subdirectory
Notes
- Docker-in-Docker requires privileged mode on the runner
- Version tagging differs by environment (develop uses branch-pipeline, others use VERSION variable)
- The
.env.versionfile can be used as a dotenv artifact to share version information - For multi-image builds, each subdirectory must contain its own
Dockerfile - Docker Compose builds require a
docker-compose.ymlfile with anappservice defined