Skip to content

CLI Commands

This page is the reference for the pilum CLI. Every flag and default below comes from the command definitions in cmd/. Run pilum <command> --help to see the same information in your terminal.

Command Overview

CommandAliasesWhat it does
deployupRun every recipe step: build, push, deploy
buildb, makeRun steps tagged build
publishpRun every step not tagged deploy
pushpsRun steps tagged push
dry-rundrPrint the commands a pipeline would run
rollback—Roll services back to their previous version (GCP)
statusstShow the state of deployed services
logslogShow or stream logs for one deployed service
historyhistShow recorded pipeline runs and rollbacks
listlsList discovered services
checkvalidateValidate pilum.yaml files against their recipes
recipesprovidersList recipes or describe one
init—Create a pilum.yaml
composedocker-compose, dcGenerate a docker-compose.yml for local development
delete-buildscleanDelete each service’s dist/ directory
completion—Generate a shell completion script
telemetry—Show or change the telemetry setting

pilum --version prints pilum <version>, for example pilum v0.9.0.

Global Flags

These flags work on every command.

FlagShortDefaultDescription
--verbose-vfalseStream command output in real time
--quiet-qfalseMinimal output (CI-friendly)
--jsonfalseOutput as JSON for scripting
--no-gitignorefalseDon’t read .gitignore files (the root one or nested ones) for ignore patterns during service discovery

--verbose, --quiet and --json are mutually exclusive.

Pipeline Flags

deploy, build, publish, push and dry-run share these flags. dry-run has every flag except --dry-run.

FlagShortDefaultDescription
--tag-t""Image or release tag. See Tag resolution
--dry-run-DfalsePrint commands without running them (not on dry-run, which always does this)
--debug-dfalsePrint each command, working directory, timeout and environment (secret-looking values redacted)
--timeout-T60Step timeout in seconds. A recipe step’s timeout replaces the default; passing -T replaces both. See Timeouts and retries
--retries-r3Extra attempts after a failed command. See Timeouts and retries
--max-workers0Parallel workers per step. 0 sizes the pool automatically
--only-tags""Run only steps that have at least one of these tags (comma-separated)
--exclude-tags""Skip steps that have any of these tags (comma-separated)
--only-changedfalseRun only services affected by changes since --since, plus services that depend on them. See Only changed services
--since""Git ref for --only-changed. Defaults to the default branch, preferring origin/main (or origin/master) over a local branch
--no-depsfalseIgnore depends_on and run every step flat-parallel
--no-warmfalseSkip the build-cache warming phase that runs before builds
--env-e""Merge the named block from each service’s environments: onto its config
--provider""Only run services whose provider matches (case-insensitive), e.g. gcp
--force-ffalseTake the deployment lock even if another run holds it
--github-statusfalsePost a commit status to GitHub. See GitHub Commit Status
--recipe-path""Load recipes from this directory instead of the built-in recipes

Timeouts and retries

Timeout. Each step’s timeout is, in order:

  1. --timeout (-T), when you pass it. It also counts when it comes from PILUM_TIMEOUT or a timeout: key in .pilum.yml.
  2. The recipe step’s timeout.
  3. The default, 60 seconds.

Every built-in recipe step sets a timeout (300s for builds, 120–180s for pushes and deploys, 600s for execute job, 900s for the MIG wait for stable), so without -T those apply. pilum build -T 1800 gives every step, and the warm phase, 1800 seconds.

-T replaces the recipe’s value in both directions: -T 30 makes a 300-second build step time out after 30 seconds. Before 0.10 a recipe’s timeout always won and -T only reached steps without one. A step that runs out of time fails with command timed out after 300s.

Retries. A step’s own retries always wins. Otherwise --retries, when you pass it, beats the step’s default_retries; without it, default_retries applies, then the --retries default of 3. Most built-in deploy steps set default_retries: 2. Before 0.9 default_retries was ignored.

Tag resolution

