Hello Cloudflare Container (Rust)
Here's an example for Rust on Cloudflare via Container.
TLDR;
- source code: hello-cloudflare-container
Setup
npm create cloudflare@latest -- --template=cloudflare/templates/containers-template
You will get Golang from template then just replace with Rust related below.
Config
# syntax=docker/dockerfile:1
#
# ---- Builder Stage ----
#
# ARG TARGETPLATFORM is an automatic variable provided by Docker buildx.
# It will be 'linux/amd64' or 'linux/arm64' depending on the --platform flag.
# We are setting a default value for builds that don't use the --platform flag.
ARG TARGETPLATFORM=linux/amd64
# Use a builder image with Rust installed.
FROM rust:1-alpine AS build
# Set build-time arguments that will be used in subsequent commands.
# This makes the value available inside the build stage.
ARG TARGETPLATFORM
# Install Zig, which will act as a universal C cross-compiler,
# then install cargo-zigbuild to integrate it with Cargo.
RUN apk add --no-cache zig build-base && \
cargo install cargo-zigbuild
# Add the cargo bin directory to the PATH to make cargo-zigbuild available.
ENV PATH="/root/.cargo/bin:${PATH}"
# Set the working directory.
WORKDIR /app
# Copy the Cargo files first to leverage Docker's layer caching.
COPY container/Cargo.toml container/Cargo.lock ./
# This single RUN command determines the correct Rust target, installs it,
# and builds the project's dependencies using the robust cargo-zigbuild.
RUN case ${TARGETPLATFORM} in \
"linux/amd64") RUST_TARGET="x86_64-unknown-linux-musl" ;; \
"linux/arm64") RUST_TARGET="aarch64-unknown-linux-musl" ;; \
esac && \
rustup target add ${RUST_TARGET} && \
mkdir -p src && \
echo "fn main() {}" > src/main.rs && \
cargo zigbuild --release --target ${RUST_TARGET}
# Now, copy the actual source code. This will be a separate layer.
COPY container/src/ ./src/
# Build the final application and create a stable symlink to the binary.
RUN case ${TARGETPLATFORM} in \
"linux/amd64") RUST_TARGET="x86_64-unknown-linux-musl" ;; \
"linux/arm64") RUST_TARGET="aarch64-unknown-linux-musl" ;; \
esac && \
rm -f target/${RUST_TARGET}/release/deps/server* && \
cargo zigbuild --release --target ${RUST_TARGET} && \
ln -s /app/target/${RUST_TARGET}/release/server /app/server
#
# ---- Final Stage ----
#
# Use a minimal 'scratch' image for the final container.
FROM scratch
# Copy the built binary using the stable symlink created in the build stage.
COPY --from=build /app/server /server
# Expose the port the application will listen on.
EXPOSE 8080
# Set the entrypoint for the container.
CMD ["/server"]
{{#include ../../../examples/r2/hello-cloudflare-container/container_src/Cargo.toml}}
Code
#![allow(unused)] fn main() { {{#include ../../../examples/r2/hello-cloudflare-container/container_src/src/main.rs}} }
import { Container, loadBalance, getContainer } from "@cloudflare/containers";
import { Hono } from "hono";
export class MyContainer extends Container {
// Port the container listens on (default: 8080)
defaultPort = 8080;
// Time before container sleeps due to inactivity (default: 30s)
sleepAfter = "2m";
// Environment variables passed to the container
envVars = {
MESSAGE: "I was passed in via the container class!",
};
// Optional lifecycle hooks
override onStart() {
console.log("Container successfully started");
}
override onStop() {
console.log("Container successfully shut down");
}
override onError(error: unknown) {
console.log("Container error:", error);
}
}
// Create Hono app with proper typing for Cloudflare Workers
const app = new Hono<{
Bindings: { MY_CONTAINER: DurableObjectNamespace<MyContainer> };
}>();
// Home route with available endpoints
app.get("/", (c) => {
return c.text(
"Available endpoints:\n" +
"GET /container/<ID> - Start a container for each ID with a 2m timeout\n" +
"GET /lb - Load balance requests over multiple containers\n" +
"GET /error - Start a container that errors (demonstrates error handling)\n" +
"GET /singleton - Get a single specific container instance",
);
});
// Route requests to a specific container using the container ID
app.get("/hello", async (c) => {
console.log("hello");
const containerId = c.env.MY_CONTAINER.idFromName(`/container/1`);
console.log("containerId:", containerId);
const container = c.env.MY_CONTAINER.get(containerId);
console.log("container:", container);
return await container.fetch(c.req.raw);
});
// Route requests to a specific container using the container ID
app.get("/container/:id", async (c) => {
const id = c.req.param("id");
const containerId = c.env.MY_CONTAINER.idFromName(`/container/${id}`);
const container = c.env.MY_CONTAINER.get(containerId);
return await container.fetch(c.req.raw);
});
// Demonstrate error handling - this route forces a panic in the container
app.get("/error", async (c) => {
const container = getContainer(c.env.MY_CONTAINER, "error-test");
return await container.fetch(c.req.raw);
});
// Load balance requests across multiple containers
app.get("/lb", async (c) => {
const container = await loadBalance(c.env.MY_CONTAINER, 3);
return await container.fetch(c.req.raw);
});
// Get a single container instance (singleton pattern)
app.get("/singleton", async (c) => {
const container = getContainer(c.env.MY_CONTAINER);
return await container.fetch(c.req.raw);
});
export default app;
Dev & Deploy
npm run dev
npm run deploy