examlab .net The most efficient path to the most valuable certifications.
In this note ≈ 25 min

CodeBuild — Custom Build Environments, buildspec.yml, and Test Integration

5,000 words · ≈ 25 min read ·

DOP-C02 deep dive on CodeBuild custom Docker images, buildspec.yml phases, environment variables, batch builds, VPC configuration, ECR private registry pulls, secrets handling via Secrets Manager and Parameter Store, and cache strategies for fast deterministic builds.

Do 20 practice questions → Free · No signup · DOP-C02

CodeBuild is the unsung workhorse of the AWS DevOps stack. Every CI/CD pipeline on DOP-C02 has at least one CodeBuild project, and every realistic build scenario forces decisions about base images, buildspec phases, environment variables, secrets, caching, and VPC connectivity. The exam never asks "what is CodeBuild" — it asks which combination of those settings produces a fast, deterministic, secure build for a given constraint, and which combination causes the failure mode described in the stem.

This guide treats buildspec.yml as a memorisation target (the exam does not let you look it up), then layers on the Pro-level patterns: custom images stored in ECR with private registry authentication, batch builds for parallel test sharding, VPC configuration to reach private RDS or ElastiCache, secrets injection through Parameter Store and Secrets Manager, and the three caching backends (S3, local, custom). By the end you should recognise every common CodeBuild failure mode — unable to access registry, cannot connect to RDS in private subnet, cache miss because cache key changed, secret reference not interpolated — within seconds.

Why CodeBuild Mastery Decides DOP-C02 Build Questions

CodeBuild questions on the exam frequently look almost identical at first glance: "the build runs slowly, what should the engineer do?", "the build cannot reach the private database, what is wrong?", "the build needs to inject a secret, which approach is most secure?". The answers diverge based on subtle details about caching backends, VPC subnets, NAT gateways, and the difference between env.variables, env.parameter-store, and env.secrets-manager in buildspec.

Three forces explain why CodeBuild eats so much of the SDLC Automation domain. First, base-image selection governs both build speed (pre-installed tools) and security posture (CIS-compliant images). Second, buildspec phase semantics dictate where commands run and how artifacts and reports are produced — install vs pre_build vs build vs post_build is not interchangeable. Third, VPC configuration trades off connectivity for performance (VPC builds are slower because of ENI provisioning) and is the only way to reach private resources during build. Master these three and the rest is detail.

  • Build environment: the compute and Docker image combination running a CodeBuild project; specified by environment.type (e.g., LINUX_CONTAINER) and environment.image (managed or custom).
  • Custom image: a Docker image stored in ECR (private), Docker Hub (public), or Docker Hub authenticated, used as the build environment instead of an AWS-managed image.
  • buildspec.yml: a YAML file (in the source root or inline in the project) describing build phases, environment variables, artifacts, and reports.
  • Phase: one of install, pre_build, build, post_build — sequential sections of buildspec where commands execute.
  • Artifact: the build output uploaded to S3; described under artifacts in buildspec.
  • Reports: a CodeBuild-managed test report and code-coverage data, declared under reports and viewable in the console.
  • Cache: a build-acceleration mechanism with three backends — S3 (artifact-based), local (Docker layer / source / custom on the build host), and disabled.
  • Batch build: a CodeBuild feature that runs multiple builds in parallel, defined via build-list, build-matrix, or build-graph in buildspec.
  • Build environment variables: typed values surfaced into the shell — plaintext, Parameter Store reference, or Secrets Manager reference.
  • Reference: https://docs.aws.amazon.com/codebuild/latest/userguide/build-spec-ref.html

Plain-Language Explanation: CodeBuild Build Environment

CodeBuild can feel like a black box because so much happens between "git push" and "artifact uploaded". Three analogies from different domains untangle the mechanics.

Analogy 1: Professional Kitchen Mise en Place

Picture a fine-dining kitchen preparing a multi-course meal. The build environment image is the station — pasta station, pastry station, grill station — pre-stocked with the right tools and ingredients. AWS-managed images are the pre-built stations the rental kitchen provides; custom ECR images are your own station you brought from your restaurant, equipped exactly the way your chefs like.