--tag defaults to an empty string, not latest. What happens with no tag depends on the recipe:

  • Every recipe gets the same resolved tag: --tag, then the service’s version: from pilum.yaml, then latest. This covers image names, every step handler, and ${tag} / ${build.version} in custom steps.
  • Cloudflare Pages deploys latest (or a tag equal to production_branch) to the production branch; any other tag becomes a preview branch.
  • npm skips set version for latest, so the version in package.json is published.
  • Homebrew needs a real version: with neither --tag nor version:, the run fails before anything starts with a homebrew release needs a version; pass --tag or set version: in pilum.yaml.
  • gcp-mig-container rejects latest before anything runs: every release needs a unique tag.

Pass --tag explicitly for any deploy you care about.

Worker sizing

With --max-workers 0, Pilum sizes each step separately:

  • Steps tagged build, and untagged steps, are CPU-bound. Workers = CPU cores ÷ cores per build (build.resources.cpu, default 2), further capped by memory: 75% of RAM ÷ the largest per-language build memory among the services. Steps that call docker are sized against the Docker daemon’s CPUs and memory (on Docker Desktop, the VM), not the host. Each build also gets a GOMAXPROCS share of the cores.
  • Steps tagged push, deploy or execute wait on the network and get min(tasks, 16) workers.

--max-workers N sets the worker count for every step. Pilum prints the plan it chose at the start of each step.

Pipeline Commands

pilum deploy

Terminal window
pilum deploy [services...] [flags]

Runs every recipe step for each selected service, in recipe order: typically build, push, deploy. With no service names, it runs every discovered service. Takes the deployment lock and records the run in history.

Flags: pipeline flags.

Terminal window
pilum deploy --tag v1.2.0
pilum deploy api worker --tag v1.2.0
pilum up api --tag v1.2.0 --env prod

pilum build

Terminal window
pilum build [services...] [flags]

Runs steps tagged build. If you pass --only-tags, it replaces the build default. pilum build --help also prints a build reference by language.

Flags: pipeline flags.

Terminal window
pilum build --tag v1.2.0
pilum b api

pilum publish

Terminal window
pilum publish [services...] [flags]

Runs every step not tagged deploy. This is not exactly build then push: steps tagged execute, and untagged steps, also run. The default applies only when you pass neither --only-tags nor --exclude-tags; either flag replaces it.

Flags: pipeline flags.

Terminal window
pilum publish --tag v1.2.0

pilum push

Terminal window
pilum push [services...] [flags]

Runs steps tagged push, normally docker push of an image built earlier. If you pass --only-tags, it replaces the push default.

Flags: pipeline flags.

Terminal window
pilum push api --tag v1.2.0

pilum dry-run

Terminal window
pilum dry-run [services...] [flags]

Prints the command each step would run, wave by wave, without executing anything. It doesn’t take the deployment lock, skips the Docker preflight check and isn’t recorded in history. It accepts every pipeline flag except --dry-run, so you can preview a filtered run exactly. pilum deploy --dry-run does the same thing.

Terminal window
pilum dry-run --tag v1.2.0
pilum dr api --only-tags deploy --env prod

Example output for a Cloud Run service api and a Cloud Run job worker that depends on it (trimmed):

Deploying 2 service(s)
━━━ Warming build caches ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
◌ go (2 services, auto) warm build cache
→ go build ./services/api ./services/worker
━━━ Step 1/5: build binary ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
Wave 1/2:
◌ api build binary
→ /bin/sh -c go build -o ./dist/app .
Wave 2/2:
◌ worker build binary
→ /bin/sh -c go build -o ./dist/app .
━━━ Step 2/5: build docker image ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
Wave 1/2:
◌ api build docker image
→ docker build --platform linux/amd64 -t us-central1-docker.pkg.dev/my-project/services/api:v1.2.0 -t us-central1-docker.pkg.dev/my-project/services/api:latest --build-arg SERVICE_NAME=services/api -f ./_templates/go .
...
━━━ Step 3/5: publish to registry ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
◌ api publish to registry
→ docker push us-central1-docker.pkg.dev/my-project/services/api:v1.2.0
...
━━━ Step 4/5: deploy to cloud run / deploy job ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
Wave 1/2:
◌ api deploy to cloud run
→ gcloud run deploy api --region us-central1 --platform managed --no-allow-unauthenticated --project my-project --image us-central1-docker.pkg.dev/my-project/services/api:v1.2.0 --clear-env-vars --clear-secrets
...
━━━ Step 5/5: route traffic to latest / execute job ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
Wave 1/2:
◌ api route traffic to latest
→ gcloud run services update-traffic api --to-latest --region us-central1 --project my-project
Wave 2/2:
◌ worker execute job

