Build Configuration
The build block in pilum.yaml says how to compile a service on the host before (or instead of) the recipe’s packaging step.
Basic Structure
build: language: go version: "1.23" cmd: "go build -o ./dist/app ." env_vars: CGO_ENABLED: "0"Fields
| Field | Type | Default | Description |
|---|---|---|---|
language | string | - | Build language. Picks pilum init defaults, the memory estimate for worker planning, and the toolchain for automatic cache warming. Does not generate a build command |
version | string | - | Language version. Informational; written by pilum init |
cmd | string | - | Shell command that builds the service. Empty means no host build |
env_vars | map | - | Environment variables for this service’s steps |
flags | map of lists | - | Flags appended to the end of cmd (see Build Flags) |
version_var | string | - | Homebrew only. Go variable that receives the release tag via -X |
resources.memory | int (MB) | language default | Peak build memory, used to cap parallel builds |
resources.cpu | int (cores) | 2 | Cores one build uses, used to size parallel builds |
warm | string or false | automatic | Command that fills the shared build cache once per run, or false to opt out |
warm_dir | string | project root | Directory to run warm in, relative to where you run Pilum |
output_dir | string | dist | Cloudflare Pages only. Directory to deploy, relative to the service directory |
output and platforms appear in older examples and in the JSON schema, but Pilum ignores them. The Homebrew recipe always builds darwin/linux × amd64/arm64.
How cmd Runs
cmd runs through /bin/sh -c, so pipes, && and environment variable expansion work.
| Recipe | Step that runs cmd | Working directory |
|---|---|---|
gcp-cloud-run, gcp-cloud-run-job, gcp-artifact-registry-image, gcp-mig-container, azure-container-apps | build binary | Service directory |
npm | build package | Service directory |
cloudflare-pages | build site | Project root (so pnpm --filter works) |
aws-lambda | — (runs sam build) | — |
homebrew | — (runs a fixed Go cross-compile) | — |
If cmd is empty, the build step is skipped for that service. For container recipes this means a Dockerfile-only build: the image is built entirely by docker build with your _templates/<template> Dockerfile. That’s the right choice when the Dockerfile compiles the code itself. Keep cmd when the Dockerfile copies a binary built on the host (for example from dist/).
Languages
Pilum knows 12 languages. Each has a template used by pilum init and a default memory estimate:
language | Aliases | pilum init default cmd | Default memory |
|---|---|---|---|
go | golang | go build -o ./dist (plus ldflags) | 1024 MB |
node | nodejs, javascript, js, typescript, ts | npm run build | 1536 MB |
bun | bun run build | 1536 MB | |
python | pip install -r requirements.txt | 512 MB | |
rust | cargo build --release (plus target flag) | 2048 MB | |
java | ./mvnw -B package -DskipTests | 3072 MB | |
kotlin | ./gradlew build -x test | 3072 MB | |
dotnet | csharp, c#, fsharp, f#, .net | dotnet publish -c Release -o ./dist | 2048 MB |
c | cmake -S . -B build -DCMAKE_BUILD_TYPE=Release && cmake --build build --parallel | 1024 MB | |
cpp | c++, cxx | same as c | 2048 MB |
php | composer install --no-dev --optimize-autoloader --no-interaction | 1024 MB | |
ruby | rb | bundle install | 1024 MB |
An unknown language is allowed and gets 1024 MB. The language never changes what cmd does.
Build Flags
flags is a map from flag name to a list of values. Pilum renders each entry as -name='value1 value2' and appends it to the end of cmd, sorted by flag name:
build: language: go cmd: "go build -o ./dist/app" flags: ldflags: - "-s" - "-w"runs:
go build -o ./dist/app -ldflags='-s -w'For language: rust, flags get a double dash, so target: ["x86_64-unknown-linux-musl"] becomes --target='x86_64-unknown-linux-musl'.
Flags are only appended for container recipes. The npm and Cloudflare Pages recipes run cmd as written.
Version Injection
Pilum does not substitute ${TAG} or any other placeholder in cmd or flags. A -X main.version=${TAG} flag passes the literal text (or an empty string) to the linker.
-
Homebrew: set
version_varand the recipe adds-X <version_var>=<tag>to its cross-compile:build:language: goversion_var: github.com/my-org/my-cli/cmd.version -
Other recipes: export the version in your CI environment and reference it from
cmd. Steps inherit the shell environment, and/bin/sh -cexpands it:build:language: gocmd: 'go build -ldflags "-s -w -X main.version=$VERSION" -o ./dist/app .'Terminal window VERSION=v1.2.3 pilum deploy --tag=v1.2.3
Environment Variables
build: env_vars: CGO_ENABLED: "0" GOOS: linux GOARCH: amd64- Values must be strings; quote numbers and booleans.
- The variables are added to Pilum’s own environment for every step of that service, not only the build command. A recipe step’s own variables override them.
$NAMEand${NAME}in values are expanded from Pilum’s environment, soGIT_SHA: "${GITHUB_SHA}"works. There is no escape, so a value cannot contain a literal$.- Pilum sets
GOMAXPROCSper task from its worker plan unless you set it here or in your shell. - Build variables also distinguish warm groups: services with different
GOOS/GOARCHwarm separately.
Runtime variables for the deployed service go in the top-level env_vars, not here. See Service Configuration.
Resources
Pilum runs builds in parallel and sizes the pool from the machine:
- Workers for CPU-bound steps = CPU cores ÷ the largest
resources.cpuacross services (default 2). - That number is capped so the largest build memory estimate times the worker count fits in 75% of RAM. The estimate is
resources.memory, or the language default from the table above. - Steps that call Docker are sized from the Docker daemon’s CPUs and memory (the Docker Desktop VM on macOS).
- Push, deploy and execute steps use up to 16 workers.
--max-workers Noverrides all of this.
Set resources when a build needs much more or less than its language default:
build: language: rust cmd: "cargo build --release" resources: memory: 4096 # MB cpu: 4 # coresBuild Cache Warming
Before the builds start, Pilum can fill the language’s shared cache (Go build cache, pnpm store, Cargo registry, …) once, so parallel builds don’t all download and compile the same dependencies.
build: language: node warm: pnpm install --frozen-lockfile # explicit command warm_dir: apps # optional, relative to where you run Pilum cmd: pnpm build- Unset: automatic. Pilum detects the toolchain (Go, Node, Python uv/pip, Cargo, Gradle, Maven, .NET, Composer) and warms when two or more services with a host
cmdshare it. - A string: run this command once. Services with the same
warm,warm_dirand buildenv_varsshare one run. false: never warm this service.--no-warmdisables warming for the whole run.
Warming runs only when a build step will run, and a failed warm falls back to cold builds. See Build Cache Warming for the commands Pilum generates per toolchain.
Language Examples
Go
build: language: go version: "1.23" cmd: "go build -o ./dist/app" env_vars: CGO_ENABLED: "0" GOOS: linux GOARCH: amd64 flags: ldflags: - "-s" - "-w"Node.js
build: language: node version: "20" cmd: "npm run build" env_vars: NODE_ENV: productionFor a pnpm workspace, build a package and its workspace dependencies in order with the ... suffix:
build: language: node cmd: "pnpm --filter @scope/package... build"Which install command runs before the build is set by the recipe, not by build:
- npm recipe:
npm.package_manager—npm(default),pnpm,yarnorbun. See npm. - Cloudflare Pages recipe:
cloudflare.package_manager—npm(default) orpnpm. See Cloudflare Pages.
Python
build: language: python version: "3.12" cmd: "pip install -r requirements.txt" env_vars: PYTHONDONTWRITEBYTECODE: "1" PYTHONUNBUFFERED: "1"Rust
build: language: rust version: "1.75" cmd: "cargo build --release" flags: target: - "x86_64-unknown-linux-musl" # rendered as --target='...' env_vars: CARGO_NET_GIT_FETCH_WITH_CLI: "true"Java
build: language: java version: "21" cmd: "./mvnw -B package -DskipTests"Dockerfile-only
type: gcp-cloud-runtemplate: node # _templates/node does the whole buildbuild: language: node # still used for memory planningCustom commands
build: language: go cmd: "make build"build: language: go cmd: "./scripts/build.sh"CLI Help
pilum build --help prints build examples by language.
Next Steps
- Service Configuration — Full
pilum.yamlreference - Build Cache Warming — Warm commands per toolchain
- How Recipes Work — Build steps, tags and execution modes
- npm Provider — npm/pnpm/yarn/bun publishing
- Cloudflare Pages — Static site deployment