The buildspec.yml is the prep card taped to the station. It has four phases that map perfectly to kitchen rhythm. install is "fire up the burners and unbox specialty equipment" — install language runtimes, set up NPM, install AWS CLI plugins. pre_build is "wash and chop everything you'll need" — log in to ECR, pull dependencies, fetch secrets. build is "actually cook the dishes" — compile, run unit tests, build Docker images. post_build is "plate, garnish, and send to the pass" — push the Docker image to ECR, generate test reports, write the imagedefinitions.json artifact for downstream stages.

The cache is the walk-in fridge between services — pre-prepped ingredients (compiled node_modules, downloaded Maven dependencies) you grab without re-running the chop step. The environment variables are the specials board — values the chef references throughout the shift. Secrets-manager references are the safe in the office — pull the wine cellar key only when needed, never write it on the prep card.

Analogy 2: Industrial Assembly Line

Think of a factory producing electronics. The build environment is the assembly line itself — conveyor speed, robot arms, soldering stations. The custom image is the specialised assembly line for a particular product class; switching products means switching lines, not retooling on the fly. The buildspec phases are the sequence of stations the product passes through — input QA (install), parts staging (pre_build), assembly (build), output QA and packaging (post_build). The batch build is multiple parallel assembly lines producing variants simultaneously — line A makes the iOS variant, line B the Android variant, line C the web variant, all kicked off by a single work order.

The VPC configuration is the factory floor's location — when builds need to reach a private database, you literally place the assembly line inside the customer's secured warehouse rather than the public industrial park; this slows setup (ENI provisioning takes 30 seconds) but is the only way to reach the warehouse's internal-only barcode service.

Analogy 3: Movie Production Set

A film production has principal photography (build), pre-production (pre_build), and post-production (post_build). The CodeBuild project is the production company's contract, declaring crew (compute type), location (image), schedule (timeout), and deliverables (artifacts and reports). The environment variables are the call sheet — what each crew member needs to know that day. The secrets are the locked script — only delivered to actors at the moment of the table read; never printed in the call sheet.

The service role is the production permit — without it, the crew cannot enter the location, hire equipment, or upload dailies. The VPC config is the closed set — the studio rents a sound stage inside a guarded facility because outdoor filming would leak the unreleased trailer. This always slows production but is sometimes mandated by the studio's compliance team.

The kitchen analogy is the most useful day-to-day because phases map cleanly to install/pre_build/build/post_build. The factory analogy is best for batch-build and VPC reasoning. The film analogy is the right model when the exam stresses governance, secrets, and audit. Reference: https://docs.aws.amazon.com/codebuild/latest/userguide/build-spec-ref.html

buildspec.yml Phases — What Runs Where

The four sequential phases of a buildspec each have semantic meaning. Misplacing a command is a common bug source and a frequent exam trap.

install runs after the build environment starts but before the source is cloned. Use it for environment setup: installing language runtimes (runtime-versions), package managers, AWS CLI plugins. Avoid putting business logic here.

pre_build runs after the source is cloned and is the right place for outbound side-effects that must precede the build: ECR login (aws ecr get-login-password | docker login), npm install, mvn dependency resolution, fetching configuration files from S3.

build is the main workload: compilation, unit tests, packaging, Docker image construction. The exit code of the last command in this phase determines build success unless overridden by on-failure.

post_build runs whether build succeeded or failed (unless on-failure: ABORT). Use it for cleanup, image push to ECR (only on success — wrap in if [ "$CODEBUILD_BUILD_SUCCEEDING" = "1" ]; then ...; fi), test report finalisation, and writing artifact-definition files.

The exam loves trick questions where docker push is in build and rate-limit errors trigger phase failure but the image still uploaded — knowing that post_build runs on failure is the key insight.

Custom Images from ECR

For builds that need exotic toolchains (specific Java version, Node + Python multi-runtime, embedded toolchains), AWS-managed images are insufficient. Custom Docker images stored in ECR are the standard answer.

To use an ECR image, set environment.image to the image URI (e.g., 123456789012.dkr.ecr.us-east-1.amazonaws.com/build-toolchain:v1.2) and environment.imagePullCredentialsType to SERVICE_ROLE (CodeBuild uses the project service role to pull) or CODEBUILD (CodeBuild uses its own service principal — works for Docker Hub but not ECR). The service role must include ecr:GetAuthorizationToken, ecr:BatchCheckLayerAvailability, ecr:GetDownloadUrlForLayer, and ecr:BatchGetImage on the image's repository.