A step that generates no command (here execute job, because job.execute_on_deploy is not true) is listed without an arrow line and does nothing.

With --json, the output is a single object:

{
"dry_run": true,
"steps": [
{ "service": "api", "step": "publish to registry", "command": "docker push us-central1-docker.pkg.dev/my-project/services/api:v1.2.0" }
]
}

Rollback

pilum rollback

Terminal window
pilum rollback <services...> [flags]

Rolls one or more deployed services back to the version before the one currently running. Run it again to step back further. At least one service name is required.

RecipeWhat rollback does
gcp-cloud-run, gcp-cloud-run-from-imageMoves 100% of traffic to the previous healthy revision. Nothing is rebuilt; that revision’s full config (env, CPU, secrets) comes back with it.
gcp-cloud-run-jobPoints the job at the image of its previous execution, keeping the job’s current config.
gcp-mig-containerRolls the group onto the previous <template_base> instance template, using the same surge settings as a deploy, then waits until the group is stable.

Any other recipe is rejected with exit code 6: rollback is not supported for <service> (recipe <key>).

FlagShortDefaultDescription
--to""Target revision (Cloud Run service), image tag or reference (job), or template name or tag (MIG). Default: the previous version. Works with exactly one service instance
--yes-yfalseSkip the confirmation prompt. Required when not interactive
--dry-run-DfalseShow the plan and commands without changing anything
--force-ffalseTake the deployment lock even if another run holds it
--timeout-T60Timeout per command in seconds
--debug-dfalseEnable debug output
--env-e""Apply an environments: override block

Behavior:

  • The previous version is read from the cloud provider, not from local history, so rollback works from any machine, including CI.
  • All services are planned in parallel first. If planning fails for any service (for example, it has no earlier revision), nothing changes.
  • Rollback asks Roll back N service(s)? (y/N). Without --yes it refuses to run when stdin is not a terminal or is /dev/null, when any of CI, GITHUB_ACTIONS, GITLAB_CI, CIRCLECI, JENKINS_URL or BUILDKITE is set, or in --json mode.
  • It takes the deployment lock and is recorded in history as a rollback entry.
  • A multi-region service rolls back every region. --to needs a single instance, so name it as "api (us-east1)".
  • The next pilum deploy of a Cloud Run service sends traffic to the new revision again, because the recipe ends with route traffic to latest. That step also resets any traffic split made by hand.
Terminal window
pilum rollback api --dry-run # show the plan only
pilum rollback api # previous revision, asks first
pilum rollback api --to api-00041-xyz # a specific revision
pilum rollback nightly-job --to v1.4.2 # job: image tag
pilum rollback api worker --yes # no prompt (required in CI)

See Rollback, Status & History for a walkthrough.

Observability Commands

pilum status

Terminal window
pilum status [services...] [flags]

Queries each service’s provider in parallel and prints its state, region, image (or MIG template and stable instance count) and URL. Services with provider npm or homebrew are skipped. Supported: Cloud Run services and jobs, MIGs, Azure Container Apps, AWS Lambda and Cloudflare Pages.

Ready, Running, Succeeded and Active show as healthy. Error, NotReady and Failed show as failures, and Unknown as a warning.

