Containerize Actix Web App Using Docker In A Cleaner Way

Recently came across this task to try different Rust web frameworks and Actix Web is one of the most popular frameworks. Its documentation is clean and easy to bootstrap, however, when I was trying to deploy the app using Docker, I could not find a clean Dockerfile example that contains minimal dependencies.

Luckily, when I was trying Rocket, it has a pretty clean documentation about containerize the app. After tweaking it a bit, it just worked for Actix Web app as well.

Personally, I feel more confident to follow official documents for long term maintainability. Even tho the image size coming from the build is not as small as ~60MB like in this guide, but ~145MB image is also acceptable here consider other factors.

Initiate Actix Web Sample App

Create a new Rust app:

cargo new hello-world
cd hello-world

Now add Actix Web dependency into Cargo.toml file:

[dependencies]
actix-web = "4"

Then replace the contents of src/main.rs with the following:

use actix_web::{get, post, web, App, HttpResponse, HttpServer, Responder};

#[get("/")]
async fn hello() -> impl Responder {
    HttpResponse::Ok().body("Hello world!")
}

#[post("/echo")]
async fn echo(req_body: String) -> impl Responder {
    HttpResponse::Ok().body(req_body)
}

async fn manual_hello() -> impl Responder {
    HttpResponse::Ok().body("Hey there!")
}

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

Compile and run the program:

cargo run

Test app using http://0.0.0.0:8080

Add Dockerfile and build Docker image

The original Dockerfile from Rocket document has some Rocket specific settings, simply removing those env variables would just work:

FROM docker.io/rust:1-slim-bookworm AS build

## cargo package name: customize here or provide via --build-arg
ARG pkg=hello-world

WORKDIR /build

COPY . .

RUN --mount=type=cache,target=/build/target \
    --mount=type=cache,target=/usr/local/cargo/registry \
    --mount=type=cache,target=/usr/local/cargo/git \
    set -eux; \
    cargo build --release; \
    objcopy --compress-debug-sections target/release/$pkg ./main

################################################################################

FROM docker.io/debian:bookworm-slim

WORKDIR /app

## copy the main binary
## add more files below if needed
COPY --from=build /build/main ./

EXPOSE 8080

CMD ./main

Note that, pkg ARG need to be the same as package name in Cargo.toml.

Now let’s build the image:

docker build -t hello-world-image .

Check the image size (~145MB):

Finally, run the image:

docker run -d -p 0.0.0.0:8080:8080 hello-world-image

Test the app using http://0.0.0.0:8080

Comments

Leave a Reply

Your email address will not be published. Required fields are marked *