ECR public registry images use public.ecr.aws/... URIs and do not require authentication for pull, but they may have rate limits.

When the build fails with error pulling image: Unable to authenticate to ECR, the cause is almost always one of: (1) imagePullCredentialsType set to CODEBUILD instead of SERVICE_ROLE for an ECR image, (2) the service role missing ecr:GetAuthorizationToken, or (3) the ECR repository policy missing the service role's principal. The exam tests this exact triage. Reference: https://docs.aws.amazon.com/codebuild/latest/userguide/sample-docker-custom-image.html

Environment Variables, Parameter Store, and Secrets Manager

CodeBuild's env block in buildspec supports three flavours of environment variable.

env.variables are plaintext values defined inline. Suitable for non-sensitive configuration like BUILD_ENV: production or REGION: us-east-1.

env.parameter-store references SSM Parameter Store paths and CodeBuild fetches the value at build start. Suitable for non-sensitive structured config — feature flags, version pins, environment URLs. Hierarchical paths (/build/prod/dbHost) make multi-environment management clean.

env.secrets-manager references Secrets Manager secret ARNs and fetches values at build start. Suitable for credentials that need rotation: database passwords, third-party API keys, OAuth tokens. The service role needs secretsmanager:GetSecretValue on the specific secret ARN; least-privilege the resource scope.

A subtle DOP-C02 trap: writing secrets to plaintext in env.variables shows them in the CloudFormation template, the project description, and CloudTrail. Always use env.secrets-manager for sensitive values. Buildspec references can use secret-id:json-key:version-stage:version-id syntax to extract a specific JSON field of a secret.

Putting DB_PASSWORD: mySecret123 directly in env.variables exposes the value in the CodeBuild project configuration, the CloudFormation template, and any pipeline definition that creates the project. The fix is to use env.secrets-manager: DB_PASSWORD: rds/prod/password so the value is fetched at build time, never serialised to configuration. The exam will mark env.variables as wrong any time the stem mentions credentials, tokens, or keys. Reference: https://docs.aws.amazon.com/codebuild/latest/userguide/build-spec-ref.html

VPC Configuration for Private Resource Access

By default, CodeBuild runs in an AWS-managed VPC with public internet access but no route to your private subnets. To reach private resources (RDS, ElastiCache, internal-only endpoints), configure the project with a VPC, subnets, and security groups.

The trade-offs are real. VPC-attached builds spend 20–60 seconds provisioning an ENI before the build starts (longer for cold starts in less-used subnets). They lose direct internet access — to reach package registries, ECR, or S3, you need NAT Gateway, VPC endpoints, or both. The subnets must be private subnets with NAT for internet-facing pulls; placing the build in a public subnet does not work because CodeBuild does not assign public IPs to ENIs.

Best practice for VPC builds: pre-bake the custom build image with all toolchain dependencies so the build does not need internet access for package installs. Pull from ECR via VPC endpoint (com.amazonaws.region.ecr.dkr and com.amazonaws.region.ecr.api) instead of NAT to save bandwidth cost.

Caching Backends — S3, Local, Custom

CodeBuild supports three caching modes, configured via cache.type.

S3 cache stores arbitrary cache files (declared under cache.paths in buildspec) in an S3 bucket. Best for cross-build caches: Maven ~/.m2, NPM ~/.npm, Gradle ~/.gradle. The first build populates the cache; subsequent builds restore it before install. S3 cache is eventually consistent — concurrent builds may overwrite each other.

Local cache stores cache on the build host's local disk and survives between builds on the same instance. Three sub-modes: LOCAL_DOCKER_LAYER_CACHE (caches Docker layers, requires privileged mode), LOCAL_SOURCE_CACHE (caches the git source tree), and LOCAL_CUSTOM_CACHE (caches paths declared in buildspec). Faster than S3 cache but only effective when builds reuse the same warm host — typical only at high build frequency.

Custom cache is local custom paths only, declared in buildspec.

