Skip to content

Quickstart: run an app with StageX

Build and run a tiny Rust program with the StageX Rust pallet and Docker.

How it works

StageX is designed as a drop-in replacement for distribution images such as Alpine or Debian, so this workflow should feel familiar if you already build container images.

StageX supports common application languages and toolchain ecosystems, including C/C++, Cython, Go, Lua, Node.js, PHP, Python, Ruby, Rust, and Solidity. This guide uses the StageX Rust pallet to build a tiny Rust program as a static binary, then copies it into a minimal scratch image.

What you need

Before you begin, install Docker to build and run the application image.

Create the app

Create a small Rust project that Cargo can build inside the StageX Rust environment.

Start by creating a new Rust project directory:

mkdir hello-stagex
cd hello-stagex
mkdir src

Add Cargo.toml with the package metadata:

[package]
name = "hello-stagex"
version = "0.1.0"
edition = "2021"

Next, add src/main.rs with a small program:

fn main() {
    println!("Hello from StageX!");
}

With the application files in place, the next step is to define the container build.

Add a Containerfile

Create Containerfile to build the Rust binary with StageX and package it in a minimal runtime image:

FROM docker.io/stagex/pallet-rust@sha256:2fbe7b164dd92edb9c1096152f6d27592d8a69b1b8eb2fc907b5fadea7d11668 AS build
WORKDIR /app
COPY Cargo.toml ./
COPY src/ ./src/

RUN --network=none <<EOF
    ARCH="$(uname -m)"
    RUSTFLAGS="-C target-feature=+crt-static" \
    cargo build \
        --release \
        --target "${ARCH}-unknown-linux-musl" \
        --bin hello-stagex
    cp "target/${ARCH}-unknown-linux-musl/release/hello-stagex" /hello-stagex
EOF

FROM scratch AS runtime
COPY --from=build /hello-stagex /hello-stagex
ENTRYPOINT ["/hello-stagex"]

The important choices are:

  • The build stage uses the StageX Rust pallet as the compiler environment.
  • The binary is statically linked, to minimize required dependencies during runtime.
  • The final image used as a runtime is made from scratch, so it contains only the app binary. Alternatively the binary can be exported from the image to be used elsewhere.

This example pulls the pallet from Docker Hub. StageX images are also available from Quay; keep the image pinned by digest if you use a different registry.

Build and run

Build the runtime image from Containerfile, load it into Docker, and run it locally:

docker buildx build -f Containerfile --target runtime --load -t hello-stagex:latest .
docker run --rm hello-stagex:latest

The container should print:

Hello from StageX!

Add dependencies later

For projects with external crates, keep Cargo.lock committed and split dependency download from compilation:

RUN cargo fetch
RUN --network=none cargo build --frozen --release

The fetch step may use the network. The compile step should not.

Make your application image reproducible

StageX pallets are reproducible and full-source bootstrapped. That gives your application a verifiable toolchain and base image, but it does not automatically make your application image reproducible.

Follow this section if you want to check byte-for-byte reproducibility for the application image. To get matching rebuilds, your application build also needs stable inputs and stable image output.

Before running the reproducibility commands, check your build environment:

  • Build on an x86_64 system for this workflow.
  • Enable Docker's containerd image store.
  • Use a Buildx builder with OCI exporter support; for Docker, use the docker-container driver.

Then keep the application build deterministic:

  • Pin the StageX pallet by digest.
  • Lock application dependencies, such as Cargo.lock.
  • Keep compilation hermetic with RUN --network=none.
  • Disable Buildx provenance attestations for byte-for-byte archive comparison.
  • Export an OCI archive with fixed timestamps and forced compression.

Then run these commands in order:

  1. Build a reproducible Open Container Initiative (OCI) archive.

    mkdir -p out
    
    SOURCE_DATE_EPOCH=1 docker buildx build -f Containerfile . \
        --platform linux/amd64 \
        --build-arg BUILDKIT_MULTI_PLATFORM=1 \
        --target runtime \
        --provenance=false \
        --output type=oci,rewrite-timestamp=true,force-compression=true,dest=out/hello-stagex.tar,name=hello-stagex:latest
    
  2. Load and run the archive.

    docker image load --input out/hello-stagex.tar
    docker run --rm hello-stagex:latest
    
  3. Rebuild without cache and compare.

    SOURCE_DATE_EPOCH=1 docker buildx build -f Containerfile . \
        --no-cache \
        --platform linux/amd64 \
        --build-arg BUILDKIT_MULTI_PLATFORM=1 \
        --target runtime \
        --provenance=false \
        --output type=oci,rewrite-timestamp=true,force-compression=true,dest=out/hello-stagex-rebuild.tar,name=hello-stagex:latest
    
    sha256sum out/hello-stagex.tar out/hello-stagex-rebuild.tar
    

The two archive hashes should match. If they do not, check for unlocked dependencies, generated files, timestamps, host-specific paths, or build steps that still use the network.

Next steps

You have built and run an application with StageX. Here's what to explore next: