I have a monorepo setup for a rust project that looks sort of like this
<code>├── Cargo.toml
├── apps
│ ├── app1
│ ├── app2
├── libs
│ ├── app1lib
│ ├── app2lib
│ ├── common
</code>
├── Cargo.toml
├── apps
│ ├── app1
│ ├── app2
├── libs
│ ├── app1lib
│ ├── app2lib
│ ├── common
Where the Cargo.toml is a virtual manifest specifying the workspace, its members and some common dependencies. And each of the apps are rust binaries, and each of the libs are, well, libs.
Now the problem is, that I want to make Dockerfiles for the two apps.
My initial idea was to just copy everything in and then build. However, my issue is that app2lib has
<code>[lib]
crate-type = ["cdylib"]
</code>
[lib]
crate-type = ["cdylib"]
and building the docker image for app1 on my mac gives the following error
<code>error: cannot produce cdylib for `app2lib v0.0.1 (/app/libs/app2lib)` as the target `aarch64-unknown-linux-musl` does not support these crate types
14.90 thread 'main' panicked at /usr/local/cargo/registry/src/index.crates.io-6f17d22bba15001f/cargo-chef-0.1.67/src/recipe.rs:218:27:
14.90 Exited with status code: 101
14.90 note: run with `RUST_BACKTRACE=1` environment variable to display a backtrace
<code>error: cannot produce cdylib for `app2lib v0.0.1 (/app/libs/app2lib)` as the target `aarch64-unknown-linux-musl` does not support these crate types
14.90 thread 'main' panicked at /usr/local/cargo/registry/src/index.crates.io-6f17d22bba15001f/cargo-chef-0.1.67/src/recipe.rs:218:27:
14.90 Exited with status code: 101
14.90 note: run with `RUST_BACKTRACE=1` environment variable to display a backtrace
</code>
error: cannot produce cdylib for `app2lib v0.0.1 (/app/libs/app2lib)` as the target `aarch64-unknown-linux-musl` does not support these crate types
14.90 thread 'main' panicked at /usr/local/cargo/registry/src/index.crates.io-6f17d22bba15001f/cargo-chef-0.1.67/src/recipe.rs:218:27:
14.90 Exited with status code: 101
14.90 note: run with `RUST_BACKTRACE=1` environment variable to display a backtrace
Now, while there probably are ways of solving this, I really would like to only include those workspace dependencies in the Dockerfile that the app needs, say
<code>COPY libs/app1lib
COPY libs/common
# And so on
</code>
COPY libs/app1lib
COPY libs/common
# And so on
However, then I run into a Cargo workspace issue
<code>0.108 Error: Failed to compute recipe
0.108 0: Cannot extract Cargo metadata
0.108 1: `cargo metadata` exited with an error: error: failed to load manifest for workspace member `/app/libs/app2lib`
0.108 referenced by workspace at `/app/Cargo.toml`
0.108 failed to read `/app/libs/app2lib/Cargo.toml`
0.108 No such file or directory (os error 2)
<code>0.108 Error: Failed to compute recipe
0.108
0.108 Caused by:
0.108 0: Cannot extract Cargo metadata
0.108 1: `cargo metadata` exited with an error: error: failed to load manifest for workspace member `/app/libs/app2lib`
0.108 referenced by workspace at `/app/Cargo.toml`
0.108
0.108 Caused by:
0.108 failed to read `/app/libs/app2lib/Cargo.toml`
0.108
0.108 Caused by:
0.108 No such file or directory (os error 2)
</code>
0.108 Error: Failed to compute recipe
0.108
0.108 Caused by:
0.108 0: Cannot extract Cargo metadata
0.108 1: `cargo metadata` exited with an error: error: failed to load manifest for workspace member `/app/libs/app2lib`
0.108 referenced by workspace at `/app/Cargo.toml`
0.108
0.108 Caused by:
0.108 failed to read `/app/libs/app2lib/Cargo.toml`
0.108
0.108 Caused by:
0.108 No such file or directory (os error 2)
Which makes sense since it isn’t in the Docker image.
So my question is: How do I best go about this? What is the best approach to having a cargo workspace with all members there, while still creating dockerfiles with only the neccesary dependencies included?
For completeness, this is roughly the definition of the app1 Dockerfile (which is run from the repo root
<code>FROM lukemathwalker/cargo-chef:latest-rust-alpine AS chef
COPY Cargo.toml Cargo.toml
COPY Cargo.lock Cargo.lock
# Depending on approach...
# COPY libs/app1lib libs/app1lib
# COPY libs/common libs/common
COPY --from=planner /app/recipe.json .
RUN cargo chef cook --release
RUN cargo build --release --bin app1
RUN mv ./target/release/app1 ./app
FROM gcr.io/distroless/static-debian12 AS runtime
COPY --from=builder /app/app /usr/local/bin/
ENTRYPOINT ["/usr/local/bin/app"]
<code>FROM lukemathwalker/cargo-chef:latest-rust-alpine AS chef
WORKDIR /app
FROM chef AS planner
COPY Cargo.toml Cargo.toml
COPY Cargo.lock Cargo.lock
COPY apps/app1 apps/app1
# Depending on approach...
COPY libs libs
# Or
# COPY libs/app1lib libs/app1lib
# COPY libs/common libs/common
RUN cargo chef prepare
FROM chef AS builder
COPY --from=planner /app/recipe.json .
ENV SQLX_OFFLINE true
RUN cargo chef cook --release
COPY . .
RUN cargo build --release --bin app1
RUN mv ./target/release/app1 ./app
FROM gcr.io/distroless/static-debian12 AS runtime
WORKDIR /app
COPY --from=builder /app/app /usr/local/bin/
ENTRYPOINT ["/usr/local/bin/app"]
</code>
FROM lukemathwalker/cargo-chef:latest-rust-alpine AS chef
WORKDIR /app
FROM chef AS planner
COPY Cargo.toml Cargo.toml
COPY Cargo.lock Cargo.lock
COPY apps/app1 apps/app1
# Depending on approach...
COPY libs libs
# Or
# COPY libs/app1lib libs/app1lib
# COPY libs/common libs/common
RUN cargo chef prepare
FROM chef AS builder
COPY --from=planner /app/recipe.json .
ENV SQLX_OFFLINE true
RUN cargo chef cook --release
COPY . .
RUN cargo build --release --bin app1
RUN mv ./target/release/app1 ./app
FROM gcr.io/distroless/static-debian12 AS runtime
WORKDIR /app
COPY --from=builder /app/app /usr/local/bin/
ENTRYPOINT ["/usr/local/bin/app"]