Docker missing context

Summary

A misconfigured Docker build context caused the Rust workspace to compile the wrong crate, leading to unexpected artifacts being copied into the final image. The COPY step behaved unpredictably because Docker was sending an incorrect or incomplete build context, influenced by .dockerignore and directory layout.

Root Cause

The failure stemmed from Docker’s build context not matching the expected project structure. Specifically:

  • The build context (context: .) included directories that confused Cargo’s workspace resolution.
  • .dockerignore excluded critical folders (migration/, target/, .env) but did not exclude other workspace crates, causing Cargo to detect and build the wrong package.
  • The Dockerfile copied multiple directories (api bot cron entity) without isolating the crate intended for the bot binary.
  • Cargo automatically built the first matching crate in the workspace when invoked without explicit workspace configuration.
  • The final COPY --from=builder step referenced a binary path that did not correspond to the crate Cargo actually built.

Why This Happens in Real Systems

This class of issue is extremely common in multi-crate Rust workspaces:

  • Cargo auto-detects workspaces based on directory structure.
  • Docker build contexts often include more files than intended, especially when .dockerignore is incomplete.
  • Engineers assume Docker’s context is “just the current directory,” but Docker actually tars and ships the entire context, which may differ from the local filesystem.
  • Rust workspaces behave differently inside Docker because the build context is not the same as the local checkout.

Real-World Impact

These issues can cause:

  • Wrong binaries being built and deployed.
  • Silent miscompilation, where the build succeeds but produces the wrong artifact.
  • Bloated images due to unnecessary crates being included.
  • Slow builds because Cargo compiles crates that were never intended for that image.
  • Hard-to-debug COPY errors when paths don’t match expected outputs.

Example or Code (if necessary and relevant)

A corrected Dockerfile pattern for multi-crate Rust workspaces:

FROM rust:1.93-bookworm as builder
WORKDIR /app

# Copy only the bot crate and shared workspace files
COPY Cargo.toml Cargo.lock ./
COPY bot ./bot
COPY entity ./entity

RUN cargo build --release -p bot

FROM debian:bookworm-slim as base
WORKDIR /app

FROM base as bot
COPY --from=builder /app/target/release/bot /usr/local/bin/bot
CMD ["bot"]

How Senior Engineers Fix It

Experienced engineers typically:

  • Minimize the Docker build context to only what is required.
  • Explicitly copy only the crate(s) needed for the image.
  • Use .dockerignore aggressively to avoid workspace pollution.
  • Use cargo build -p <crate> with a clean, isolated context.
  • Split workspace crates into separate Docker contexts when appropriate.
  • Validate the build context using:
    • docker build . --no-cache --progress=plain
    • docker context inspect
  • Ensure the Dockerfile mirrors the exact directory structure expected by Cargo.

Why Juniors Miss It

Less experienced engineers often overlook:

  • That Docker’s build context is not the same as the local filesystem.
  • How .dockerignore silently alters what Cargo sees.
  • That Cargo workspaces are auto-discovered, not explicitly declared.
  • That copying entire directories (COPY . .) introduces hidden crates into the build.
  • That multi-crate Rust projects require precise control over what gets included in the build context.

They assume Docker “just builds what’s in the folder,” but in reality Docker builds whatever is in the context tarball, which may be very different from what they expect.

Leave a Comment