Your 15-minute builds are eating your lunch budget and pissing off developers. Here's how to fix the most common bottlenecks that turn 2-minute builds into coffee breaks.
Instance Size Reality Check
Stop using t3.large by default. Most builds run perfectly fine on t3.small. I've seen teams cut their CodeBuild costs by 60% just by right-sizing compute types based on actual CPU usage, not paranoia.
The BUILD_GENERAL1_SMALL environment (1 vCPU, 3GB RAM) costs $0.005/minute versus BUILD_GENERAL1_MEDIUM at $0.01/minute. For a 10-minute build running 50 times daily, that's $125/month versus $250/month. Unless your builds are actually memory-bound, you're pissing away $1,500 yearly.
Pro tip: Monitor your builds with CloudWatch. If CPU usage stays under 50% consistently, drop down a size. If memory usage hits 80%+, bump up. Don't guess - measure.
Caching That Actually Works
Docker layer caching is magic - when it works. Enable it in your buildspec.yml:
cache:
type: S3
location: your-bucket/cache-prefix
But here's what AWS docs don't tell you: S3 caching has latency. For small dependencies, it's faster to just download them fresh than fetch from cache. The break-even point is around 50MB of dependencies.
Local caching beats S3 caching for repeated builds in the same region. But local cache gets nuked randomly, so don't depend on it. Use both:
cache:
type: LOCAL
modes:
- LOCAL_DOCKER_LAYER_CACHE
- LOCAL_SOURCE_CACHE
Reality check: One team reduced their Node.js build from 12 minutes to 4 minutes by caching node_modules properly. Their monthly CodeBuild bill dropped from $800 to $320. All because they cached the right shit.
Parallel Builds Done Right
Don't parallelize everything - CodeBuild charges per concurrent build. Running 10 parallel 2-minute builds costs the same as one 20-minute build, but uses more compute slots.
Parallelize when:
- Independent test suites can run separately
- Building for multiple environments simultaneously
- Large codebases with isolated modules
Skip parallelization when:
- Dependencies between build steps
- Small builds under 5 minutes total
- You're hitting CodeBuild concurrent build limits
Example: Frontend tests, backend tests, and linting can run in parallel. Database migrations and deployment cannot.
Build Environment Optimization
Custom Docker images save minutes on every build. Instead of installing Python, Node.js, and 47 other packages every time, bake them into a custom image.
Before (slow as hell):
phases:
install:
commands:
- curl -o- https://raw.githubusercontent.com/nvm-sh/nvm/v0.39.0/install.sh | bash
- nvm install 18.17.0
- pip install -r requirements.txt
- apt-get install -y postgresql-client
After (actually fast):
phases:
build:
commands:
- npm run build
Create a custom image with everything pre-installed. Push it to ECR. Reference it in your buildspec. Builds that used to take 8 minutes now finish in 3.
Warning: Custom images add maintenance overhead. Keep them updated or you'll have security vulnerabilities. Use automated builds to rebuild your base images monthly.
Buildspec Optimization Tricks
Fail fast and fail early. Put quick checks at the top:
phases:
pre_build:
commands:
- npm run lint # Fails in 30 seconds, not 10 minutes
- npm run test:unit # Fast tests first
build:
commands:
- npm run test:integration # Slow tests after everything else passes
Skip unnecessary shit:
- Don't run full test suites on documentation changes
- Skip builds for draft PRs unless specifically requested
- Use git diff to detect which services changed in monorepos
Artifact optimization: Only upload what you actually need. Uploading 2GB of node_modules to S3 takes 5 minutes and costs money. Your production deployment doesn't need test files, source maps, or that random README.
The 3AM Debug Reality
When your build breaks at 3AM and you need it fixed NOW:
- Check CloudWatch logs immediately - not the CodeBuild console, which runs 2 minutes behind
- Look for OOMKilled errors - means you need more RAM, not faster CPU
- Check your cache hit rates - broken caching makes everything slow
- Validate your buildspec syntax - YAML indentation will ruin your weekend
Most 3AM build failures are either:
- Out of memory (bump instance size)
- Network timeouts (retry logic)
- Dependency conflicts (pin your versions)
Pro tip: Set up Slack notifications for build failures that include the actual error message. "Build failed" notifications are useless. "npm ERR! ECONNRESET" tells you exactly what broke.
The goal isn't perfect optimization - it's removing the obvious bottlenecks that waste time and money. Fix the big stuff first, then obsess over the details.