content/guides/azure-pipelines.md
This guide is a community contribution. Docker would like to thank Kristiyan Velkov for his valuable contribution.
Before you begin, ensure you have the following requirements:
Dockerfile at its root or appropriate build context.This guide walks you through building and pushing Docker images using Azure Pipelines, enabling a streamlined and secure CI workflow for containerized applications. You’ll learn how to:
To securely authenticate with Docker Hub using Azure Pipelines:
my-docker-registry.[!IMPORTANT]
Avoid selecting the option to grant access to all pipelines unless absolutely necessary. Always apply the principle of least privilege.
Add the following azure-pipelines.yml file to the root of your repository:
# Trigger pipeline on commits to the main branch
trigger:
- main
# Trigger pipeline on pull requests targeting the main branch
pr:
- main
# Define variables for reuse across the pipeline
variables:
imageName: 'docker.io/$(dockerUsername)/my-image'
buildTag: '$(Build.BuildId)'
latestTag: 'latest'
stages:
- stage: BuildAndPush
displayName: Build and Push Docker Image
jobs:
- job: DockerJob
displayName: Build and Push
pool:
vmImage: ubuntu-latest
demands:
- docker
steps:
- checkout: self
displayName: Checkout Code
- task: Docker@2
displayName: Docker Login
inputs:
command: login
containerRegistry: 'my-docker-registry' # Service connection name
- task: Docker@2
displayName: Build Docker Image
inputs:
command: build
repository: $(imageName)
tags: |
$(buildTag)
$(latestTag)
dockerfile: './Dockerfile'
arguments: |
--sbom=true
--attest type=provenance
--cache-from $(imageName):latest
env:
DOCKER_BUILDKIT: 1
- task: Docker@2
displayName: Push Docker Image
condition: eq(variables['Build.SourceBranch'], 'refs/heads/main')
inputs:
command: push
repository: $(imageName)
tags: |
$(buildTag)
$(latestTag)
# Optional: logout for self-hosted agents
- script: docker logout
displayName: Docker Logout (Self-hosted only)
condition: ne(variables['Agent.OS'], 'Windows_NT')
This pipeline automates the Docker image build and deployment process for the main branch. It ensures a secure and efficient workflow with best practices like caching, tagging, and conditional cleanup. Here's what it does:
main branch.trigger:
- main
pr:
- main
This pipeline is triggered automatically on:
main branchmain main branch[!TIP] Learn more: Define pipeline triggers in Azure Pipelines
variables:
imageName: 'docker.io/$(dockerUsername)/my-image'
buildTag: '$(Build.BuildId)'
latestTag: 'latest'
These variables ensure consistent naming, versioning, and reuse throughout the pipeline steps:
imageName: your image path on Docker HubbuildTag: a unique tag for each pipeline runlatestTag: a stable alias for your most recent image[!IMPORTANT]
The variable
dockerUsernameis not set automatically.
Set it securely in your Azure DevOps pipeline variables:
- Go to Pipelines > Edit > Variables
- Add
dockerUsernamewith your Docker Hub usernameLearn more: Define and use variables in Azure Pipelines
stages:
- stage: BuildAndPush
displayName: Build and Push Docker Image
This stage executes only if the source branch is main.
[!TIP]
Learn more: Stage conditions in Azure Pipelines
jobs:
- job: DockerJob
displayName: Build and Push
pool:
vmImage: ubuntu-latest
demands:
- docker
This job utilizes the latest Ubuntu VM image with Docker support, provided by Microsoft-hosted agents. It can be replaced with a custom pool for self-hosted agents if necessary.
[!TIP]
Learn more: Specify a pool in your pipeline
steps:
- checkout: self
displayName: Checkout Code
This step pulls your repository code into the build agent, so the pipeline can access the Dockerfile and application files.
[!TIP]
Learn more: checkout step documentation
- task: Docker@2
displayName: Docker Login
inputs:
command: login
containerRegistry: 'my-docker-registry' # Replace with your service connection name
Uses a pre-configured Azure DevOps Docker registry service connection to authenticate securely without exposing credentials directly.
[!TIP]
Learn more: Use service connections for Docker Hub
- task: Docker@2
displayName: Build Docker Image
inputs:
command: build
repository: $(imageName)
tags: |
$(buildTag)
$(latestTag)
dockerfile: './Dockerfile'
arguments: |
--sbom=true
--attest type=provenance
--cache-from $(imageName):latest
env:
DOCKER_BUILDKIT: 1
This builds the image with:
[!TIP]
Learn more:
- task: Docker@2
displayName: Push Docker Image
condition: eq(variables['Build.SourceBranch'], 'refs/heads/main')
inputs:
command: push
repository: $(imageName)
tags: |
$(buildTag)
$(latestTag)
By applying this condition, the pipeline builds the Docker image on every run to ensure early detection of issues, but only pushes the image to the registry when changes are merged into the main branch—keeping your Docker Hub clean and focused
This uploads both tags to Docker Hub:
$(buildTag) ensures traceability per run.latest is used for most recent image references.- script: docker logout
displayName: Docker Logout (Self-hosted only)
condition: ne(variables['Agent.OS'], 'Windows_NT')
Executes docker logout at the end of the pipeline on Linux-based self-hosted agents to proactively clean up credentials and enhance security posture.
With this Azure Pipelines CI setup, you get: