2025-05-11

Debugging Slow Docker Builds in Next.js (Tailwind CSS v4.1 Fix Inside)

Docker builds on macOS shouldn't crawl—but they often do when Tailwind gets chatty. Here's why they slow down and the quick fix that slashes build time.

If you're working with monorepos, check out my Fun with Monorepos guide for setup tips. Curious about my full dev setup? See What I Use. For Node.js version management with Bun or Fish shell, see Using asdf to solve NVM for Fish.


1 — Why Docker Builds Crawl

Tailwind's Oxide engine (Tailwind CSS v4 announcement) can read tens of thousands of files every build, and Docker‑for‑Mac's overlay filesystem is painfully slow at large directory walks (Docker‑for‑Mac filesystem performance issue).


2 — Baseline the Pain

time bun run build   # local
time docker build .              # container

Anything over a 3× delta is worth digging into.


3 — Turn the Lights On

Add this just before the expensive layer in your Dockerfile:

ENV DEBUG=*
RUN bun run build

Pipe BuildKit logs to avoid the console clamp:

docker buildx build --progress=plain . | tee build.log

4 — Trace the Bottleneck

Look through build.log for:

  • tailwindcss_oxide: Reading … file(s) spam
  • Long t= durations in Next.js output
  • Repeated cache misses

These clues point to disk I/O churn, not CPU limits.


5 — Fix: Upgrade Tailwind CSS → v4.1+

Tailwind v4 introduced Oxide for speed (Oxide engine preview), but v4.1 trims unnecessary scans, skips irrelevant files, and threads its work (v4.1 release notes, community reports). Upgrade and rebuild:

bun add tailwindcss@latest        # or npm install tailwindcss@latest
docker build .

My main build step dropped 85 s → ≈30 s, with the full image down from 140 s → < 60s.


6 — Extra Optimizations (Optional)

TweakWhy it Helps
.dockerignore node_modules, .next, dist, logs, testSmaller context, faster COPY
Persist .next/cacheWarm cache beats cold starts
Narrow globs or use @source not to ignore big paths (new in v4.1)Fewer files scanned
Raise BuildKit log caps with BUILDKIT_STEP_LOG_MAX_SIZE/SPEEDNo more clipped logs

Bonus Tip: Debugging with Verbose Logs

When you enable verbose logs (DEBUG=*), Docker may throttle output at around 200 KiB/s, showing [output clipped, log limit …] (BuildKit log‑size issue, Buildx discussion). This doesn't directly cause slow builds, but it can make debugging harder by hiding logs. If you see [output clipped], try piping BuildKit logs to plain output or raising the BUILDKIT_STEP_LOG_MAX_SIZE and BUILDKIT_STEP_LOG_MAX_SPEED environment variables. See Stack Overflow example for more details.


7 — Conclusion

Slow Docker builds are almost always an I/O problem. Light up verbose logs, pinpoint the choke point, and upgrade the offending dependency. For Next.js projects, the single most effective change is moving to Tailwind CSS v4.1+—everything else is just cleanup.