Skip to content
Putting technology to work.
Insights to guide decisions and action.

Search articles

Cutting Docker Build Times from 106s to 44s — Practical CI Build Acceleration in Client Development for 2026

Table of contents · 11 items

"It takes far too long from git push to production deployment," "CI Docker builds force us to wait several minutes every time," "Usage charges for CI mount up in direct proportion to build counts"—when handling custom development and maintenance for systems, we hear these frustrations regularly. The catalyst for this article, 3 Improvements That Accelerated Docker Builds from 106s to 44s and 32s to 3s (Zenn) (2026-06-08), was a real-world case study where a multi-container project cut build times by more than half, and in some cases to a tenth, solely through unglamorous tweaks: reordering instructions, leveraging caches, and excluding unneeded files. Without adding special external tools, simply fixing how Dockerfiles and CI workflows were written made a massive difference.

In client development, this is not about bragging rights over whether a build is fast or slow. Build duration directly translates to deployment lead time, CI usage billing, and developer idle time. When a 100-second build runs dozens of times a day, the accumulated delay erodes developer experience and inflates operational budgets. When delivering systems for clients, the core question is: "Can we optimize the Dockerfile and configure CI caching so that builds are fast, maintainable, and dependable when handed off?" Connecting with the strategies for slashing runtime costs discussed in Optimizing Cloud Costs by Reducing Backend Memory Usage (GH Media), this article structures "CI build acceleration support" as a client service package.

Why builds slow down — How layer caching works

A Docker image is built from a stack of layers corresponding to individual instructions (FROM, COPY, RUN, ...). During a build, Docker caches the result of each instruction, and if the inputs match the previous run, it reuses the cache without re-executing. Conversely, once a cache miss occurs at a given instruction, every subsequent instruction must be re-executed from scratch. This cascade is the true culprit behind slow builds.

DimensionSlow builds (common state)Fast builds (after optimization)
Instruction orderVolatile source copying comes firstInfrequently changed dependency installs come first
Cache scopeLayer cache only+ Cache mounts preserving downloaded caches
Build contextSends entire directory including unneeded filesMinimized using .dockerignore
Image structureBuild tools bundled into production imageMulti-stage builds packaging only final artifacts
CI reuseRebuilt from scratch on every runShared via registry or GHA cache

In other words, architectural flaws such as "bundling dependency installs and source copying into the same layer," "transferring extraneous files into the build context," or "lacking operational caching in CI" cause heavy rebuilds even when editing a single line of code. Acceleration is simply the process of breaking this chain of re-execution and locking unchanging stages into cache.

Specific acceleration techniques

1. Instruction reordering and cache layer separation

The most impactful change is separating "dependency installation" and "source code copying" into distinct layers, placing the infrequently changed dependencies first. By copying only package.json first, installing dependencies, and only then copying the remainder of the source tree, the dependency installation layer stays cached even when you modify application code.

# 改善前: ソースを全部コピーしてから install
# → ソースを1行直すたびに npm install が再実行される
COPY . .
RUN npm ci

# 改善後: 依存定義だけ先にコピーして install
# → ソース変更では install 層のキャッシュが効く
COPY package.json package-lock.json ./
RUN npm ci
COPY . .

This simple reordering allows routine "edit-and-rebuild" cycles to skip dependency installations altogether. The optimization highlighted in the Zenn article fundamentally hinged on this design of cache-effective layers.

2. Preserving download caches via BuildKit cache mounts

Even if a layer cache is invalidated, you often want to preserve package manager download caches (such as npm, pip, apt, or Go modules)—and BuildKit's cache mount (--mount=type=cache) accomplishes exactly that. It provides a dedicated cache area that exists solely during the build; its key virtue is that it is not included in the final image, yet remains available for the next build.

# syntax=docker/dockerfile:1
FROM node:22-slim
WORKDIR /app
COPY package.json package-lock.json ./
# npm のキャッシュディレクトリを cache mount で保持
RUN --mount=type=cache,target=/root/.npm \
    npm ci
COPY . .
RUN npm run build

Layer caching (storing instruction outputs) and cache mounts (persisting download directories) are separate mechanisms, and combining both compounds their benefits. Even if the layer cache is invalidated in step 1, a cache mount prevents packages from having to be downloaded over the network again.

Multi-stage builds separate the "build stage" from the "runtime stage," ensuring only final compilation artifacts are copied into the production image. By excluding compilers, build tools, and raw source files from production, the final image becomes significantly smaller, transfers and boots faster, and minimizes its attack surface. Furthermore, using COPY --link makes downstream caches less vulnerable to changes in source layers, increasing cache hit ratios.

