Docs menu

Customizing the build

How to shape the Rust build on Primcloud: which binaries ship, release profile settings, Cargo configuration, native dependencies, and Debian packages via project.toml.

On this page

Most Rust apps deploy on Primcloud with no custom build steps. The build compiles every binary in your package or workspace in release mode and ships the results. When you need more, Cargo's own configuration covers the common cases: which binaries get built, how the release profile is tuned, how native code is compiled, and how OS-level libraries are installed.

There are no pre- or post-build hook scripts for Rust. Anything that needs to run during the build has to fit into what cargo build does: a build.rs script, a Cargo profile setting, or the frontend build that runs ahead of Cargo when the repo has a package.json.

Selecting binaries to build#

By default, every binary target of every workspace member is compiled and shipped. The build command is fixed, so selection happens in your manifests:

Gate a binary behind a feature. Cargo skips a binary whose required-features aren't enabled, and the build passes no feature flags, so only default features are on:

Cargo.toml
[features]
dev-tools = []

[[bin]]
name = "seed"
required-features = ["dev-tools"]

seed isn't built or shipped, and it gets no process. The build log lists it as skipped. To ship a feature-gated binary, add its feature to default.

Choose which binaries run. Building a binary doesn't mean running it. Without a Procfile, every shipped binary gets a process type, but only web starts with a replica. The rest sit at zero until you scale them. To name the processes yourself, ship a Procfile:

Procfile
web: api
worker: worker

Release profile#

The build always runs with --release. Tune that profile in the root Cargo.toml the way you would locally:

Cargo.toml
[profile.release]
lto = "thin"
codegen-units = 1
strip = true

Cargo also reads profile settings from the environment, so you can change them per environment without a commit. Set them in the Secrets panel:

  • CARGO_PROFILE_RELEASE_DEBUG=true: keep debug info in the deployed binary.
  • RUSTFLAGS: extra flags passed to every rustc invocation.

A .cargo/config.toml in the repo is honored too, including build.target. The build installs binaries from wherever Cargo reports writing them.

Build scripts#

build.rs scripts run as part of cargo build, exactly as they do locally. That's the place for code generation, compiling bundled C sources, or embedding build metadata.

Native dependencies#

Two shapes of "native" your Rust app can lean on:

C and C++ compiled from source. Crates that build bundled native code (through the cc or cmake crates, say) need a compiler at build time. gcc, g++, make, and cmake are available during the build, so these compile as they would on a Linux workstation.

OS-level shared libraries. Crates that link against a system library need its headers at build time and the shared object at runtime. When a library isn't in Primcloud's base image, install it through project.toml, covered below.

Debian packages#

When your app needs OS-level libraries or CLI tools that aren't in Primcloud's base image, declare them in a project.toml at the repo root. Primcloud's build installs them automatically when it finds the [com.heroku.buildpacks.deb-packages] table:

project.toml
[_]
schema-version = "0.2"

[com.heroku.buildpacks.deb-packages]
install = [
  "libgeos-dev",
  "libpq-dev",
]

Each entry is a Debian package name from the Ubuntu 24.04 archive, the base the run image is built on. Installed packages and their libraries are added to LD_LIBRARY_PATH for both build and runtime, so the linker resolves them during cargo build and the running binary finds the shared objects, with no extra config.

Advanced options (rarely needed):

  • Skip transitive dependencies: { name = "some-package", skip_dependencies = true }
  • Force reinstall: { name = "some-package", force = true }
  • Install from a URL: download = ["https://example.com/package-1.2.3.deb"]
  • Add a custom Debian repository: a [[com.heroku.buildpacks.deb-packages.sources]] table with uri, suites, components, arch, and signed_by (the source key as an ASCII-armored PGP block).