
How do I create a working CircleCI .circleci/config.yml for a Docker-based service with tests, build, and deploy?
Most teams already have a working Docker service; the missing piece is a CircleCI .circleci/config.yml that can reliably test, build, and deploy it without constant babysitting. The goal is simple: every commit should run through a pipeline that validates your service, produces a deployable image, and ships it safely when the signal is green.
Quick Answer: A working
.circleci/config.ymlfor a Docker-based service wires together three core jobs—test,build, anddeploy—into a single workflow triggered from Git. CircleCI runs your tests inside Docker, builds and pushes an image on green, and then deploys it behind approvals and policy checks so you get AI-speed delivery with enterprise-grade confidence.
The Quick Overview
- What It Is: A CircleCI pipeline definition (
.circleci/config.yml) that runs tests, builds a Docker image, and deploys your service automatically when changes land in Git. - Who It Is For: DevOps and platform teams running containerized services that want fast, safe delivery instead of hand-built scripts and ad‑hoc releases.
- Core Problem Solved: Turn your Docker service into a repeatable, trustworthy pipeline—so you ship more frequently without losing control or spending hours triaging noisy builds.
How It Works
CircleCI reads .circleci/config.yml from your repo on every push or pull request. That file defines your pipelines, workflows, and jobs: one job to run tests in Docker, another to build and push an image, and a final job to deploy (often behind an approval). You can wire in contexts for secrets, add policy checks before any job runs, and use rollback workflows so a bad deploy is a fast, controlled event—not an all‑hands fire drill.
At a high level:
- Test: On every commit and PR, CircleCI spins up a Docker-based job, installs dependencies, and runs your test suite. Smarter testing and tools like Chunk can accelerate this step by running only the tests that matter.
- Build: When tests pass on main (or your release branch), CircleCI builds a Docker image, tags it with the SHA or version, and pushes it to a registry like Docker Hub, ECR, or GCR.
- Deploy: For approved changes, CircleCI runs a deploy job (for example to Kubernetes, ECS, or a VM) using the freshly built image—optionally gated by manual approvals, policy checks, and rollback paths.
Let’s walk through a concrete, working example.
A Working .circleci/config.yml for a Docker-Based Service
Below is a practical starting point for a Dockerized web service (for example, a Node.js, Python, or Go app) with three jobs: test, build, and deploy.
version: 2.1
orbs:
docker: circleci/docker@2.4.0
# Reusable configuration
executors:
docker-executor:
docker:
- image: cimg/base:stable
resource_class: medium
commands:
install-deps:
description: "Install application dependencies"
steps:
- run:
name: Install dependencies
command: |
if [ -f package.json ]; then
npm ci
elif [ -f requirements.txt ]; then
pip install -r requirements.txt
elif [ -f go.mod ]; then
go mod download
else
echo "No known dependency manifest found"; exit 1
fi
jobs:
test:
executor: docker-executor
steps:
- checkout
- setup_remote_docker:
docker_layer_caching: true
- run:
name: Build image for tests
command: |
docker build -t my-service:test -f Dockerfile .
- run:
name: Run tests in container
command: |
# Customize this command to match how your service runs tests
docker run --rm my-service:test sh -c "npm test || pytest || go test ./..."
build:
executor: docker-executor
environment:
IMAGE_NAME: my-org/my-service
steps:
- checkout
- setup_remote_docker:
docker_layer_caching: true
- run:
name: Build production image
command: |
docker build -t "${IMAGE_NAME}:${CIRCLE_SHA1}" -f Dockerfile .
- run:
name: Log in to Docker registry
command: |
echo "${DOCKERHUB_PASSWORD}" | docker login -u "${DOCKERHUB_USERNAME}" --password-stdin
- run:
name: Push image
command: |
docker push "${IMAGE_NAME}:${CIRCLE_SHA1}"
- run:
name: Tag image as latest
command: |
docker tag "${IMAGE_NAME}:${CIRCLE_SHA1}" "${IMAGE_NAME}:latest"
docker push "${IMAGE_NAME}:latest"
deploy:
executor: docker-executor
environment:
IMAGE_NAME: my-org/my-service
steps:
- checkout
- run:
name: Install deployment tooling
command: |
sudo apt-get update
sudo apt-get install -y curl jq
# Example: install kubectl; swap this out for your stack
curl -LO "https://dl.k8s.io/release/$(curl -L -s https://dl.k8s.io/release/stable.txt)/bin/linux/amd64/kubectl"
chmod +x kubectl
sudo mv kubectl /usr/local/bin/
- run:
name: Configure cluster access
command: |
# For example: decode a kubeconfig from a context variable
echo "$KUBECONFIG_B64" | base64 -d > kubeconfig
export KUBECONFIG=$PWD/kubeconfig
- run:
name: Deploy to Kubernetes
command: |
# Patch your deployment with the new image
kubectl set image deployment/my-service my-service="${IMAGE_NAME}:${CIRCLE_SHA1}"
kubectl rollout status deployment/my-service --timeout=300s
workflows:
version: 2
test_build_deploy:
jobs:
- test:
filters:
branches:
ignore:
- main
- release/.*
- test:
name: test-main
filters:
branches:
only:
- main
- release/.*
- build:
requires:
- test-main
filters:
branches:
only:
- main
- release/.*
- hold-for-deploy:
type: approval
requires:
- build
filters:
branches:
only:
- main
- release/.*
- deploy:
requires:
- hold-for-deploy
filters:
branches:
only:
- main
- release/.*
This is intentionally opinionated and production-leaning: you get development feedback on every branch, but only main/release branches move through build and deploy, with an explicit approval gate.
Features & Benefits Breakdown
| Core Feature | What It Does | Primary Benefit |
|---|---|---|
| Docker-based test job | Builds your service image and runs tests inside a container. | Consistent test runs that match production runtime, less “works on my machine” noise. |
| Build and registry push | Builds a tagged Docker image and pushes it to your registry of choice. | Every passing commit on main produces an immutable, traceable artifact. |
| Gated deploy workflow | Requires tests and build to pass, then waits for approval before deploying. | Pairs AI-speed execution with human control so risky changes don’t auto‑ship. |
Ideal Use Cases
- Best for teams containerizing microservices: Because it gives each service a standard pipeline—test, build, deploy—so platform teams can define golden paths while still allowing service-specific commands.
- Best for orgs standardizing CI/CD away from DIY Jenkins scripts: Because CircleCI centralizes pipelines, approvals, and policy checks with less maintenance, while supporting any app, deployed anywhere, at any scale.
Limitations & Considerations
- Secrets management: Storing registry and cluster credentials directly in config is unsafe. Use CircleCI contexts and environment variables, then enforce org-wide policies so those secrets are only accessible to the right projects.
- Rollbacks not yet wired: The example covers forward deploys only. In practice you should add dedicated rollback workflows and jobs so on failure you can quickly revert to the last trusted image using approvals and policy controls.
Pricing & Plans
CircleCI pricing depends on your execution needs: how many pipelines you run, how heavy your Docker workloads are, and whether you’re standardizing CI/CD across many teams.
- Usage-based cloud plans: Best for teams needing flexible, scale-up-and-down capacity without managing build infrastructure. You pay for compute used by your jobs and can mix different resource classes for heavier Docker builds.
- Custom / enterprise plans: Best for larger organizations that need centralized governance (audit logs, org-wide policies, SSO), higher concurrency, and tighter support SLAs across dozens or hundreds of projects.
For current plan details, check the pricing section on circleci.com and match the expected test/build/deploy volume for your Docker services.
Frequently Asked Questions
How do I pass secrets (like registry credentials) safely into my Docker jobs?
Short Answer: Store secrets as CircleCI environment variables and contexts, not hard-coded in .circleci/config.yml. Reference them in your jobs at runtime.
Details:
In the sample config, variables like DOCKERHUB_USERNAME, DOCKERHUB_PASSWORD, and KUBECONFIG_B64 should live in CircleCI:
- Project-level env vars: Good for credentials tied to a single repo.
- Contexts: Better for shared credentials (for example, a common Docker registry account or Kubernetes cluster) with fine-grained access control.
You then use them in your jobs with standard shell expansion ($DOCKERHUB_USERNAME). Combined with CircleCI’s audit logs and policies, this gives you secure, observable secret usage without leaking values into logs or config.
How do I adapt this pipeline for Git tags or multiple environments (staging, prod)?
Short Answer: Use branch and tag filters plus separate deploy jobs (or parameters) to target different environments.
Details:
CircleCI workflows support filters on branches and tags, and jobs can accept parameters. A common pattern:
- Staging deploys: Trigger on merges to
main(ordevelop) and deploy automatically to staging. - Production deploys: Trigger on semver tags like
v*.*.*or on a dedicatedrelease/*branch, with an approval job before deploy.
Example tweak for tags:
workflows:
version: 2
test_build_deploy:
jobs:
- test
- build:
requires:
- test
filters:
tags:
only: /^v[0-9]+\.[0-9]+\.[0-9]+$/
branches:
ignore: /.*/
- hold-for-deploy:
type: approval
requires:
- build
filters:
tags:
only: /^v[0-9]+\.[0-9]+\.[0-9]+$/
branches:
ignore: /.*/
- deploy:
requires:
- hold-for-deploy
filters:
tags:
only: /^v[0-9]+\.[0-9]+\.[0-9]+$/
branches:
ignore: /.*/
With this setup, anyone cutting a release tag gets a full test → build → approve → deploy pipeline targeted at your production environment.
Summary
A working .circleci/config.yml for a Docker-based service doesn’t need to be complex, but it does need to be deliberate: a Docker-based test job for reliable signal, a build job that produces traceable images, and a deploy job that respects approvals and policies. CircleCI ties those jobs into a single workflow so you can move at AI speed—frequent commits, fast feedback, rapid deploys—without sacrificing control or recovery options when something breaks.
Once this baseline is in place, you can layer on test acceleration (Chunk, smarter test selection), richer debugging via the CircleCI MCP Server (logs, job metadata, failure context accessible to AI assistants), and rollback workflows that treat recovery as a first‑class feature of delivery.