Skip to content

Troubleshooting

Start with pilum dry-run for the service. It prints every command Pilum would run, which answers most “why did it do that” questions. Add --debug to a real run to see each command’s working directory, timeout and environment.

Service Discovery

”No services found to deploy”

$ pilum deploy
⚠ No services found to deploy

Causes:

  1. No pilum.yaml files below the current directory. Pipeline commands search from where you run them, down to 4 directory levels.
  2. Files named pilum.yml or service.yaml. The file must be named pilum.yaml.
  3. The directory is excluded by .gitignore or .pilumignore.
  4. --only-changed found no changed services, or --provider filtered everything out.

Fix:

Terminal window
find . -maxdepth 5 -name pilum.yaml
pilum list
pilum list --no-gitignore # if a .gitignore pattern hides the service

With no service names, an empty result is a warning and exits 0. If you named services and none matched, the command fails with exit code 3 and suggests close names. See Ignore Files.


”has no matching recipe”

Error: service 'web' (recipe key 'fly-app') has no matching recipe

Cause: the service’s type doesn’t match a recipe key, and no recipe exists for its provider either. This usually means a custom type without the matching --recipe-path.

Fix: use one of the built-in types (pilum recipes), or pass the directory with your recipe:

Terminal window
pilum deploy web --recipe-path ./recipes --tag v1.2.0

--recipe-path replaces the built-in recipes, so that directory must also contain any built-in recipes your other services use. See How Recipes Work.


Service only builds and pushes, never deploys

━━━ Step 1/3: build binary ━━━
━━━ Step 2/3: build docker image ━━━
━━━ Step 3/3: publish to registry ━━━

Cause: the service has provider: gcp but no type, or a misspelled one. Pilum then falls back to the first GCP recipe alphabetically, gcp-artifact-registry-image, which builds and pushes an image and has no deploy step. pilum check only warns about it:

Checking service api (recipe: gcp)
⚠ No recipe found for 'gcp' - did you mean 'npm'?
✓ All services are valid

Fix: set type explicitly:

name: api
type: gcp-cloud-run
project: my-project
region: us-central1
registry_name: services
template: go

Validation Errors

”recipe requires field ‘X’”

✗ 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)

Fix: add the field to pilum.yaml. pilum recipes <type> lists every required field. For template, the error explains that it names a Dockerfile in your project at _templates/<name>; pilum init can scaffold one.


”missing required field: provider”

✗ error checking service api: missing required field: provider

Cause: neither provider nor a recognized built-in type is set. Pilum infers provider only from built-in types, so a misspelled type (such as gcp-cloudrun) or a custom type also produces this.

Fix: correct the type, or set provider explicitly.


Nested field errors

Error: recipe 'homebrew' requires field 'homebrew.tap_url'

Fix: dotted field names are nested keys:

name: my-tool
type: homebrew
description: My CLI tool
license: Apache-2.0
homebrew:
project_url: https://github.com/org/my-tool
tap_url: https://github.com/org/homebrew-tap
token_env: HOMEBREW_TAP_TOKEN

Before Any Step Runs

Docker daemon not running

✗ docker daemon is not running — start Docker and retry (...)
✗ docker daemon did not respond within 10s — is Docker running?
✗ docker is required but was not found in PATH

Cause: at least one step that will run calls docker, and Pilum checks the daemon before building anything.

Fix: start Docker Desktop (or the daemon) and retry. If you only need non-Docker steps, filter them, for example pilum deploy --only-tags deploy. Dry runs skip this check.


Deployment locked

✗ deployment locked by PID 12345 on ci-runner-7 (started 2026-10-07T10:00:00Z, command: deploy, services: [api worker]). Use --force to override

Exit code 7.

Cause: another deploy, build, publish, push or rollback in the same project holds .pilum/deploy.lock.

Fix: wait for it to finish. Pilum clears the lock itself if the holding process is dead or the lock is more than 1 hour old. If you are sure nothing is running (for example, a CI runner was killed on another machine, so Pilum can’t check its PID), override it:

Terminal window
pilum deploy --tag v1.2.0 --force

A FORCE environment variable also sets --force; see Environment variables.


”gcp-mig-container needs a unique —tag per release”

Error: service 'egress-proxy' validation failed: gcp-mig-container needs a unique --tag per release (got "latest"): templates are named <base>-<tag>, and reusing one would make the rollout a no-op

Cause: MIG releases create an instance template named <template_base>-<tag>. An empty tag or latest would reuse a template, so Pilum rejects it before building.

Fix: pass a unique tag such as a version or commit SHA:

Terminal window
pilum deploy egress-proxy --tag v1.4.2

