1.2 CodeBuild Projects, Build Environments & Pipeline Secrets
Key Takeaways
- AWS CodeBuild executes builds inside managed or custom ephemeral Docker containers governed by buildspec.yml, executing across four sequential phases: install, pre_build, build, and post_build.
- Building container images within CodeBuild (Docker-in-Docker) strictly requires setting privilegedMode: true in the project environment configuration and granting ECR repository access to the build service role.
- Attaching CodeBuild to a VPC enables private connectivity to internal resources like RDS databases and artifact repositories, but requires a NAT Gateway or VPC endpoints for internet access; containers in public subnets do not receive public IP addresses.
- Build acceleration relies on dependency caching: Amazon S3 cache provides durable storage across distributed build fleets, while Local caching (LOCAL_DOCKER_LAYER_CACHE, LOCAL_CUSTOM_CACHE) delivers optimal speed for sequential builds on the same host.
- Sensitive credentials must be retrieved dynamically in buildspec.yml using parameter-store for Systems Manager parameters and secrets-manager for Secrets Manager secrets, preventing secrets from leaking into source code or build logs.
AWS CodeBuild Fundamentals & buildspec.yml Anatomy
AWS CodeBuild is a fully managed, serverless build service that provisions isolated, ephemeral compute containers, executes specified commands, produces build artifacts, and streams logs to Amazon CloudWatch. CodeBuild scales elastically to handle concurrent build workloads without requiring management of dedicated build servers.
The buildspec.yml Lifecycle and Phases
A buildspec.yml file is a YAML-formatted configuration placed in the root of the source directory (or defined inline in the project). It coordinates the lifecycle of the build through four strictly ordered phases:
version: 0.2
env:
variables:
APP_ENV: "production"
JAVA_OPTS: "-Xmx2048m"
parameter-store:
DB_USER: "/app/prod/db_user"
secrets-manager:
DB_PASSWORD: "prod/rds/credentials:password"
exported-variables:
- IMAGE_TAG
- BUILD_TIMESTAMP
phases:
install:
run-as: root
runtime-versions:
java: corretto17
nodejs: 20
commands:
- echo "Installing build dependencies and tools..."
- apt-get update && apt-get install -y jq
- npm install -g snyk
pre_build:
commands:
- echo "Executing pre-build verifications and authentications..."
- aws --version
- aws ecr get-login-password --region $AWS_DEFAULT_REGION | docker login --username AWS --password-stdin $REPOSITORY_URI
- COMMIT_HASH=$(echo $CODEBUILD_RESOLVED_SOURCE_VERSION | cut -c 1-7)
- export IMAGE_TAG=${COMMIT_HASH:=latest}
- export BUILD_TIMESTAMP=$(date +%s)
build:
commands:
- echo "Compiling application and running test suites..."
- mvn clean package -DskipTests=false
- docker build -t $REPOSITORY_URI:latest .
- docker tag $REPOSITORY_URI:latest $REPOSITORY_URI:$IMAGE_TAG
post_build:
commands:
- echo "Build completed on $(date)"
- docker push $REPOSITORY_URI:latest
- docker push $REPOSITORY_URI:$IMAGE_TAG
- printf '[{"name":"microservice","imageUri":"%s"}]' $REPOSITORY_URI:$IMAGE_TAG > imagedefinitions.json
reports:
surefire-reports:
files:
- '**/*'
base-directory: 'target/surefire-reports'
file-format: JUNITXML
artifacts:
files:
- imagedefinitions.json
- target/*.jar
- appspec.yml
discard-paths: no
cache:
paths:
- '/root/.m2/**/*'
- '/root/.npm/**/*'
Phase Behavior and Failure Control
- Phase Sequence:
install->pre_build->build->post_build. - Failure Semantics: If any command within
install,pre_build, orbuildexits with a non-zero status code, that phase immediately halts. By default, subsequent phases are skipped, and the build status transitions toFAILED. - The
finallyBlock: Commands placed in afinallyblock execute regardless of whether commands in the main phase succeeded or failed. This is critical for releasing locks, flushing telemetry, or publishing test results. on-failure: CONTINUE: Configuringon-failure: CONTINUEallows the phase to proceed even if individual commands fail, which is useful when generating test reports that must be evaluated downstream.
Custom Docker Build Environments & ECR
CodeBuild provides managed Docker images for standard runtimes (Amazon Linux 2, Amazon Linux 2023, Ubuntu). However, enterprise workflows frequently require custom compilers, specialized CLI tooling, or proprietary security agents.
Custom Docker Images from Amazon ECR
Organizations build hardened Docker images, store them in a private Amazon ECR repository, and configure the CodeBuild project environment to use the custom image:
- ECR Permissions: The CodeBuild service role requires IAM permissions to authenticate and pull the image (
ecr:GetAuthorizationToken,ecr:BatchCheckLayerAvailability,ecr:GetDownloadUrlForLayer, andecr:BatchGetImage). - Cross-Account ECR: If the custom image resides in a shared security/tools account, the ECR repository policy must explicitly grant
ecr:BatchGetImageand layer download permissions to the CodeBuild project service role in the build account.
Privileged Mode (Docker-in-Docker)
[!IMPORTANT] DOP-C02 Exam Trap: If your CodeBuild project builds Docker images, interacts with the Docker daemon, or executes Docker Compose commands, you must explicitly enable the
privilegedModeflag (privilegedMode: truein CloudFormation/Terraform, or checking the "Privileged" box in the AWS Console). WithoutprivilegedMode, the build container cannot access/var/run/docker.sockor launch the nested Docker daemon, resulting in the error:Cannot connect to the Docker daemon at unix:///var/run/docker.sock.
VPC Support for Private Resource Access
By default, CodeBuild containers execute in an AWS-managed VPC with direct access to the public internet, public AWS service endpoints, and public Git repositories. However, builds frequently require access to private VPC resources, such as:
- Amazon RDS / Aurora databases to execute schema migration scripts.
- Internal package repositories (e.g., self-hosted Sonatype Nexus or JFrog Artifactory).
- Private Amazon ElastiCache clusters or internal microservices.
The VPC Public Subnet Internet Black Hole
When you configure CodeBuild to attach to a VPC, CodeBuild provisions Elastic Network Interfaces (ENIs) inside your specified subnets.
[ CodeBuild Project in VPC ]
│
▼ (Uses ENI)
[ Private Subnet ]
│
Route: 0.0.0.0/0
│
▼
[ NAT Gateway ] ──> [ Internet Gateway ] ──> [ Public Internet ]
│ (npm, pip, GitHub)
▼
[ VPC Gateway/Interface Endpoints ] ──> [ S3, ECR, CloudWatch Logs ]
[!CAUTION] Critical VPC Networking Rule: CodeBuild ENIs never receive public IPv4 addresses. Even if you place CodeBuild in a public subnet with an Internet Gateway route (
0.0.0.0/0 -> igw-xxx), the build container cannot reach the internet because it lacks a public IP for address translation. External requests (e.g.,npm install,pip install,docker pull) will hang and time out!
To enable internet access for a VPC-attached CodeBuild project:
- Attach CodeBuild to private subnets.
- Configure the private subnet route table with
0.0.0.0/0pointing to an Amazon VPC NAT Gateway in a public subnet. - Alternatively, for AWS-internal traffic, configure VPC Endpoints for Amazon S3, Amazon ECR, and CloudWatch Logs.
Dependency Caching Strategies
Build execution duration directly impacts developer feedback loops and AWS billing. CodeBuild supports two distinct caching mechanisms:
| Cache Mechanism | Scope | Latency | Recommended Use Case |
|---|---|---|---|
| Amazon S3 Cache | Distributed across all build hosts; durable in S3 | Moderate (S3 upload/download time on build start/end) | Intermittent builds, multi-account build fleets, or builds distributed across multiple regions |
| Local Cache | Stored on the local EC2 host running the build container | Ultra-low (direct NVMe/SSD block storage access) | High-frequency builds where successive builds likely run on the same underlying host |
Local Cache Modes
Local caching offers three distinct configuration options:
LOCAL_DOCKER_LAYER_CACHE: Caches intermediate Docker build layers locally. Eliminates re-pulling and re-building unmodified layers inDockerfilestages.LOCAL_SOURCE_CACHE: Caches the Git source repository metadata, avoiding a complete re-clone of the entire repository on sequential builds.LOCAL_CUSTOM_CACHE: Caches arbitrary directories defined inbuildspec.ymlundercache: paths, such as~/.m2(Maven),~/.npm(Node), or~/.cache/pip(Python).
Note: Local cache is maintained on a best-effort basis. If AWS provisions the build on a fresh underlying host due to scaling or maintenance, the local cache will be cold.
Secure Secrets Retrieval in CodeBuild
Hardcoding API tokens, database passwords, or private signing keys in buildspec.yml or source code is a critical security vulnerability. CodeBuild provides native integration with AWS Systems Manager Parameter Store and AWS Secrets Manager through the env block:
env:
parameter-store:
# Key: Environment variable name in build
# Value: Parameter Store parameter name or ARN
API_ENDPOINT: "/config/prod/api_endpoint"
DATABASE_USER: "/config/prod/db_username"
secrets-manager:
# Key: Environment variable name in build
# Value: SecretName:SecretKey:VersionStage:VersionId
DB_PASSWORD: "prod/app/rds:password"
GITHUB_PAT: "prod/vcs/tokens:github_token"
Operational and Security Benefits
- Automatic Decryption: For SecureString parameters in Parameter Store and secrets in Secrets Manager, CodeBuild automatically requests decryption using the KMS key associated with the secret, provided the CodeBuild service role has
kms:Decryptpermissions. - Log Masking: Secrets retrieved via
parameter-storeandsecrets-managerare automatically masked in CloudWatch Logs output. If a command echoes$DB_PASSWORD, CloudWatch Logs displays***instead of the plaintext credential. - Exported Variables: Values generated during the build (such as
$IMAGE_TAG) listed underenv: exported-variablesare exported to downstream CodePipeline actions, enabling dynamic variable propagation.
Compute Types, Architectures & Batch Builds
CodeBuild provides flexible compute configurations to balance cost and compilation performance:
- Compute Types:
BUILD_GENERAL1_SMALL(2 vCPU, 4 GiB RAM),BUILD_GENERAL1_MEDIUM(4 vCPU, 8 GiB RAM),BUILD_GENERAL1_LARGE(8 vCPU, 16 GiB RAM),BUILD_GENERAL1_XLARGE(36 vCPU, 72 GiB RAM), andBUILD_GENERAL1_2XLARGE(72 vCPU, 144 GiB RAM). - Architectures: x86_64 (
LINUX_CONTAINER), ARM64 (ARM_CONTAINERpowered by AWS Graviton for native multi-architecture builds and cost efficiency), and Windows Server (WINDOWS_SERVER_2019_CONTAINER).
Batch Builds: Matrices and Graphs
For large-scale testing across matrix configurations, CodeBuild supports Batch Builds:
- Build Matrix: Runs multiple parallel builds across permutations of operating systems, language versions, and test suites (e.g., testing across Node 18, 20, and 22 on both Linux and ARM64).
- Build Graph (DAG): Defines a directed acyclic graph of dependent builds where downstream builds execute only after upstream prerequisites succeed (e.g., compile code -> run unit tests & SAST in parallel -> package container image).
A DevOps engineer moves an existing AWS CodeBuild project into a corporate Amazon VPC so that the build phase can run automated database migration scripts against an Amazon RDS PostgreSQL database in a private subnet. The CodeBuild project is configured with the VPC ID, two public subnets with direct routes to an Internet Gateway, and a security group allowing outbound traffic to the database. When the build runs, the database migration succeeds, but the subsequent step npm install fails with connection timeout errors while attempting to fetch packages from the public npm registry. What is the root cause of this failure and the most architecturally appropriate resolution?
A software development team is migrating their containerized microservice build pipeline to AWS CodeBuild. The buildspec.yml executes docker build -t my-app:latest . and docker push <account-id>.dkr.ecr.<region>.amazonaws.com/my-app:latest. When the build executes, the CodeBuild project fails immediately during the docker build command with the error: Cannot connect to the Docker daemon at unix:///var/run/docker.sock. Is the docker daemon running? What configuration change must the DevOps engineer make to resolve this issue?
An organization requires that all database credentials used during automated integration testing in AWS CodeBuild be dynamically retrieved from AWS Secrets Manager without exposing passwords in source code, buildspec definitions, or build logs. The secret is stored under the name prod/db/credentials as a JSON object containing keys username and password. Which buildspec.yml configuration correctly injects the database password into an environment variable named DB_PASSWORD?