Building an Alpine image for my Rust project which uses OpenSSL

· ·

Recently I was writing a Rust project using reqwest to call an API or two and I ran into an issue… When I decided to deploy the small project into a Container and run it as a small service but I couldn’t compile the code.

But why?

It turns out its because of two reasons:

  1. I was using an Alpine image (a minimal Linux distribution) which uses musl libc (not glibc).
  2. OpenSSL issues because reqwest uses it for TLS via OpenSSL.

Here is a sample image to resolve the issues.

Alpine Image

Here is the image and the break down.

Container Image:

FROM docker.io/library/rust:1.80-alpine as build

WORKDIR /app

COPY . .

RUN apk add --no-cache pkgconf alpine-sdk openssl-dev perl musl-dev && \
    rustup target add x86_64-unknown-linux-musl && \
    cargo build --release --target x86_64-unknown-linux-musl


FROM docker.io/library/alpine:3.20

WORKDIR /app

COPY --from=build /app/target/x86_64-unknown-linux-musl/release/rust-alpine-openssl /app/rust-alpine-openssl

ENTRYPOINT [ "/app/target/release/rust-alpine-openssl" ]

Breakdown

  1. Create base image as a build container
FROM docker.io/library/rust:1.80-alpine as build

This first step allows us to create an image with all the tools / libraries we need to compile the code but not have all the build-time dependencies in our run-time container.

  1. Create working directory + copy the code into our container
WORKDIR /app

COPY . .

Note: You may want a .dockerignore file to help not copy everything into the container.

  1. Install deps and build the project

Install all the build-time dependencies

apk add --no-cache pkgconf alpine-sdk openssl-dev perl musl-dev

The first part of the run command installs using apk (Alpine’s package manager) all the build tools we’ll need for our project. This includes openssl-dev (OpenSSL) and other tools such as pkgconf (pkgconf), alpine-sdk (Alpine SDK), perl (Perl), and musl-dev (musl libc).

Install Rust target:

rustup target add x86_64-unknown-linux-musl

This installs the musl target using rustup so the Rust toolchain can produce a musl-linked binary.

Compile / Build the project

Last step on the build container is to compile a release artifact of the Rust project with Cargo, making sure to specify the target (from previous step).

  1. Define our final image
FROM docker.io/library/alpine:3.20

We define a new vanila alpine v3.20 image withour Rust and the other tools / libraries. This is because we don’t need these dynamic libraries at runtime as they are statically compiled into our binary.

  1. Create and sets working directory
WORKDIR /app

This creates and sets the working directory for our final image.

  1. Copy the compiled binary from the build container
COPY --from=build /app/target/x86_64-unknown-linux-musl/release/rust-alpine-openssl /app/rust-alpine-openssl

This copies the compiled binary from the build container into our final image.

  1. Define the entrypoint
ENTRYPOINT [ "/app/target/release/rust-alpine-openssl" ]

This defines the entrypoint for our container to run the compiled binary.

Conclusion

And there you have it, a small Rust project compiled and running in an Alpine container using OpenSSL. This is a great way to create small, efficient containers for your Rust projects that use OpenSSL. Happy Coding!