Skip to content

Service Configuration

Each service has a pilum.yaml that says what the service is, which recipe deploys it, and how to build it. Pilum discovers these files automatically; see Discovery.

Basic Structure

name: my-service
description: My service
type: gcp-cloud-run # Selects the recipe
project: my-gcp-project
region: us-central1
registry_name: services
template: go # Dockerfile at _templates/go
build:
language: go
cmd: "go build -o ./dist/app ."
env_vars:
LOG_LEVEL: info
secrets:
DATABASE_URL: database-url:latest

Required Fields

Every service needs:

FieldDescription
nameService identifier, used in commands, image names and cloud resource names. Allowed characters: a-z, A-Z, 0-9, ., -, _
type or providerWhich recipe to use. Set type; provider is then inferred for the built-in recipes

Each recipe adds its own required fields (for example, gcp-cloud-run requires project, region, registry_name and template). pilum recipes <type> lists them, and pilum check reports any that are missing. The provider pages document each one.

type and provider

type names the recipe. The built-in values are:

typeInferred providerProvider page
gcp-cloud-rungcpGCP Cloud Run
gcp-cloud-run-jobgcpGCP Cloud Run Jobs
gcp-cloud-run-from-imagegcpCloud Run from Image
gcp-artifact-registry-imagegcpArtifact Registry Image
gcp-mig-containergcpManaged Instance Group
azure-container-appsazureAzure Container Apps
aws-lambdaawsAWS Lambda
cloudflare-pagescloudflareCloudflare Pages
npmnpmnpm
homebrewhomebrewHomebrew

Pilum looks up the recipe by type. If type is missing it falls back to provider and picks the first recipe for that provider in alphabetical order. provider also drives image naming and the --provider filter. With a custom recipe whose name isn’t in the table above, set provider explicitly.

Top-Level Fields

FieldTypeDefaultDescription
namestring-Service name (required)
descriptionstring-Human-readable description. Used as the formula desc by Homebrew
typestring-Recipe name (see above)
providerstringinferred from typegcp, aws, azure, cloudflare, homebrew, npm
projectstring-GCP project ID; Azure resource group (required by most recipes)
regionstring-Deployment region
regionslist-Deploy one instance per region (see Multi-Region)
registry_namestring-Bare registry name: Artifact Registry repository (GCP), ACR name (Azure). Not a full host or path
templatestring-Dockerfile name under _templates/ in the project root (container recipes)
imagestring-gcp-cloud-run-from-image: the full image reference to deploy. gcp-mig-container: the repository to push to, without a tag
image_namestringnameLast path segment of the image name, when it should differ from the service name
versionstring-Image tag used when --tag is not given. Without either, the tag is latest
licensestring-SPDX license identifier, written into the Homebrew formula
depends_onlist-Names of services this one depends on (see Dependencies)
env_varsmap-Runtime environment variables (see Environment Variables)
secretsmap-Runtime secret references (see Secrets)
sidecarslist-Extra containers for Cloud Run services (see Sidecars)
environmentsmap-Per-environment overrides (see Environment Overrides)
buildmap-How to build the service — see Build Configuration

Target-specific blocks are listed in Target-Specific Blocks.

Image names and tags

For recipes that build an image, Pilum composes the image reference from the service fields:

ProviderImage
gcp<region>-docker.pkg.dev/<project>/<registry_name>/<image_name>:<tag> (if registry_name is empty, the project ID is used as the repository; a registry_name containing docker.pkg.dev or gcr.io is used as-is)
azure<registry_name>.azurecr.io/<image_name>:<tag>
gcp-mig-container<image>:<tag>

<image_name> defaults to name. The tag is --tag, else version, else latest.

Value Types

String fields must be YAML strings. Pilum silently ignores a number or boolean where it expects a string, so quote values like cpu: "1", version: "1.23" and PORT: "8080". Numeric fields such as cloud_run.min_instances take plain numbers.

Environment Variables

env_vars is a map of names to string values:

env_vars:
LOG_LEVEL: info
API_TIMEOUT: "30"
FEATURE_FLAGS: "new-ui,dark-mode"

Variables are set on the deployed Cloud Run service, Cloud Run Job or MIG container. Azure Container Apps and AWS Lambda do not receive them (for Lambda, set them in your SAM template).

Target blocks can carry their own env_vars: cloud_run.env_vars, container_app.env_vars, lambda.env_vars and mig.env_vars. Pilum merges them with the top-level map, and top-level values win on conflicts:

env_vars:
LOG_LEVEL: info
cloud_run:
env_vars:
LOG_LEVEL: debug # overridden by the top-level value
GOMEMLIMIT: 450MiB # added

The JSON schema doesn’t list env_vars under cloud_run, container_app or lambda yet, so editors may flag these keys even though Pilum reads them.

Build-time variables go in build.env_vars instead; see Build Configuration.

Secrets

secrets maps an environment variable name to a secret reference:

secrets:
DATABASE_URL: database-url:latest
API_KEY: projects/my-gcp-project/secrets/api-key/versions/3

For GCP targets the value is secret-name:version or a full projects/<project>/secrets/<name>/versions/<version> path; Pilum converts the full path to name:version for gcloud --set-secrets. Secrets are applied to Cloud Run services, Cloud Run Jobs and MIG containers. Azure Container Apps and AWS Lambda ignore them.

Multi-Region Deployments

