Introduction
Pilum is a cloud-agnostic build and deployment CLI. Define your service once in a pilum.yaml, then build, publish and deploy it with one command.
pilum deploy --tag=v1.0.0Pilum runs the build → push → deploy pipeline. Your infrastructure-as-code (Terraform, Pulumi) still owns the resources themselves: projects, registries, networks, IAM. Pilum ships code onto them.
Why Pilum?
| Challenge | Pilum Solution |
|---|---|
| Provider lock-in | Move a service between targets by changing its type and target-specific block |
| Slow sequential deploys | Parallel execution with auto-sized worker pools |
| Complex CI/CD scripts | Declarative YAML recipes; CI calls one command |
| Per-provider validation code | Recipe-driven validation — no Go code per provider |
The Cooking Metaphor
Pilum uses a cooking metaphor:
- Recipes (
recipes/) — Deployment workflows: required fields plus ordered steps. The built-in recipes are embedded in the binary. - Ingredients (
ingredients/) — Provider-specific command generators (gcloud, docker, az, sam, wrangler, …) - Services — Your applications, each discovered via a
pilum.yamlfile
Deployment Targets
Each service picks a recipe with its type field. Pilum ships 10 built-in recipes:
type | What it does |
|---|---|
gcp-cloud-run | Build, push to Artifact Registry, deploy a Cloud Run service — docs |
gcp-cloud-run-job | Build, push, deploy (and optionally execute) a Cloud Run Job — docs |
gcp-cloud-run-from-image | Deploy a Cloud Run service from a pre-built image (no build or push) — docs |
gcp-artifact-registry-image | Build and push an image to Artifact Registry, no deploy — docs |
gcp-mig-container | Build, push and roll a new container image onto an existing Compute Engine managed instance group — docs |
azure-container-apps | Build, push to ACR, deploy to Azure Container Apps — docs |
aws-lambda | sam build and sam deploy — docs |
cloudflare-pages | Install, build and deploy a static site with wrangler — docs |
npm | Version, build and publish an npm package — docs |
homebrew | Cross-compile a Go CLI and update a Homebrew tap formula — docs |
Run pilum recipes to list them, and pilum recipes <name> to see a recipe’s fields and steps.
Features
- Recipe-driven deployments and validation — Each recipe declares its required fields and steps;
pilum checkvalidates every service against its recipe - Parallel execution — Services run concurrently within each step; worker counts are sized from CPU cores, memory and the Docker daemon’s resources
- Dependency waves —
depends_onorders build, deploy and execute steps; a failed service skips its dependents - Build cache warming — Fills the shared Go, Node, Python, Rust, JVM, .NET or PHP dependency cache once before the parallel builds (details)
- Docker layer cache — Opt-in registry layer cache (
build.docker_cache) so fresh CI runners reuse unchanged image layers (details) - Rollback —
pilum rollbackreturns Cloud Run services, Cloud Run Jobs and managed instance groups to their previous version (details) - Status, logs and history —
pilum status,pilum logsandpilum historyshow what is deployed and what ran - Environments — Per-environment overrides (staging, prod) via an
environmentsblock and--env(details) - Git-aware filtering —
--only-changeddeploys only services affected by changes since a base ref (their own files, Go packages they import, lockfiles, Dockerfile templates), plus their dependents (details) - Step filtering — Run only build steps, only deploy steps, or any tag combination
- Deployment locks — Prevent concurrent deploys from the same checkout, with stale-lock cleanup
- CI integration — GitHub commit statuses (
--github-status) and HTTP webhooks on start, success and failure (webhooks) - Local development —
pilum composegenerates adocker-compose.ymlfor your services and their databases (details) - Dry-run mode — Print every command without running anything
- JSON output —
--jsonfor scripting and AI agents - Editor support — JSON Schema for autocompletion and validation of
pilum.yaml
How It Works
- Discovery — Pilum finds
pilum.yamlfiles up to four directories below the current directory, skipping paths matched by.gitignorefiles and.pilumignore(ignore files) - Environment merge — With
--env, each service’senvironments.<env>block is merged onto its base config - Matching — Each service is matched to a recipe by its
typefield (for exampletype: gcp-cloud-run). Withouttype, Pilum uses theprovider’s recipe if it has only one; GCP has several, so GCP services needtype - Locking — A deployment lock (
.pilum/deploy.lock) prevents concurrent runs - Warming — When build steps will run, shared build caches are filled once
- Orchestration — Steps run in recipe order. Within a step, services run in parallel; when services declare
depends_on, build, deploy and execute steps run in dependency waves - Recording — Results are appended to
.pilum/history.jsonl
A three-service dry run where api and worker depend on database looks like this (commands trimmed):
━━━ Warming build caches ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ ◌ go (3 services, auto) warm build cache → go build ./services/database ./services/api ./services/worker
━━━ Step 1/5: build binary ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ Wave 1/2: ◌ database build binary Wave 2/2: ◌ api build binary ◌ worker build binary
━━━ Step 2/5: build docker image ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ ...
━━━ Step 3/5: publish to registry ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ ◌ database publish to registry ◌ api publish to registry ◌ worker publish to registry
━━━ Step 4/5: deploy to cloud run ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ Wave 1/2: ◌ database deploy to cloud run Wave 2/2: ◌ api deploy to cloud run ◌ worker deploy to cloud run
━━━ Step 5/5: route traffic to latest ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ ...Waves apply to steps tagged build, deploy or execute; push steps always run flat in parallel. Services without dependencies share a single wave. When a service fails, its dependents in later waves are skipped, and once any step fails, the remaining steps are skipped (except steps marked always_run). Use --no-deps to disable wave ordering.
Next Steps
- Install Pilum — Get Pilum on your machine
- Quick Start — Deploy your first service
- When to Use Pilum — Where Pilum fits, and where it doesn’t