Using Docker with Azure DevOps for Test Environments
How to use Docker containers with Azure DevOps pipelines to create consistent, reproducible test environments. Covers Docker Compose for local dev, service containers in pipelines, containerised test runners, and database containers.
Docker eliminates the "works on my machine" problem in test automation. When your test environment is defined in code (a Dockerfile or docker-compose.yml), the same environment runs locally, in CI, and in every future pipeline run.
Service containers in Azure Pipelines
Azure Pipelines natively supports Docker service containers — containers that run alongside your pipeline job. This is the simplest way to add a database, cache, or mock server to your test pipeline.
trigger:
branches:
include: [main]
pool:
vmImage: ubuntu-latest
# Service containers run alongside the job
resources:
containers:
- container: postgres
image: postgres:16
env:
POSTGRES_PASSWORD: testpass
POSTGRES_DB: testdb
POSTGRES_USER: testuser
ports:
- 5432:5432
options: >-
--health-cmd "pg_isready -U testuser"
--health-interval 10s
--health-timeout 5s
--health-retries 5
- container: redis
image: redis:7-alpine
ports:
- 6379:6379
jobs:
- job: IntegrationTests
services:
postgres: postgres
redis: redis
steps:
- task: NodeTool@0
inputs:
versionSpec: '20.x'
- script: npm ci
- script: npm run db:migrate
displayName: Run database migrations
env:
DATABASE_URL: postgresql://testuser:testpass@localhost:5432/testdb
- script: npm run test:integration
displayName: Run integration tests
env:
DATABASE_URL: postgresql://testuser:testpass@localhost:5432/testdb
REDIS_URL: redis://localhost:6379
- task: PublishTestResults@2
inputs:
testResultsFormat: JUnit
testResultsFiles: test-results/integration.xml
condition: always()Running tests in a Docker container
For consistent browser and dependency versions:
pool:
vmImage: ubuntu-latest
steps:
- script: |
docker run --rm \
-v $(Build.SourcesDirectory):/workspace \
-w /workspace \
-e BASE_URL=$(STAGING_URL) \
-e CI=true \
mcr.microsoft.com/playwright:v1.45.0-jammy \
npx playwright test
displayName: Run Playwright in container
- task: PublishTestResults@2
inputs:
testResultsFormat: JUnit
testResultsFiles: test-results/results.xml
condition: always()This guarantees identical Playwright and browser versions between local runs and CI.
Docker Compose for local development matching CI
# docker-compose.test.yml
version: '3.8'
services:
postgres:
image: postgres:16
environment:
POSTGRES_PASSWORD: testpass
POSTGRES_DB: testdb
POSTGRES_USER: testuser
ports:
- '5432:5432'
healthcheck:
test: ["CMD-SHELL", "pg_isready -U testuser"]
interval: 10s
timeout: 5s
retries: 5
redis:
image: redis:7-alpine
ports:
- '6379:6379'
app:
build: .
environment:
DATABASE_URL: postgresql://testuser:testpass@postgres:5432/testdb
REDIS_URL: redis://redis:6379
depends_on:
postgres:
condition: service_healthy
ports:
- '3000:3000'Run locally:
docker-compose -f docker-compose.test.yml up -d
npm run test:integration
docker-compose -f docker-compose.test.yml downThe same database, same Redis, same app version as CI.
Building and caching Docker images in pipelines
variables:
DOCKER_BUILDKIT: '1'
IMAGE_NAME: $(Build.Repository.Name)
IMAGE_TAG: $(Build.BuildId)
steps:
- task: Docker@2
displayName: Build test image
inputs:
command: build
dockerfile: Dockerfile.test
tags: $(IMAGE_NAME):$(IMAGE_TAG)
arguments: --build-arg BUILDKIT_INLINE_CACHE=1
- task: Docker@2
displayName: Push to ACR
inputs:
command: push
containerRegistry: $(ACR_SERVICE_CONNECTION)
repository: $(IMAGE_NAME)
tags: $(IMAGE_TAG)Common errors and fixes
Error: Service container shows as unhealthy in pipeline
Fix: Add a health check and --health-retries option. The pipeline starts the job before the container is ready without health checks. Use options: --health-cmd "..." --health-retries 5.
Error: cannot connect to localhost:5432 in the test job
Fix: Service containers are accessed via localhost in Azure Pipelines. Ensure the port mapping is correct (5432:5432) and the connection string uses localhost not the container name.
Error: Docker build fails with "permission denied" on Linux agents
Fix: Add the current user to the docker group: - script: sudo usermod -aG docker $USER && newgrp docker. Or use sudo docker build in the script.
Error: Tests pass in Docker locally but fail in pipeline container
Fix: Check environment variable mapping. Variables passed with -e flag in local Docker run must be set as pipeline variables. Also check file mount paths — $(Build.SourcesDirectory) is the correct pipeline path.
Stay ahead in AI-driven QA
Get practical tutorials on test automation, AI testing, and quality engineering — straight to your inbox. No spam, unsubscribe any time.
Discussion
Sign in with GitHub to comment · powered by Giscus