name: my-api
type: gcp-cloud-run
project: my-project
registry_name: services
template: go
regions:
- us-central1
- europe-west1
- asia-east1

Pilum expands this into three service instances named my-api (us-central1), my-api (europe-west1) and my-api (asia-east1). Each gets its own image path (the region is part of the Artifact Registry host) and its own deploy. When regions is set, region is ignored. To target one instance, quote the full name:

Terminal window
pilum deploy "my-api (europe-west1)" --tag=v1.0.0

Dependencies

depends_on orders services. Pilum groups services into waves by dependency depth: services in the same wave run in parallel, and waves run in sequence.

services/database/pilum.yaml
name: database
type: gcp-cloud-run
# ...
# services/api/pilum.yaml
name: api
type: gcp-cloud-run
depends_on:
- database

Waves apply to steps tagged build, deploy and execute; push steps run flat. If a service fails, its dependents in later waves are skipped. --no-deps disables wave ordering. --only-changed also selects the dependents of changed services. pilum check does not detect dependency cycles; a cycle fails at deploy time.

Environment Overrides

environments holds per-environment overrides that are merged onto the base config when you pass --env:

name: api
type: gcp-cloud-run
project: acme-dev
region: us-central1
registry_name: services
template: go
environments:
staging:
project: acme-staging
prod:
project: acme-prod
region: us-east1
cloud_run:
min_instances: 2
Terminal window
pilum deploy --env=prod --tag=v1.0.0

Merge rules:

  • Scalars are replaced, maps are merged key by key, and lists are replaced entirely.
  • A service without an environments block ignores --env.
  • A service that has an environments block but not the requested environment is an error, which lists the available names.
  • Without --env, the environments block is dropped and the base config is used.

check, list, status, logs, rollback and compose also accept --env. See Environments & CI.

Sidecars

gcp-cloud-run and gcp-cloud-run-from-image services can run extra containers next to the main (ingress) container:

sidecars:
- name: otel-collector
image: us-docker.pkg.dev/my-project/tools/otel-collector:0.98.0
memory: 256Mi
cpu: "0.5"
env_vars:
OTEL_LOG_LEVEL: info
secrets:
OTEL_TOKEN: otel-token:latest
args: ["--config=/etc/otel/config.yaml"]

Sidecar keys: name, image, memory, cpu, env_vars, secrets, args, command, depends_on, plus port and startup_probe, which Pilum currently parses but does not pass to gcloud. When sidecars are present, the main container’s port is always sent (default 8080). See GCP Cloud Run.

Target-Specific Blocks

Each recipe reads its own nested block. Field-by-field references are on the provider pages.

BlockUsed bySummary
cloud_rungcp-cloud-run, gcp-cloud-run-from-imageScaling, memory/CPU, concurrency, timeout, port, HTTP/2, Cloud SQL, public access (allow_unauthenticated), ingress, VPC connector, service account, no_traffic, revision tag, env_vars — GCP Cloud Run
jobgcp-cloud-run-jobTask timeout, retries, parallelism, task count, CPU/memory, service account, Cloud SQL, execute_on_deploy — Cloud Run Jobs
miggcp-mig-containerInstance group name and zone/region, template base name, machine type, network, rollout surge/unavailable, wait timeout, env_vars — Managed Instance Group
container_appazure-container-appsEnvironment, replicas, CPU/memory, ingress port and visibility — Azure Container Apps
lambdaaws-lambdaOnly env_vars is read; memory, timeout and runtime belong in your SAM template — AWS Lambda
cloudflarecloudflare-pagesAccount ID, token env var, production branch, package manager — Cloudflare Pages
npmnpmScope, registry, access, token env var, package manager — npm
homebrewhomebrewProject URL, tap URL, token env var — Homebrew

runtime.service is also accepted; pilum list prints it, but nothing else uses it.

Discovery

  • Pilum searches for files named exactly pilum.yaml in the directory you run it from and up to four levels below it.
  • pilum list and pilum compose search from the project root instead: the nearest directory, at or above the current one, that contains package.json, cdk.json, tsconfig.json, .gitignore, go.mod or Cargo.toml. The deployment lock and history (.pilum/) also live in that root. Run Pilum from the project root and the two agree.
  • Paths matched by .gitignore and .pilumignore are skipped. See Ignore Files.
  • A pilum.yaml without name aborts discovery.
  • The service’s directory (relative to where you run Pilum) is its path: service_dir steps run there, and it’s passed to docker build as SERVICE_NAME.
  • Project-wide settings (webhooks, compose dependencies) live in .pilum.yml, not in pilum.yaml. See Project Configuration.

Complete Example

name: payment-api
description: Payment processing API
type: gcp-cloud-run
project: acme-payments
region: us-central1
registry_name: services
template: go
build:
language: go
version: "1.23"
# Package path last, so no build.flags (they would be appended after it)
cmd: "go build -ldflags='-s -w' -o ./dist/payment-api ./cmd/api"
env_vars:
CGO_ENABLED: "0"
GOOS: linux
GOARCH: amd64
env_vars:
LOG_LEVEL: info
secrets:
STRIPE_KEY: stripe-key:latest
DATABASE_URL: payment-db-url:latest
cloud_run:
min_instances: 1
max_instances: 50
memory: 1Gi
cpu: "2"
concurrency: 100
timeout_seconds: 30
service_account: [email protected]
depends_on:
- payment-migrations
environments:
staging:
project: acme-payments-staging
cloud_run:
min_instances: 0

Next Steps