FlagShortDefaultDescription
--timeout-T30Query timeout in seconds
--debug-dfalseEnable debug output
--env-e""Apply an environments: override block
--provider""Only query services with this provider
Terminal window
pilum status
pilum st api --env prod
pilum status --json

pilum logs

Terminal window
pilum logs <service> [flags]

Prints recent logs for exactly one service, or streams them with --follow. If the name matches a multi-region service, the first region is used; name a specific instance as "api (us-east1)".

Supported: Cloud Run services and jobs, Azure Container Apps, AWS Lambda and Cloudflare Pages. gcp-mig-container is not supported; use Cloud Logging for the group’s instances. npm and Homebrew services return an error.

FlagShortDefaultDescription
--lines-n50Number of lines to show (GCP only)
--follow-ffalseStream logs in real time. On GCP this runs gcloud beta run services logs tail (or jobs logs tail), which needs gcloud’s log-streaming component
--timeout-T30Query timeout in seconds (not applied with --follow)
--debug-dfalsePrint the generated logs command
--env-e""Apply an environments: override block
Terminal window
pilum logs api
pilum logs api -n 200
pilum log api --follow

pilum history

Terminal window
pilum history [flags]

Shows recorded runs, newest first. Every non-dry-run deploy, build, publish and push, and every rollback, appends one line to .pilum/history.jsonl in the project root. Nothing is recorded if Pilum can’t find a project root (see Deployment Lock).

FlagShortDefaultDescription
--limit-n10Number of entries to show (0 shows all)
--service-s""Only entries that include this service. Exact match on the display name, so a multi-region instance is "api (us-east1)"
Deployment History
abc12def 2026-10-07 14:32 deploy v1.2.3 ✓ 10/10 (4m12s)
98fe76ba 2026-10-07 12:10 rollback ✓ 1/1 (38s)

The count is successful step results out of the total, one per service per step. The file is append-only JSONL, so appends from different machines merge cleanly if you commit it.

Terminal window
pilum history
pilum hist -n 20 -s api
pilum history --json

Configuration Commands

pilum list

Terminal window
pilum list [flags]

Lists every service discovered under the project root, with provider, project, region and runtime service.

FlagShortDefaultDescription
--env-e""Apply an environments: override block
--provider""Only list services with this provider
Found 2 services:
• api
Provider: gcp
Project: my-project
Region: us-central1
Service:
• worker
Provider: gcp
Project: my-project
Region: us-central1
Service:

Service shows runtime.service from pilum.yaml, which is usually empty. --json returns name, provider, project, region, type and path for each service.

Terminal window
pilum ls --provider gcp
pilum list --json

pilum check

Terminal window
pilum check [services...] [flags]

Validates each service: name and provider must be set (provider is inferred from a built-in type), and every required field of the matching recipe must be present unless the recipe gives it a default. It stops at the first invalid service with exit code 2. With --json, it reports every service.

FlagShortDefaultDescription
--env-e""Apply an environments: override block before validating
● Checking configuration of the services
Checking service api (recipe: gcp-cloud-run)
✓ api: valid
Checking service worker (recipe: gcp-cloud-run-job)
✓ worker: valid
✓ All services are valid

A failure looks like this:

✗ error checking service api: recipe 'gcp-cloud-run' requires field 'registry_name': Artifact Registry repository name (bare name, e.g. "my-repo" — not a full host/path)

check finds recipes the same way the pipeline does, so a service that passes check is one a deploy can run. It fails when no recipe matches:

✗ error checking service api: no recipe named 'gcp-cloudrun' - did you mean 'gcp-cloud-run'?
✗ error checking service api: provider 'gcp' has several recipes; set type: to one of gcp-artifact-registry-image, gcp-cloud-run, gcp-cloud-run-from-image, gcp-cloud-run-job, gcp-mig-container

It also reports env_vars and secrets entries it can’t read (a nested map, or a list entry without name).

check always validates against the built-in recipes; it has no --recipe-path.


pilum recipes