Tags are lowercased and ., _ and + become -, so v1.4.2 creates <template_base>-v1-4-2. The full name must fit in GCP’s 63-character limit. See GCP Managed Instance Group.


”Warming failed; continuing with cold builds”

⚠ Warming failed; continuing with cold builds

Cause: a build-cache warming command failed, for example because a lockfile is out of date or a build.warm command is wrong.

What happens: nothing breaks. The builds still run, without the shared warm cache, and any real error shows up in the build step itself.

Fix: run the warm command shown in pilum dry-run by hand to see the error. To turn warming off, use --no-warm for one run, or build.warm: false for a service. See Build Cache Warming.


Build Failures

”command not found”

sh: go: command not found

Cause: build.cmd runs through sh -c, and other steps call docker, gcloud, az, sam, npm or git directly. The tool isn’t installed or isn’t on the PATH Pilum inherits.

Fix: install the tool the step calls and make sure it’s on PATH:

Terminal window
which go docker gcloud

Build timeout

command timed out

Cause: the step ran longer than its timeout. Every built-in step sets its own timeout (300 seconds for builds), and a step’s timeout overrides --timeout. So pilum deploy --timeout 600 does not extend built-in steps.

Fix:

  1. Make the build faster: let build cache warming run, and reduce parallel pressure with --max-workers.
  2. Or copy the built-in recipes into a directory, raise that step’s timeout, and deploy with --recipe-path:
steps:
- name: build docker image
execution_mode: root
timeout: 900
tags: [build]
  1. Run the build by hand to see where the time goes:
Terminal window
cd services/api && go build -o ./dist/app .

Dockerfile not found

The build docker image step fails and Docker reports that the Dockerfile path (_templates/<template>) doesn’t exist.

Cause: Pilum doesn’t look for a Dockerfile in each service directory. The image step runs from the directory where you ran pilum:

Terminal window
docker build -t <image>:<tag> --build-arg SERVICE_NAME=<service path> -f ./_templates/<template> .

The Dockerfile is _templates/<template>, relative to that directory, and the build context is that directory. The service’s path relative to it is passed as the SERVICE_NAME build argument, so one template can serve many services.

Fix:

  1. Run pilum from the project root (the directory that contains _templates/).
  2. Check that _templates/<template> exists, where <template> is the template: value in pilum.yaml.
  3. pilum init writes _templates/<language> in the directory you run it from. If you ran it inside a service directory, move _templates/ to the project root.

failed to compute cache key: "…/dist": not found

ERROR: failed to compute cache key: failed to calculate checksum of ref …::…: "/services/api/dist": not found

Docker usually also prints an easy-to-miss warning:

1 warning found:
- CopyIgnoredFile: Attempting to Copy file "services/api/dist" that is excluded by .dockerignore (line N)

Cause: the build binary step compiles into services/<name>/dist/ on the host, and the Dockerfile copies it with something like COPY ./services/<name>/dist .. If the repo’s .dockerignore contains **/dist, dist/ or any pattern matching that directory, Docker drops it from the build context even though it exists on disk.

Fix: remove the dist pattern from .dockerignore, or narrow it to the outputs you really want excluded (for example apps/web/dist) instead of a repo-wide **/dist.

.gitignore controls what git tracks; .dockerignore controls what Docker sends to the daemon. Any path a Dockerfile COPYs must not match .dockerignore.


Authentication Errors

GCP

ERROR: (gcloud.run.deploy) PERMISSION_DENIED

Fix:

Terminal window
gcloud auth login
gcloud auth configure-docker us-central1-docker.pkg.dev # your Artifact Registry host
gcloud config set project YOUR_PROJECT_ID

In CI, authenticate the runner’s service account before running Pilum. Pilum uses whatever credentials gcloud and docker already have.


AWS

Unable to locate credentials

Fix:

Terminal window
aws configure
# or
export AWS_ACCESS_KEY_ID=...
export AWS_SECRET_ACCESS_KEY=...
export AWS_REGION=us-east-1

The aws-lambda recipe runs sam build and sam deploy, so the AWS SAM CLI must be installed too.


Azure

ERROR: Please run 'az login' to setup account.

Fix:

Terminal window
az login
az acr login --name YOUR_ACR_NAME

Push Failures

Registry access denied

denied: Permission "artifactregistry.repositories.uploadArtifacts" denied

Fix:

  1. Configure Docker for the registry host: gcloud auth configure-docker <region>-docker.pkg.dev for GCP, az acr login --name <registry> for Azure.
  2. Check registry_name. For GCP it is the bare Artifact Registry repository name, not a host or path; Pilum builds <region>-docker.pkg.dev/<project>/<registry_name>/<name> from it:
registry_name: services
  1. Make sure the repository exists in that project and region. Pilum doesn’t create it.

Deploy Problems

