dockerrustdockerfileactix-web

Cache Rust dependencies with Docker build


I have hello world web project in Rust + Actix-web. I have several problems. First is every change in code causes recompiling whole project including downloading and compiling every crate. I'd like to work like in normal development - it means cache compiled crates and only recompile my codebase. Second problem is it doesn't expose my my app. It's unreachable via web browser

Dockerfile:

FROM rust

WORKDIR /var/www/app

COPY . .

EXPOSE 8080

RUN cargo run

docker-compose.yml:

version: "3"
services:
  app:
    container_name: hello-world
    build: .
    ports:
      - '8080:8080'
    volumes:
      - .:/var/www/app
      - registry:/root/.cargo/registry

volumes:
  registry:
    driver: local

main.rs:

extern crate actix_web;

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

fn index() -> impl Responder {
    "Hello world"
}

fn main() -> std::io::Result<()> {
    HttpServer::new(|| App::new().service(web::resource("/").to(index)))
        .bind("0.0.0.0:8080")?
        .run()
}

Cargo.toml:

[package]
name = "hello-world"
version = "0.1.0"
authors = []
edition = "2018"

[dependencies]
actix-web = "1.0"

Solution

  • Seems like you are not alone in your endeavor to cache rust dependencies via the docker build process. Here is a great article that helps you along the way.

    The gist of it is you need a dummy.rs and your Cargo.toml first, then build it to cache the dependencies and then copy your application source later in order to not invalidate the cache with every build.

    Dockerfile

    FROM rust
    WORKDIR /var/www/app
    COPY dummy.rs .
    COPY Cargo.toml .
    RUN sed -i 's#src/main.rs#dummy.rs#' Cargo.toml
    RUN cargo build --release
    RUN sed -i 's#dummy.rs#src/main.rs#' Cargo.toml
    COPY . .
    RUN cargo build --release
    CMD ["target/release/app"]
    

    CMD application name "app" is based on what you have specified in your Cargo.toml for your binary.

    dummy.rs

    fn main() {}
    

    Cargo.toml

    [package]
    name = "app"
    version = "0.1.0"
    authors = ["..."]
    [[bin]]
    name = "app"
    path = "src/main.rs"
    
    [dependencies]
    actix-web = "1.0.0"
    

    src/main.rs

    extern crate actix_web;
    
    use actix_web::{web, App, HttpServer, Responder};
    
    fn index() -> impl Responder {
        "Hello world"
    }
    
    fn main() -> std::io::Result<()> {
        HttpServer::new(|| App::new().service(web::resource("/").to(index)))
            .bind("0.0.0.0:8080")?
            .run()
    }