Terminal window
pilum recipes [name]

Without an argument, lists the built-in recipes. With a name, shows its provider and service, required fields, optional fields with defaults, and steps with their tags and timeouts. It always reads the built-in recipes.

gcp-cloud-run
Deploy to Google Cloud Run
Provider: gcp / cloud-run
Required Fields
project GCP project ID (default: my-gcp-project)
region GCP region to deploy to (default: us-central1)
name Service name (used for Cloud Run service and container naming)
registry_name Artifact Registry repository name (bare name, e.g. "my-repo" — not a full host/path)
template Dockerfile template name (path relative to _templates directory)
...
Steps
1. build binary [build] timeout: 300s
2. build docker image [build] timeout: 300s
3. publish to registry [push] timeout: 120s
4. deploy to cloud run [deploy] timeout: 180s
5. route traffic to latest [deploy] timeout: 120s
Terminal window
pilum recipes
pilum recipes gcp-mig-container
pilum providers --json

See How Recipes Work for all ten built-in recipes.


pilum init

Terminal window
pilum init [flags]

Creates pilum.yaml in the current directory. Interactive by default: it asks for provider, service type, name, required and optional fields, and build language. For container recipes it also writes a starter Dockerfile at _templates/<language> (unless one exists) and sets template to match. Starter Dockerfiles exist for bun, dotnet, go, java, node, python and rust; for other languages, init asks you to create _templates/<name> yourself.

It is non-interactive when --provider, --name and --language are all set. Providers with service types (gcp, aws, azure, cloudflare) then also need --service.

FlagShortDefaultDescription
--provider-p""gcp, aws, azure, cloudflare, homebrew or npm
--service-s""Service type, e.g. cloud-run, cloud-run-job, mig-container, lambda, container-apps, pages
--name-n""Service name
--language-l""bun, c, cpp, dotnet, go, java, kotlin, node, php, python, ruby or rust
--force-ffalseOverwrite an existing pilum.yaml without asking
Terminal window
pilum init
pilum init --provider gcp --service cloud-run --name my-api --language go
pilum init -p cloudflare -s pages -n my-site -l node --force

The generated file sets type: <provider>-<service>. It offers only service types that have a recipe: for gcp, cloud-run, cloud-run-job, cloud-run-from-image, artifact-registry-image and mig-container. String fields are written quoted, so cpu: "1" stays a string.


pilum compose

Terminal window
pilum compose [services...] [flags]

Writes a docker-compose.yml that runs the selected services locally, plus infrastructure dependencies (Postgres, MySQL, MariaDB, Redis, Mongo or a custom image) declared under compose.dependencies in .pilum.yml. You must pass --all or at least one service name.

FlagShortDefaultDescription
--allfalseInclude every discovered service
--registry-R""Registry prefix for images (overrides pilum.yaml)
--tag-tlatestImage tag
--output-odocker-compose.ymlOutput file path
--env-e""Apply an environments: override block
--provider""Only include services with this provider
--no-depsfalseLeave out infrastructure dependencies
--env-file""Also write a .env template with service URLs, e.g. apps/web/.env.example
Terminal window
pilum compose --all
pilum compose api worker -o compose.dev.yml
pilum dc --all --no-deps --registry ghcr.io/myorg

See Local Development and Project Config for the dependency format.

Utility Commands

pilum delete-builds

Terminal window
pilum delete-builds [services...]

Deletes the dist/ directory inside each selected service’s directory. No flags beyond the global ones.

Terminal window
pilum clean
pilum delete-builds api

pilum completion

Terminal window
pilum completion bash|zsh|fish|powershell

Prints a completion script for the given shell.

Terminal window
# Bash
source <(pilum completion bash)
# Zsh
pilum completion zsh > "${fpath[1]}/_pilum"
# Fish
pilum completion fish | source
# PowerShell
pilum completion powershell | Out-String | Invoke-Expression

pilum telemetry

Terminal window
pilum telemetry status | enable | disable | last