# syntax=docker/dockerfile:1
# --- build stage ---
FROM node:22 AS build
WORKDIR /app
COPY package.json package-lock.json ./
RUN --mount=type=cache,target=/root/.npm npm ci
COPY . .
RUN npm run build

# --- runtime stage ---
FROM node:22-slim AS runtime
WORKDIR /app
ENV NODE_ENV=production
# 成果物だけを --link で持ち込む
COPY --link --from=build /app/dist ./dist
COPY --link --from=build /app/node_modules ./node_modules
CMD ["node", "dist/server.js"]

Simply decoupling "what is needed to build" from "what is needed to run" delivers both compact image sizes and build stability. For broader efficiencies in container lifecycle operations, please also read Container Operations with the Docker Gordon AI Agent (GH Media).

4. Strict .dockerignore enforcement

When starting a build, Docker transfers the entire build context (the current working directory) to the daemon. If unneeded files like .git, node_modules, logs, or build artifacts are transmitted, time is wasted just uploading data, and unrelated file edits invalidate caches unnecessarily. Filtering build context via .dockerignore is a high-yield step that is frequently overlooked.

# .dockerignore の例
.git
node_modules
dist
*.log
.env
.env.*
Dockerfile
.dockerignore

Excluding .env patterns is particularly vital for preventing accidental leakage of sensitive credentials, as discussed below.

5. Sharing build caches across CI

A cache that works locally but fails in CI due to a clean environment on every run is the classic reason why builds are "slow only in CI." By exporting the BuildKit cache to a registry or using the GitHub Actions cache backend (type=gha), you can share build caches across jobs.

# GitHub Actions: BuildKit キャッシュを GHA に共有する例
- name: Set up Buildx
  uses: docker/setup-buildx-action@v3

- name: Build and push
  uses: docker/build-push-action@v6
  with:
    context: .
    push: true
    tags: registry.example.com/app:latest
    cache-from: type=gha
    cache-to: type=gha,mode=max

Setting mode=max ensures that intermediate stage caches are preserved, raising hit rates even across multi-stage pipelines. Keep in mind that cache mount contents are not automatically preserved in GHA caches without dedicated workarounds. For baseline considerations on infrastructure selection, refer to Infrastructure Selection: Cloudflare vs. AWS (GH Media).

The 5 phases of "CI build acceleration support" offered in client development

Phase 1: Measurement and diagnosis (1 week)

  • Auditing current Dockerfiles and CI configurations
  • Measuring build duration, cache hit rates, and image sizes
  • Identifying exactly which instructions invalidate the cache
  • Deliverable: Build bottleneck diagnosis report + improvement priority matrix

Phase 2: Design (1 week)

  • Formulating policies for layer separation, cache mounts, and multi-stage builds
  • Selecting CI cache sharing strategies (registry vs. GHA)
  • Setting target metrics for post-optimization build times and image sizes
  • Deliverable: Optimization design specification + before/after projection

Phase 3: Implementation (1–3 weeks)

  • Refactoring Dockerfiles (instruction order, cache mounts, COPY --link)
  • Configuring .dockerignore and minimizing the build context
  • Integrating cache-sharing configurations into CI workflows
  • Deliverable: Optimized Dockerfile/CI configs + implementation standard documentation

Phase 4: Verification and handover (1 week)

  • Comparative measurement of build times, costs, and image sizes before and after
  • Validating cache invalidation scenarios to ensure predictable reproducibility
  • Deliverable: Impact evaluation report + operational runbook

Phase 5: Continuous maintenance (ongoing)

  • Monitoring cache degradation when adding dependencies or updating base images
  • Tracking CI cache hit ratios at regular intervals
  • Assisting with build architecture when onboarding new services

Implementation standards set for custom development

ApplicationRecommendationAvoid
Dependencies and source codeSeparate layers, placing dependencies firstInstalling after COPY . .
Download cache--mount=type=cacheRe-downloading everything every time
Image structureMulti-stage + slim/distrolessBloated images bundling build toolchains
CopyingStabilized via COPY --linkBulk-copying giant directories
Build contextMinimized using .dockerignoreSending entire repository
CI cachingregistry / type=gha,mode=maxNo cache configured, rebuilding from zero every run

Which projects need this and which do not

