I’m working on creating a scratch image as part of a test network for a system I’m building. My goal is to set up a multi-stage Docker build that allows me to compile and run a Rust application both locally (on my Mac M4) and in a GitLab pipeline using a self-hosted runner. My local deployment uses docker compose and the the final deployment is to EKS, therefore containerization is a must.
This means I need to cross-compile for different environments — aarch64/arm64 for my Mac and amd64/x86_64 for cloud-based Linux environments. However, after days of troubleshooting, I’m still stuck.
Here are some of the challenges I’ve encountered:
- Linker issues— Could I be using the wrong one? How do I pick the right linker?
- Static vs. dynamic libraries - I did a very generic hello world compilation using alpine to build and scratch to run and it worked, therefore i thought it could be my dependencies, but with no compilation problems I do not know how to see which ones?
- Alpine-related build problems—I’m still trying to fully grasp the differences between musl and glibc and how they impact my build. For example https://github.com/rust-lang/rust/issues/115430.
I’m by no means an expert in Rust or cross-compilation, so I really appreciate any guidance. Please explain it like I’m five — I’m happy to learn, and I’d love recommendations on good resources to better understand this topic.
Thanks in advance!
My approach to this task has been as follows:
- Use Debian bookworm to create an image for comparison in size - hello world, this worked.
ARG PLATFORM FROM --platform=${PLATFORM} rust:1.84.0-slim-bookworm AS build RUN apt update && apt install -y libssl-dev WORKDIR /app COPY ./hello_world_compile_test . RUN cargo build --release FROM --platform=${PLATFORM} rust:1.84.0-slim-bookworm AS prod COPY --from=build /app/target/release/hello_world_compile_test /opt/hello_world_compile_test/ CMD ["/opt/hello_world_compile_test/hello_world_compile_test"]
- Then use alpine and scratch - hello world again no dependencies - this worked.
FROM --platform=${PLATFORM} rust:1.84.0-alpine AS build ARG TARGET_APP RUN apk update && apk add openssl-dev WORKDIR /app COPY ./${TARGET_APP} . RUN rustup target add x86_64-unknown-linux-musl RUN cargo build --target x86_64-unknown-linux-musl --release RUN mv ./target/x86_64-unknown-linux-musl/release/${TARGET_APP} /app/rust_application FROM --platform=${PLATFORM} scratch AS prod COPY --from=build /app/rust_application /opt/rust_application CMD ["/opt/rust_application"]
Try to build and run my application using alpine and then
Add a debug step which was an alpine image, which worked when I installed glibc in the container however would provide errors when not installed.
ARG PLATFORM=linux/aarch64 ARG TARGET_APP=custom_rust_app # Building on alpine causes a lot of problems, there are open GH issues # Why would you use ALpine, Speed and Size FROM rust:1.84.0-alpine AS build ARG TARGET_APP=custom_rust_app RUN apk update && apk add openssl-dev musl-dev build-base curl zip protobuf protobuf-dev grpc grpc-plugins WORKDIR /app COPY ./${TARGET_APP} . RUN rustup target add aarch64-unknown-linux-musl RUN --mount=type=ssh RUSTFLAGS="-Ctarget-feature=-crt-static" cargo build --target aarch64-unknown-linux-musl --release RUN mv /app/target/aarch64-unknown-linux-musl/release/${TARGET_APP} /app/rust_application FROM scratch AS prod COPY --from=build /app/rust_application /opt/rust_application CMD ["/opt/rust_application"] FROM alpine AS debug RUN apk update && apk add openssl libgcc libstdc++ COPY --from=build /app/rust_application /opt/rust_application CMD ["/bin/sh"]
Without RUN apk update && apk add openssl libgcc libstdc++
in the debug step:
docker run -it test-scratch-mac / # /opt/rust_application Error loading shared library libgcc_s.so.1: No such file or directory (needed by /opt/rust_application) Error relocating /opt/rust_application: _Unwind_SetIP: symbol not found Error relocating /opt/rust_application: _Unwind_GetRegionStart: symbol not found Error relocating /opt/rust_application: _Unwind_GetTextRelBase: symbol not found Error relocating /opt/rust_application: _Unwind_FindEnclosingFunction: symbol not found Error relocating /opt/rust_application: _Unwind_Resume: symbol not found Error relocating /opt/rust_application: _Unwind_DeleteException: symbol not found Error relocating /opt/rust_application: _Unwind_RaiseException: symbol not found Error relocating /opt/rust_application: _Unwind_GetIP: symbol not found Error relocating /opt/rust_application: _Unwind_GetIPInfo: symbol not found Error relocating /opt/rust_application: _Unwind_GetDataRelBase: symbol not found Error relocating /opt/rust_application: _Unwind_SetGR: symbol not found Error relocating /opt/rust_application: _Unwind_GetCFA: symbol not found Error relocating /opt/rust_application: _Unwind_Backtrace: symbol not found Error relocating /opt/rust_application: _Unwind_GetLanguageSpecificData: symbol not found / #
- I then tried to go and build on book worm again and transfer to scratch, where I had the same problem of
exec /opt/rust_application: no such file or directory
FROM rust:1.84.0-slim-bookworm AS build ARG TARGET_APP=custom_rust_app ARG PLAT_RUST=aarch64 # ARG PLAT_RUST=x86_64 RUN apt update && apt install -y libssl-dev build-essential \ protobuf-compiler \ libssl-dev \ libudev-dev \ pkg-config \ zlib1g-dev \ llvm \ clang \ cmake \ make \ libprotobuf-dev WORKDIR /app COPY ./${TARGET_APP} . RUN rustup toolchain install stable-${PLAT_RUST}-unknown-linux-gnu RUN rustup target add ${PLAT_RUST}-unknown-linux-gnu RUN --mount=type=ssh RUSTFLAGS="-Ctarget-feature=-crt-static" cargo build --target ${PLAT_RUST}-unknown-linux-gnu --release RUN mv /app/target/${PLAT_RUST}-unknown-linux-gnu/release/${TARGET_APP} /app/rust_application FROM scratch AS prod COPY --from=build /app/rust_application /opt/rust_application CMD ["/opt/rust_application"]
I know there are different approaches to the docker file, however i am just trying to get it working atm not concerned about the perfect dockerfile!
An example command can be found here
docker buildx build --platform linux/aarch64 --ssh default -t test-scratch-mac -f ./Alpine_Lightweight_Rust_Dockerfile --target debug .
Where i will (for now) change the target platform as per the ARGS in the Dockefile.
libgcc_s.so.1
into the runtime environment. Put its location into theLD_PRELOAD
environment variable. Not the best fix but a very straightforward one