Build Docker Containers from a Monorepo

Written — Updated
  • A .dockerignore file is very important here to reduce the amount of context that needs to be sent to the builder process. Note that the .dockerfile file requires prepending **/ to any match that you want to apply outside of the root directory.
    • **/node_modules
      **/*.js
      **/*.ts
      **/apps
      **/target
      !apps/my-app
      apps/my-app/target
      
  • Then you will want to build with the context directory being the root of the monorepo
    • #!/bin/bash
      docker build \
        -t $NAME \
        -f Dockerfile \
        --ignorefile .dockerignore \
        ../../ # The monorepo root
      
  • Building for Rust

  • If your application is built in Rust, then you can use cargo-chef to help speed up the builds, but the default recipe needs a few directory tweaks if your application has dependencies elsewhere in the monorepo.
  • Something like this works well:
  • # This Dockerfile works with cargo chef, which prebuilds dependencies in a
    # separate Docker image to speed up builds.
    
    FROM lukemathwalker/cargo-chef:latest-rust-1 AS chef
    RUN apt-get update && apt-get install -y pkg-config libssl-dev
    WORKDIR /app/apps/my-app
    
    FROM chef as planner
    COPY ./libs/some-lib /app/libs/some-lib
    COPY ./apps/my-app /app/apps/my-app
    RUN cargo chef prepare --recipe-path recipe.json
    
    FROM chef as builder
    COPY --from=planner /app/apps/my-app/recipe.json recipe.json
    # Build dependencies - this is the caching Docker layer!
    COPY ./libs/some-lib /app/libs/some-lib
    RUN cargo chef cook --release --recipe-path recipe.json
    # Build application
    COPY ./apps/my-app /app/apps/my-app
    RUN cargo build --release --bin my-app
    
    FROM debian:bookworm-slim as runtime
    RUN apt-get update && apt-get install -y pkg-config libssl-dev ca-certificates
    RUN update-ca-certificates
    COPY --from=builder /app/apps/my-app/target/release/my-app /usr/local/bin
    ENTRYPOINT ["/usr/local/bin/my-app"]
    
  • If building from Mac or Windows, you may also see benefits from increasing the amount of RAM available to the Docker VM. For example, the default VM from Podman uses only 2GB and while the Rust compiler can run within those boundaries, it's very slow. Using a 16 or 32GB VM can speed up the compilation by an order of magnitude.

Thanks for reading! If you have any questions or comments, please send me a note on Twitter.