Projects requiring thisLow-priority projects
CI builds take several minutes, blocking deploymentsSmall scale where builds complete in seconds
High deployment frequency where waiting times accumulateDeployments occur only a few times a month
CI pay-as-you-go bills are inflatingAdequately covered by free tiers
Images are massive, causing slow transfers and boot timesImages are already lightweight
Building multiple services concurrentlySingle, simple architecture

Six clauses to include in custom development contracts

ClauseDetailsWhat the client should verify
Target scopeServices and Dockerfiles targeted for optimizationTarget scope boundaries
Target metricsBuild time and image size targetsMeasurement definitions
CI prerequisitesTarget CI platforms, permissions, and caching environmentsDeployment target consistency
SecretsHandling of credentials during buildsCriteria for preventing credential leaks
HandoverDockerfiles, CI configurations, and runbooksMaintenance framework
Ongoing maintenanceMonitoring cache degradationOperating costs

Client-side estimated ROI (assuming a team with frequent CI builds)

ItemBefore optimizationAfter optimizationDifference
Build timeAround 100 secondsA few seconds to tens of secondsShortened lead times
CI usage-based billingProportional to build durationSubstantially compressedReduced monthly costs
Deployment frequencyConstrained by wait timesFrictionless, frequent releasesHigher release cadence
Developer wait timeOccurs each timeVirtually eliminatedImproved developer experience
Annual benefitCost reduction + faster development velocity

Even an initial assessment (starting from 200,000 yen) provides immediate value simply by quantifying where caches break and exactly how many seconds can be eliminated. Because build overhead accrues every day, a single optimization delivers enduring returns.

Five common pitfalls to avoid

Pitfall 1: Invalidating caches through careless instruction placement

Placing COPY . . ahead of dependency installation triggers a full rebuild every time source code is touched. Always order infrequently changing layers first, and volatile layers last.

Pitfall 2: Leaking secrets during the build

Hardcoding API keys or .env into images using ARG or COPY causes them to persist in layer history and leak. Exclude them with .dockerignore, and inject necessary credentials via --mount=type=secret.

Pitfall 3: Discrepancies between CI and local cache states

When builds are fast locally but build from scratch in CI, configure CI cache sharing and benchmark directly on CI to close the gap.

Pitfall 4: Leaving image sizes bloated

Leaving build tools and superfluous packages inside production containers slows image transfer and startup while widening attack surfaces. Use multi-stage builds to package only final artifacts.

Pitfall 5: Obscuring readability through over-optimization

Overcrowding a Dockerfile with complex cache mounts and conditionals results in unmaintainable code nobody dares touch. Prioritize high-impact changes first and keep optimizations within a maintainable scope.

90-day action plan

WeekAction
Week 1Measuring build duration, cache hit rates, and image sizes
Week 2Root cause analysis + setting optimization policy and targets
Week 3〜5Dockerfile refactoring + .dockerignore configuration + CI cache setup
Week 6Comparative before/after benchmarking + runbook documentation
Week 7〜13Cache hit rate monitoring + rollout to additional services

Summary — Moving from "getting builds to pass somehow" to "delivering fast, maintainable builds"

Accelerating Docker builds does not demand esoteric tooling; it is achieved through methodical compounding of instruction reordering, cache mounts, multi-stage builds, .dockerignore, and shared CI caching. Turning 106 seconds into 44 seconds, or 32 seconds into 3 seconds, is not magic—it is the direct consequence of re-architecting for cache efficiency. When delivering solutions in custom development, our "CI build acceleration support"optimizing Dockerfiles and CI workflows, validating results with numbers, and handing off maintainable runbooks—serves as a primary service to slash deployment lead times and cloud CI bills simultaneously. To extend these optimizations to production performance guarantees, also read Performance Assurance with k6 Load Testing (GH Media).

If you are experiencing issues where "slow CI builds stall deployments," "you want to reduce usage-based build costs," or "you need Dockerfiles refactored into a clean, maintainable structure," feel free to contact us via our contact form.

Sources

Share this articleXFacebook
Kakeru Suzuki

Fascinated by the possibilities of technology, has had a deep interest in programming and digital art since student days

Turn this article's theme into your company's next step

Concrete steps forward for your organization.

We organize your desired architecture, legacy systems, and operational requirements to formulate your next steps toward execution.

  • Desired architecture
  • Integration with existing environments
  • Operational requirements
Consult on development & operations initiatives

You can consult with us from the initial conceptual stage. Details from this article will be carried over to the inquiry form.

Receive the latest articles by email