Cache choice depends on workload. Docker-heavy builds benefit from local Docker layer cache. Cross-team shared dependencies (Maven repos) benefit from S3 cache. Single-team high-frequency builds benefit from local custom cache. Always declare a cache.paths list — without it, S3 cache writes only compiler-managed paths and frequently misses.

LOCAL_DOCKER_LAYER_CACHE is the most effective cache for Docker-heavy pipelines (per-layer caching halves typical container build time on warm hosts). It requires environment.privilegedMode: true and only works when the build host is reused — the exam often tests "first build is slow, second build is fast, why?" with this as the answer. Reference: https://docs.aws.amazon.com/codebuild/latest/userguide/build-caching.html

Batch Builds for Parallel Test Sharding

Batch builds let one CodeBuild project execute multiple builds in parallel, useful for parallel test sharding, multi-arch container builds, and matrix testing across language versions.

Three batch types exist. build-list is a static array of variant configurations, each running independently. Useful for "one build per test shard". build-matrix is a Cartesian product of dimensions (e.g., 3 OS × 4 Node versions = 12 builds). build-graph is a DAG with dependencies, where downstream builds wait for upstream completion. Useful for "compile once, then test in parallel, then publish".

Batch builds emit a single aggregate result; failure of any sub-build can fail the batch (combine-artifacts and ignore-failure settings tune this). The pipeline action consuming a batch build uses the BatchEnabled: true configuration.

Reports Group for Test Results and Code Coverage

CodeBuild reports surface unit test results and code coverage in the console without external tooling. Declare a reports block in buildspec listing the report group ARN, file format (JUnit XML, Cucumber JSON, etc.), and base directory of report files.

Report groups can be referenced across multiple projects, aggregating results for dashboard purposes. The service role needs codebuild:CreateReport, codebuild:UpdateReport, and codebuild:BatchPutTestCases.

For DOP-C02, knowing that reports are first-class CodeBuild constructs (not external integrations) is enough; deep XML format details are over-tested in non-Pro exams.

Webhooks and Source Triggers