Shows or changes whether Pilum shares anonymous build telemetry. On your machine it is off unless you turn it on; the GitHub Action turns it on by default. status says which setting decided (DO_NOT_TRACK, PILUM_TELEMETRY, the saved setting or the default), disable also deletes the install ID, and last prints the exact payload last sent. See Telemetry for what is and isn’t sent.

Service Selection

Commands that take [services...] select services by name. With no names, they act on every service discovered from the current directory down (list and compose search from the project root).

FormExampleMatches
Namepilum deploy apiapi, in every region if it’s multi-region
Several namespilum deploy api workerBoth services
Comma listpilum deploy api,workerBoth services (pipeline commands and rollback)
Globpilum deploy 'api-*'Every service whose name matches; ? and [...] also work
One regionpilum deploy "api (us-east1)"Only the us-east1 instance of a multi-region service

Quote globs and region names so the shell passes them through unchanged. An unknown name prints a warning with a suggestion. If none of the names you gave match anything, the command fails with exit code 3.

Filters combine with names:

Terminal window
pilum deploy --provider gcp --tag v1.2.0
pilum deploy --only-changed --tag v1.2.0
pilum deploy --only-changed --since v1.1.0 --tag v1.2.0

--only-changed compares against --since (default: the default branch, preferring origin/main) and also counts uncommitted and untracked files. A service is affected by a change in its directory, in a Go package it imports, in its workspace’s lockfile or manifest, in its Dockerfile template, or in a path under watch:. Every service that depends on an affected one through depends_on, directly or transitively, is included too. When git can’t compare (shallow clone, unknown ref) it warns and selects everything; outside a git repository it does the same. When nothing is affected, it prints No services changed; nothing to deploy and exits 0, even if you named services (since 0.10). See Only changed services.

Step Tags and Filtering

Every built-in recipe step carries one or more tags:

TagMeaningOrderingWorkers
buildCompiles code or builds an imageDependency wavesCPU and memory budget
pushUploads an artifact to a registryFlat parallelUp to 16
deployChanges what runs in the cloudDependency wavesUp to 16
executeRuns a jobDependency wavesUp to 16

--only-tags keeps steps with at least one listed tag; --exclude-tags drops steps with any listed tag. Matching is case-insensitive. A step with no tags is dropped by any --only-tags filter.

Terminal window
# Build and push, skip deploy steps
pilum deploy --exclude-tags deploy --tag v1.2.0
# Only deploy steps; the images must already exist
pilum deploy --only-tags deploy --tag v1.2.0
# Several tags
pilum deploy --only-tags build,push --tag v1.2.0

Waves. For steps tagged build, deploy or execute, services run in waves ordered by depends_on: a service waits until everything it depends on has finished that step. If a service fails, its dependents in later waves are skipped. --no-deps turns waves off. check does not detect dependency cycles; a run with a cycle fails at the first wave-ordered step with circular dependency detected involving: <service>.

Failures. Once any step fails, Pilum lets the other services finish that step, then skips every later step except those marked always_run (such as npm’s restore package manifest). The exit code reflects the original failure.

Warming. Before the first build step, Pilum fills shared build caches once (for example, a single go build over all Go services’ packages). It runs only when a build step will run, and is skipped with --no-warm, or per service with build.warm: false. A failed warm prints Warming failed; continuing with cold builds and the builds still run. See Build Cache Warming.

Docker preflight. If any step that will run calls docker, Pilum checks the daemon first (10-second limit) and fails before building anything if it isn’t running. When a service sets build.docker_cache, it also checks that the active buildx builder can export a registry cache, and if not, turns the cache off for the run with a warning. Dry runs skip both checks.

JSON Output

--json prints machine-readable output instead of text. It is supported by list, check, recipes, status, logs, history, compose, init, rollback, delete-builds and dry-run. When one of these commands other than dry-run fails, it prints {"success": false, "error": "..."} and exits with the matching exit code.

