Docker and Rust: Containerizing Applications in Rust

онлайн тренажер по питону
Online Python Trainer for Beginners

Learn Python easily without overwhelming theory. Solve practical tasks with automatic checking, get hints in Russian, and write code directly in your browser — no installation required.

Start Course

Introduction: Why Docker and Rust are the Perfect Pair

Rust is a modern systems programming language known for its performance and memory safety. However, to deliver a Rust application to an end user or the cloud, you need to solve the environment problem. This is where Docker comes to the rescue. Containerization allows you to package a Rust application along with all its dependencies into a lightweight, isolated container. In this article, we will explore how to create an efficient and minimal Docker image for a Rust project using best practices.



1. Docker Basics for Rust Developers

Docker is a platform for developing, shipping, and running applications in containers. A container includes the application itself and everything it needs to run: libraries, system utilities, and configurations. For Rust, this is especially relevant because a compiled binary does not depend on a runtime (unlike Python or Node.js), allowing you to create very small images.

The main components you will need:

  • Dockerfile — a text file with instructions for building the image.
  • .dockerignore — a file that excludes unnecessary files from the build context.
  • Docker Compose (optional) — for orchestrating multiple containers.

Before you begin, make sure you have Docker Desktop or Docker Engine installed.



2. Creating a Dockerfile for a Rust Application

Let's consider a simple example of a web server in Rust using the actix-web framework. Our main file main.rs looks like this:

use actix_web::{web, App, HttpServer, Responder};

async fn index() -> impl Responder { "Hello, Docker and Rust!"}

#[actix_web::main]async fn main() -> std::io::Result<()> { HttpServer::new(|| { App::new().route("/", web::get().to(index)) }) .bind("0.0.0.0:8080")? .run() .await}

Now let's create a Dockerfile. The most important technique here is multi-stage build. It allows you to first build the application in an environment with the full set of Rust tools, and then copy the finished binary into a minimal final image.

// ---- First stage: build ----FROM rust:1.75 as builder

WORKDIR /app

// Copy manifests for dependency cachingCOPY Cargo.toml Cargo.lock ./

// Create an empty main.rs to build dependenciesRUN mkdir src && echo "fn main() {}" > src/main.rsRUN cargo build --release 2>/dev/null || true

// Now copy the actual source codeCOPY src ./src

// Build the final binary (dependency cache already exists)RUN cargo build --release

// ---- Second stage: final image ----FROM debian:bookworm-slim

RUN apt-get update && apt-get install -y ca-certificates && rm -rf /var/lib/apt/lists/*

WORKDIR /app

// Copy the binary from the first stageCOPY --from=builder /app/target/release/my_rust_app .

EXPOSE 8080

CMD ["./my_rust_app"]

Notice the trick with the empty main.rs. This allows Docker to cache the compiled dependencies (crates) and not rebuild them every time you change only the source code. This significantly speeds up subsequent builds.



2.1. Optimizing Image Size

The final image based on debian:bookworm-slim weighs about 80-100 MB. However, if you want a truly minimal image, you can use scratch (an empty image) or alpine. For statically compiled Rust applications, this is the ideal option. To do this, you need to change the build target:

// In the first stage (builder):RUN rustup target add x86_64-                

Blogs

Book Recommendations