CodeBuild projects can be triggered by webhooks from CodeCommit, GitHub, Bitbucket, and S3 events. Webhook filter groups let you scope triggers — e.g., only run on push to main branch, or only on pull_request events with paths matching src/**.

For DOP-C02, the key insight is that webhook filters are evaluated server-side at the SCM before CodeBuild starts, saving build minutes. A common scenario: developers want PR builds but not branch-push builds; configure two filter groups, one for PR events and one excluding branch pushes.

End-to-End Build Pipeline Pattern

A representative DOP-C02 build pipeline assembles like this. Source action pulls from CodeCommit. Build action runs a CodeBuild project with: a custom ECR image baked with Java 17 and Node 18, an S3 cache for ~/.m2 and ~/.npm, secrets pulled from Secrets Manager for ECR push (cross-account ECR), a VPC configuration to reach an internal artifact repository, and a buildspec that runs unit tests, builds a Docker image, runs container security scanning, and pushes to ECR. The build emits an artifact imagedefinitions.json consumed by the downstream ECS deploy action.

The buildspec for this pipeline is roughly 60 lines and demonstrates every Pro-level pattern in one place. Memorising the shape lets you recognise gaps in any exam stem.

Memorise this five-piece structure for any buildspec question:

  1. version: 0.2 (always; 0.1 is deprecated and not supported by new features).
  2. env block with variables, parameter-store, secrets-manager, and (optionally) git-credential-helper: yes.
  3. phases with install, pre_build, build, post_build and explicit runtime-versions in install for Linux managed images.
  4. artifacts block with files, discard-paths, and (for multi-artifact) secondary-artifacts.
  5. reports block referencing report group ARNs, plus cache.paths for cross-build acceleration.

Any buildspec question can be triaged against these five sections. Reference: https://docs.aws.amazon.com/codebuild/latest/userguide/build-spec-ref.html

Common Trap Patterns

CodeBuild has a recurring set of failure modes the exam likes to dangle.

Trap one: imagePullCredentialsType: CODEBUILD for an ECR image. CodeBuild's own service principal cannot read your ECR repository; must be SERVICE_ROLE.

Trap two: VPC build cannot reach internet because the subnet has no NAT Gateway or VPC endpoint to the package registry.

Trap three: cache miss because cache.paths was not declared and S3 cache only stores compiler-managed defaults.

Trap four: secrets in env.variables. Always use env.secrets-manager or env.parameter-store for sensitive values.

Trap five: post_build running ECR push without checking CODEBUILD_BUILD_SUCCEEDING, causing failed builds to push broken images.

If the build runs docker build, docker run, or any Docker command, the project must have environment.privilegedMode: true. Without it, the build fails with Cannot connect to the Docker daemon. The exam often tests this with a buildspec that builds a Docker image and a project configuration omitting privileged mode — the fix is the configuration flag, not the buildspec. Reference: https://docs.aws.amazon.com/codebuild/latest/userguide/sample-docker-custom-image.html

常考陷阱(Common Exam Traps)

  1. imagePullCredentialsType: CODEBUILD for ECR custom image — CodeBuild's own service cannot pull from your private ECR; must be SERVICE_ROLE with ecr:* permissions on the service role.
  2. Plaintext secrets in env.variables — exposes credentials in CloudFormation/console; always use env.secrets-manager or env.parameter-store with type: SECRETS_MANAGER.
  3. VPC build with no NAT or VPC endpoint — private-subnet builds fail to pull from package registries; either pre-bake a custom image with all dependencies or add VPC endpoints for ECR/S3/Secrets Manager.
  4. Missing privilegedMode: true for Docker buildsdocker build inside CodeBuild requires privileged mode plus an image with Docker installed (e.g., aws/codebuild/standard:7.0).
  5. Cache without cache.paths — S3/local custom cache only persists declared paths; without explicit cache.paths Maven, NPM, and Gradle caches are not preserved.

FAQ

Q1: When should I prefer a custom ECR image over an AWS-managed image? Use custom images when (a) you need toolchains AWS does not bundle, (b) regulatory compliance demands a hardened base, or (c) build speed is dominated by apt-get install operations that pre-baking eliminates. Otherwise the AWS-managed aws/codebuild/standard:7.0 image is fastest to maintain because AWS patches CVEs.

Q2: Can a single CodeBuild project run on Linux and Windows? No. The environment.type (e.g., LINUX_CONTAINER, WINDOWS_SERVER_2019_CONTAINER, ARM_CONTAINER) is fixed per project. For multi-platform builds, use batch builds with one variant per platform, each pointing at a different project or batch entry, or run separate projects in parallel pipeline actions.

Q3: How do I share a build cache across teams without leaking artifacts? Use S3 cache with bucket-level access controls. Each team's CodeBuild service role gets s3:GetObject only on its team prefix. Common-dependency caches (Maven Central mirror) can be shared read-only across all teams from a central prefix.

Q4: What happens if a buildspec phase exits non-zero? By default, the entire build fails and post_build still runs (unless on-failure: ABORT). Use set -e at phase start to make any failed command abort the phase, or if ! command; then ...; fi patterns for explicit error handling. The build status is determined by the final phase's exit code unless overridden.

Q5: How do I make a CodeBuild project deploy to ECR cross-account? Two-step IAM. The service role needs ecr:PutImage on the cross-account ECR repository. The cross-account ECR repository policy needs to allow the service role's ARN as principal. ECR is regional, so a cross-region cross-account push also requires the destination region's repository to have the matching repository policy.

Q6: Do batch builds count as one build or many for billing? Many. Each sub-build of a batch is billed independently for its compute minutes. Batch builds primarily save wall-clock time by parallelising; they do not save cost compared to running the same builds sequentially.

Q7: How does the Docker rate limit for Docker Hub affect CodeBuild? Anonymous Docker Hub pulls are limited (currently 100 pulls per 6 hours per IP). CodeBuild's NAT Gateway IPs are shared across many builds, hitting limits quickly. Mitigations: pull from ECR Public (free, no rate limit on most images), authenticate to Docker Hub via Secrets Manager-backed credentials, or pre-mirror frequently used images to ECR private.

Q8: Can CodeBuild reports be used as a quality gate to fail the pipeline? Reports themselves do not fail the pipeline — they record results. To enforce a quality gate, parse the report file in post_build and exit non-zero if coverage drops below a threshold or test failures exceed a count. The build-failure status then propagates to the pipeline.

Official sources

More DOP-C02 topics