For enhanced flexibility, users can bring existing containerized apps to Cerebrium. These can range from standard Python apps to compiled Rust binaries, provided a functional Dockerfile is supplied to build the app.

There are multiple benefits to building using Dockerfiles on Cerebrium, including the ability to bring existing containerized apps to the platform and maintaining consistent deployment environments that are easily managed locally.

Building Dockerized Python Apps

This example demonstrates a simple FastAPI server that has been containerized:

from fastapi import FastAPI

app = FastAPI()

@app.post("/hello")
def hello():
    return {"message": "Hello Cerebrium!"}

@app.get("/health")
def health():
    return "OK"

The application is built using the following Dockerfile:

# Base image
FROM python:3.12-bookworm
RUN apt-get update && apt-get install dumb-init
RUN update-ca-certificates

# Source code
COPY . .

# Dependencies
RUN pip install -r requirements.txt

# Configuration
EXPOSE 8192
CMD ["python", "-m", "uvicorn", "main:app", "--host", "0.0.0.0", "--port", "8192"]

When creating a Dockerfile for Cerebrium, there are three key requirements:

  1. You must expose a port using the EXPOSE command - this port will be referenced later in your cerebrium.toml configuration
  2. A CMD command is required to specify what runs when the container starts (typically your server process)
  3. Set the working directory using WORKDIR to ensure your application runs from the correct location (defaults to root directory if not specified)

Update cerebrium.toml to include a custom runtime section with the dockerfile_path parameter:

[cerebrium.runtime.custom]
port = 8192
healthcheck_endpoint = "/health"
dockerfile_path = "./Dockerfile"

The configuration requires three key parameters:

  • port: The port the server listens on.
  • healthcheck_endpoint: The endpoint used to confirm server health. If unspecified, defaults to a TCP ping on the configured port.
  • dockerfile_path: The relative path to the Dockerfile used to build the app.

If a Dockerfile does not contain a CMD clause, specifying the entrypoint parameter in the cerebrium.toml file is required.

[cerebrium.runtime.custom]
entrypoint = ["uvicorn", "main:app", "--host", "0.0.0.0", "--port", "8192"]
...

When specifying a dockerfile_path, all dependencies and necessary commands should be installed and executed within the Dockerfile. Dependencies listed under cerebrium.dependencies.*, as well as cerebrium.deployment.shell_commands and cerebrium.deployment.pre_build_commands, will be ignored.

Building Generic Dockerized Apps

Cerebrium supports apps in languages other than Python, provided a Dockerfile is supplied. The following example demonstrates a Rust-based API server using the Axum framework:

use axum::{
    routing::{get, post},
    Json, Router,
};
use serde_json::json;

async fn hello() -> Json<serde_json::Value> {
    Json(json!({ "message": "Hello Cerebrium!" }))
}

async fn health() -> &'static str {
    "OK"
}

#[tokio::main]
async fn main() {
    let app = Router::new()
        .route("/hello", post(hello))
        .route("/health", get(health));
    tracing::info!("Listening on port 8192");

    let listener = tokio::net::TcpListener::bind("0.0.0.0:8192").await.unwrap();
    axum::serve(listener, app).await.unwrap();
}

In this case, a multi-stage Dockerfile is used to separate the build step, creating a smaller and more secure image for the runtime:

# Build stage
FROM rust:bookworm as build
RUN apt-get update && apt-get install dumb-init
RUN update-ca-certificates

# Project setup
RUN USER=root cargo new --bin rs_server
WORKDIR /rs_server

# Dependencies
COPY Cargo.lock ./Cargo.lock
COPY Cargo.toml ./Cargo.toml

# Cache dependencies
RUN cargo build --release
RUN rm src/*.rs

# Source code
COPY src/* src/

# Build
RUN rm ./target/release/deps/rs_server*
RUN cargo build --release

# Runtime stage
FROM gcr.io/distroless/base-debian12
WORKDIR /
COPY --from=build  /etc/ssl/certs/ca-certificates.crt /etc/ssl/certs/
COPY --from=build /lib/x86_64-linux-gnu/libgcc_s.so.1 /lib/x86_64-linux-gnu/libgcc_s.so.1
COPY --from=build /rs_server/target/release/rs_server /rs_server
COPY --from=build /usr/bin/dumb-init /usr/bin/dumb-init
EXPOSE 8192
CMD ["dumb-init", "--", "/rs_server"]

Similarly to the FastAPI webserver, the application should be configured in the cerebrium.toml file:

[cerebrium.runtime.custom]
port = 8192
healthcheck_endpoint = "/health"
dockerfile_path = "./Dockerfile"