Existing secrets or env vars disappeared after a Cloud Run deploy

Cause: Cloud Run deploys state env vars and secrets every time. When a service has none, Pilum passes --clear-env-vars and --clear-secrets, which remove whatever is set on the live service. The usual trigger is writing secrets or env_vars as a list:

# Wrong: a list is silently ignored, so the deploy clears all secrets
secrets:
- name: DATABASE_URL
value: database-url:latest

Fix: use a map of NAME: value:

env_vars:
LOG_LEVEL: info
secrets:
DATABASE_URL: database-url:latest # secret-name:version

Check with pilum dry-run: the deploy command should show --set-secrets, not --clear-secrets. Then redeploy to restore them. See Service Configuration.


Cloud Run service returns 403

Cause: Pilum deploys Cloud Run services with --no-allow-unauthenticated unless cloud_run.allow_unauthenticated: true is set.

Fix: for a public service:

cloud_run:
allow_unauthenticated: true

Deploy reports success but nothing ran

Cause: a step with no command whose name matches no handler does nothing and counts as a success. This happens with custom recipes that use an unknown step name, and with execute job when job.execute_on_deploy is not true.

Fix: run pilum dry-run. Steps listed without a → command line are no-ops.


Rollback

”refusing to roll back without confirmation”

✗ refusing to roll back without confirmation; pass --yes to proceed non-interactively

Exit code 6.

Cause: rollback asks for confirmation, and there’s no terminal to ask on. Pilum treats a run as non-interactive when stdin isn’t a terminal or is /dev/null, when CI, GITHUB_ACTIONS, GITLAB_CI, CIRCLECI, JENKINS_URL or BUILDKITE is set, and in --json mode.

Fix: review the plan, then pass --yes:

Terminal window
pilum rollback api --dry-run
pilum rollback api --yes

“rollback is not supported”

✗ rollback is not supported for web (recipe cloudflare-pages); supported: Cloud Run services and jobs, GCP MIGs

Cause: rollback works only for gcp-cloud-run, gcp-cloud-run-from-image, gcp-cloud-run-job and gcp-mig-container.

Fix: for other targets, deploy the previous known-good tag:

Terminal window
pilum history -s web
pilum deploy web --tag v1.1.0

Traffic went back to the new revision

Cause: pilum rollback pins traffic to an older Cloud Run revision. The next pilum deploy runs route traffic to latest, which moves traffic to the new revision and resets any manual traffic split. That is intended: a deploy means “serve this”.

Fix: fix forward and deploy again, or roll back again after the deploy. See Rollback, Status & History.


Parallel Execution

Machine slows down or runs out of memory

Cause: too many builds at once. Pilum sizes build steps by CPU cores and memory, but its per-language memory estimates may be low for your builds.

Fix: cap the workers, or tell Pilum how much each build needs:

Terminal window
pilum deploy --max-workers 2 --tag v1.2.0
build:
resources:
memory: 4096 # MB per build
cpu: 4 # cores per build

On Docker Desktop, Docker steps are sized against the Docker VM’s CPUs and memory. Give the VM more resources if image builds are slow.


Services interfere with each other

Cause: parallel steps write to the same files, for example a shared output directory.

Fix: run serially to confirm, then give each service its own output path:

Terminal window
pilum deploy --max-workers 1 --tag v1.2.0

Unexpected Flag Values

Wrong tag, environment or provider with no flag set

Cause: Pilum reads environment variables named like its flags. TAG, ENV, PROVIDER, DEBUG, TIMEOUT, RETRIES, FORCE and SINCE set in your shell or CI act as defaults for those flags.

Fix: check with env | grep -E '^(TAG|ENV|PROVIDER|DEBUG|FORCE)=', unset the variable, or pass the flag explicitly. See CLI Commands.


Homebrew Issues

”push to tap” authentication fails

Cause: Pilum pushes the formula to the tap with git, using the token from the environment variable named by homebrew.token_env.

Fix: export that variable with a token that can push to the tap repository:

Terminal window
export HOMEBREW_TAP_TOKEN="ghp_xxxxx" # the name set in homebrew.token_env

Debug Mode

Terminal window
pilum deploy api --debug --tag v1.2.0

Prints, for each command:

  • the command and its arguments
  • the working directory and execution mode
  • the timeout
  • environment variables, with values of names ending in _TOKEN, _SECRET, _KEY, _PASSWORD or _CREDENTIAL redacted

--verbose streams each command’s output as it runs.

Getting Help

If the problem persists:

  1. Run pilum dry-run and the failing command with --debug.
  2. Search GitHub Issues.
  3. Open an issue with your Pilum version (pilum --version), the full error, the pilum.yaml with secrets removed, the recipe type, and the debug output.

Next Steps