Terminal window
pilum list --json
pilum recipes gcp-cloud-run --json
pilum status --json
pilum dry-run --json --tag v1.2.0

Deployment Lock

deploy, build, publish, push and rollback take a lock file at .pilum/deploy.lock in the project root, so two runs can’t change the same project at once. A second run fails with exit code 7:

deployment locked by PID 12345 on my-host (started 2026-10-07T10:00:00Z, command: deploy, services: [api worker]). Use --force to override
  • The lock is cleared automatically when the holding process is dead or the lock is more than 1 hour old.
  • It is released on normal exit and on Ctrl+C (SIGINT) or SIGTERM.
  • --force removes an existing lock and takes it.
  • Dry runs don’t take the lock.

The project root is the nearest directory, from the current one upward, that contains package.json, cdk.json, tsconfig.json, .gitignore, go.mod or Cargo.toml. Without one, no lock is taken and no history is recorded.

GitHub Commit Status

Terminal window
pilum deploy --tag "$GITHUB_SHA" --github-status

With --github-status, Pilum posts pending to the commit before the run, then success or failure, using the context pilum/<command> (for example pilum/deploy). It is opt-in: the flag is required, although its help text says “auto-detected”. It also needs all of these environment variables, or it silently posts nothing:

  • GITHUB_ACTIONS=true
  • GITHUB_TOKEN, with permission to write commit statuses
  • GITHUB_SHA, the commit to update
  • GITHUB_REPOSITORY, as owner/repo

GitHub Actions sets all of them except GITHUB_TOKEN, which you pass in:

- name: Deploy
run: pilum deploy --tag ${{ github.sha }} --github-status
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}

Webhooks for start, success and failure are configured in .pilum.yml; see Project Config and Environments & CI.

Environment Overrides

Terminal window
pilum deploy --env prod --tag v1.2.0

--env prod deep-merges environments.prod onto each service’s base config: scalars replace, maps merge, lists replace. Services without an environments: block ignore --env. A service that has the block but not the named environment is an error that lists the available ones. See Service Configuration.

Environment Variables and .pilum.yml

Pilum reads .pilum.yml from the current directory and looks up environment variables named PILUM_ plus the flag name, upper-cased, with hyphens as underscores. Each acts as that flag’s default for deploy, build, publish, push, dry-run, rollback, status and logs:

VariableActs as
PILUM_TAG--tag
PILUM_ENV--env
PILUM_PROVIDER--provider
PILUM_TIMEOUT, PILUM_RETRIES--timeout, --retries
PILUM_DEBUG--debug
PILUM_DRY_RUN--dry-run
PILUM_FORCE--force (overrides the deployment lock)
PILUM_SINCE, PILUM_ONLY_CHANGED--since, --only-changed
PILUM_ONLY_TAGS, PILUM_EXCLUDE_TAGS, PILUM_MAX_WORKERS--only-tags, --exclude-tags, --max-workers
PILUM_YES, PILUM_TOrollback --yes, rollback --to

PILUM_TELEMETRY=1 or 0 turns telemetry on or off for that shell, and DO_NOT_TRACK=1 always turns it off.

A flag on the command line always wins, then the variable, then a top-level key with the flag’s name in .pilum.yml, then the built-in default.

Unprefixed names (TAG, ENV, DEBUG, TIMEOUT) are not read. Before 0.9 they were, so a CI job’s own TAG or ENV silently changed what Pilum deployed. If you relied on that, rename the variable or pass the flag.

Exit Codes

CodeMeaning
0Success
1General error
2Configuration error (bad YAML, missing fields, recipe load failure)
3No services found (discovery errors, named services not found)
4Deployment error (command failure, timeout, step failure)
5I/O error
6Invalid arguments (unknown provider, recipe or language; rollback refused)
7Lock conflict (another run holds the deployment lock)

Running deploy with no service names when nothing is discovered prints a warning and exits 0. So does --only-changed when no service is affected, with or without service names.

Next Steps