Common Options
-a XXX, --app XXX app ref on Control Plane (GVC)This -a option is used in most of the commands and will pick all other app configurations from the project-specific
.controlplane/controlplane.yml file.
Commands
ai-github-flow-prompt
Prints a copy-paste prompt for an AI agent to roll out the reusable Control Plane GitHub Flow:
- verifies the repo is deployable from a clean clone before generating files
- scaffolds
.controlplane/andcpflow-*GitHub Actions files when the repo qualifies - stops on external blockers or product decisions instead of forcing a broken rollout
# Prints the recommended AI rollout prompt for the current repo
cpflow ai-github-flow-promptapply-template
- Applies application-specific configs from templates (e.g., for every review-app)
- Publishes (creates or updates) those at Control Plane infrastructure
- Picks templates from the
.controlplane/templatesdirectory - Templates are ordinary Control Plane templates but with variable preprocessing
Preprocessed template variables:
{{APP_ORG}} - organization name
{{APP_NAME}} - GVC/app name
{{APP_LOCATION}} - location, per YML file, ENV, or command line arg
{{APP_LOCATION_LINK}} - full link for location, ready to be used for the value of `staticPlacement.locationLinks` in the templates
{{APP_IMAGE}} - latest app image
{{APP_IMAGE_LINK}} - full link for latest app image, ready to be used for the value of `containers[].image` in the templates
{{APP_IDENTITY}} - default identity
{{APP_IDENTITY_LINK}} - full link for identity, ready to be used for the value of `identityLink` in the templates
{{APP_SECRETS}} - app secret dictionary name
{{APP_SECRETS_POLICY}} - app secret policy name
{{SHARED_SECRET_<NAME>}} - shared secret dictionary name from `shared_secret_grants`# Applies single template.
cpflow apply-template redis -a $APP_NAME
# Applies several templates (practically creating full app).
cpflow apply-template app postgres redis rails -a $APP_NAMEbuild-image
- Builds and pushes the image to Control Plane
- Automatically assigns image numbers, e.g.,
app:1,app:2, etc. - Uses
.controlplane/Dockerfileor a different Dockerfile specified throughdockerfilein the.controlplane/controlplane.ymlfile - If a commit is provided through
--commitor-c, it will be set as the runtime env varGIT_COMMIT - Accepts extra options that are passed to
docker build
cpflow build-image -a $APP_NAMEcleanup-images
- Deletes all images for an app that either exceed the max quantity or are older than the specified amount of days
- Specify the max quantity through
image_retention_max_qtyin the.controlplane/controlplane.ymlfile - Specify the amount of days through
image_retention_daysin the.controlplane/controlplane.ymlfile - If
image_retention_max_qtyis specified, any images that exceed it will be deleted, regardless ofimage_retention_days - Will ask for explicit user confirmation
- Never deletes the latest image
cpflow cleanup-images -a $APP_NAMEcleanup-stale-apps
- Acts on stale apps based on the creation date of the latest image, or the GVC if no images exist
- With
--mode=delete(default): deletes the whole app (GVC with all workloads, all volumesets and all images), and unbinds the app from the secrets policy and any configuredshared_secret_grantspolicies as long as both the identity and each policy exist (and are bound) - With
--mode=stop: suspends all workloads viacpflow ps:stop— no GVC, volumeset, or image is removed; resume withcpflow ps:start --mode=stoponly suspends workloads listed inapp_workloads+additional_workloads; workloads present in the live GVC but missing from the config are skipped silently--mode=stopreturns once each workload is marked suspended; it does not wait for the workload to reach a not-ready state- Specify the amount of days after an app should be considered stale through
stale_app_image_deployed_daysin the.controlplane/controlplane.ymlfile - If
match_if_app_name_starts_withistruein the.controlplane/controlplane.ymlfile, it will act on all stale apps that start with the name - Will ask for explicit user confirmation
# Deletes stale apps (default).
cpflow cleanup-stale-apps -a $APP_NAME
# Stops stale apps instead of deleting them; resume with `cpflow ps:start`.
cpflow cleanup-stale-apps -a $APP_NAME --mode=stopconfig
- Displays config for each app or a specific app
# Shows the config for each app.
cpflow config
# Shows the config for a specific app.
cpflow config -a $APP_NAMEcopy-image-from-upstream
- Copies an image (by default the latest) from a source org to the current org
- The source app must be specified either through the
CPLN_UPSTREAMenv var orupstreamin the.controlplane/controlplane.ymlfile - The token for the source org must be provided through
--upstream-token/-tor theCPLN_UPSTREAM_TOKENenv var - A
cplnprofile will be temporarily created to pull the image from the source org
# Copies the latest image from the source org to the current org.
cpflow copy-image-from-upstream -a $APP_NAME --upstream-token $UPSTREAM_TOKEN
# Equivalent call using an env var (avoids exposing the token via the OS process table).
CPLN_UPSTREAM_TOKEN=$UPSTREAM_TOKEN cpflow copy-image-from-upstream -a $APP_NAME
# Copies a specific image from the source org to the current org.
cpflow copy-image-from-upstream -a $APP_NAME --upstream-token $UPSTREAM_TOKEN --image appimage:123delete
- Deletes the whole app (GVC with all workloads, all volumesets and all images) or a specific workload
- Also unbinds the app from the secrets policy and any configured
shared_secret_grantspolicies, as long as both the identity and each policy exist (and are bound) - For the app-specific secrets policy, removes every permission held by the app identity; for
shared_secret_grants, removes onlyreveal - Will ask for explicit user confirmation
- Runs a pre-deletion hook before the app is deleted if
hooks.pre_deletionis specified in the.controlplane/controlplane.ymlfile - If the hook exits with a non-zero code, the command will stop executing and also exit with a non-zero code
- Use
--skip-pre-deletion-hookto skip the hook if specified incontrolplane.yml
# Deletes the whole app (GVC with all workloads, all volumesets and all images).
cpflow delete -a $APP_NAME
# Deletes a specific workload.
cpflow delete -a $APP_NAME -w $WORKLOAD_NAMEdeploy-image
- Deploys the latest image to app workloads
- Runs a release script before deploying if
release_scriptis specified in the.controlplane/controlplane.ymlfile and--run-release-phaseis provided - The release script is run in the context of
cpflow runwith the latest image - If the release script exits with a non-zero code, the command will stop executing and also exit with a non-zero code
- If
use_digest_image_refistruein the.controlplane/controlplane.ymlfile or--use-digest-image-refoption is provided, deployed image's reference will include its digest - Repairs missing
shared_secret_grantspolicy bindings before running a release phase or updating workloads
cpflow deploy-image -a $APP_NAMEdoctor
- Runs validations
# Runs all validations that don't require additional options by default.
cpflow doctor
# Runs config validation.
cpflow doctor --validations config
# Runs templates validation (requires app).
cpflow doctor --validations templates -a $APP_NAMEenv
- Displays app-specific environment variables
cpflow env -a $APP_NAMEexists
- Shell-checks if an application (GVC) exists, useful in scripts, e.g.:
- Exits 0 when the app exists, 3 when it does not exist, and 64 for other errors.
cpflow exists -a "$APP_NAME"
status=$?
if [ "$status" -eq 0 ]; then
echo "exists"
elif [ "$status" -eq 3 ]; then
echo "not found"
else
echo "error: cpflow exists exited $status"
figenerate
Creates base Control Plane config and template files for a Rails project:
- infers the app prefix from the current directory and wires staging, review, and production entries
- infers the Docker base Ruby version from
.ruby-version,.tool-versions, or the app'sGemfile - preserves repo-defined asset precompile hooks, including React on Rails auto bundle generation
- detects SQLite in
config/database.ymland generates persistentdbandstoragevolume templates instead of the default Postgres workload
# Creates .controlplane directory with Control Plane config and starter templates
cpflow generategenerate-github-actions
Creates GitHub Actions templates for a Heroku Flow style Control Plane pipeline:
- on-demand review apps for pull requests
- automatic staging deploys from your main branch
- manual promotion from staging to production
- nightly cleanup and PR help workflows
Pass --staging-branch BRANCH when staging should auto-deploy from a branch
other than main or master; the generator will bake that branch into the
GitHub Actions push trigger and use it as the default STAGING_APP_BRANCH.
Pass --force to overwrite existing generated files. Prefer
cpflow update-github-actions after bumping the cpflow gem in a downstream
repo.
# Creates thin .github/workflows wrappers for the Control Plane flow
cpflow generate-github-actions
# Creates the flow with staging deploys triggered from develop
cpflow generate-github-actions --staging-branch develop
# Overwrites existing generated wrappers from the installed cpflow gem
cpflow generate-github-actions --forcegithub-flow-readiness
Checks the current repository for common rollout blockers before adding the Control Plane GitHub flow:
- Rails runtime scaffold present
- modern Ruby and Bundler toolchain
- installable exact-pinned direct gem and npm package versions
- production Dockerfile presence and SQLite production hints
# Checks the current repo for common rollout blockers
cpflow github-flow-readinessinfo
- Displays the diff between defined/available apps/workloads (apps equal GVCs)
- Apps that are defined but not available are displayed in red
- Apps that are available but not defined are displayed in green
- Apps that are both defined and available are displayed in white
- The diff is based on what's defined in the
.controlplane/controlplane.ymlfile
# Shows diff for all apps in all orgs (based on `.controlplane/controlplane.yml`).
cpflow info
# Shows diff for all apps in a specific org.
cpflow info -o $ORG_NAME
# Shows diff for a specific app.
cpflow info -a $APP_NAMElatest-image
- Displays the latest image name
cpflow latest-image -a $APP_NAMElogs
- Light wrapper to display tailed raw logs for app/workload syntax
- Defaults to showing the last 200 entries from the past 1 hour before tailing
# Displays logs for the default workload (`one_off_workload`).
cpflow logs -a $APP_NAME
# Displays logs for a specific workload.
cpflow logs -a $APP_NAME -w $WORKLOAD_NAME
# Displays logs for a specific replica of a workload.
cpflow logs -a $APP_NAME -w $WORKLOAD_NAME -r $REPLICA_NAME
# Uses a different limit on number of entries.
cpflow logs -a $APP_NAME --limit 100
# Uses a different loopback window.
cpflow logs -a $APP_NAME --since 30minmaintenance
- Checks if maintenance mode is on or off for an app
- Outputs 'on' or 'off'
- Specify the one-off workload through
one_off_workloadin the.controlplane/controlplane.ymlfile - Optionally specify the maintenance workload through
maintenance_workloadin the.controlplane/controlplane.ymlfile (defaults to 'maintenance') - Maintenance mode is only supported for domains that use path based routing mode and have a route configured for the prefix '/' on either port 80 or 443
cpflow maintenance -a $APP_NAMEmaintenance:off
- Disables maintenance mode for an app
- Safe to re-run: if a previous run timed out after switching the domain but before stopping the maintenance workload, re-running while maintenance mode is already disabled stops the maintenance workload to finish it (so it is not a pure no-op)
- Specify the one-off workload through
one_off_workloadin the.controlplane/controlplane.ymlfile - Optionally specify the maintenance workload through
maintenance_workloadin the.controlplane/controlplane.ymlfile (defaults to 'maintenance') - Maintenance mode is only supported for domains that use path based routing mode and have a route configured for the prefix '/' on either port 80 or 443
cpflow maintenance:off -a $APP_NAMEmaintenance:on
- Enables maintenance mode for an app
- Safe to re-run: if a previous run timed out after switching the domain but before stopping the app workloads, re-running while maintenance mode is already enabled stops the app workloads to finish it (so it is not a pure no-op)
- Specify the one-off workload through
one_off_workloadin the.controlplane/controlplane.ymlfile - Optionally specify the maintenance workload through
maintenance_workloadin the.controlplane/controlplane.ymlfile (defaults to 'maintenance') - Maintenance mode is only supported for domains that use path based routing mode and have a route configured for the prefix '/' on either port 80 or 443
cpflow maintenance:on -a $APP_NAMEmaintenance:set-page
- Sets the page for maintenance mode
- Only works if the maintenance workload uses the
shakacode/maintenance-modeimage - Will set the URL as an env var
PAGE_URLon the maintenance workload - Optionally specify the maintenance workload through
maintenance_workloadin the.controlplane/controlplane.ymlfile (defaults to 'maintenance')
cpflow maintenance:set-page PAGE_URL -a $APP_NAMEopen
- Opens the app endpoint URL in the default browser
# Opens the endpoint of the default workload (`one_off_workload`).
cpflow open -a $APP_NAME
# Opens the endpoint of a specific workload.
cpflow open -a $APP_NAME -w $WORKLOAD_NAMEopen-console
- Opens the app console on Control Plane in the default browser
- Can also go directly to a workload page if
--workloadis provided
cpflow open-console -a $APP_NAMEpromote-app-from-upstream
- Copies the latest image from upstream, runs a release script (optional), and deploys the image
- It performs the following steps:
- Runs
cpflow copy-image-from-upstreamto copy the latest image from upstream - Runs
cpflow deploy-imageto deploy the image - If
.controlplane/controlplane.ymlincludes therelease_script,cpflow deploy-imagewill use the--run-release-phaseoption - If the release script exits with a non-zero code, the command will stop executing and also exit with a non-zero code
- If
use_digest_image_refistruein the.controlplane/controlplane.ymlfile or--use-digest-image-refoption is provided, deployed image's reference will include its digest
- Runs
cpflow promote-app-from-upstream -a $APP_NAME -t $UPSTREAM_TOKENps
- Shows running replicas in app
# Shows running replicas in app, for all workloads.
cpflow ps -a $APP_NAME
# Shows running replicas in app, for a specific workload.
cpflow ps -a $APP_NAME -w $WORKLOAD_NAMEps:restart
- Forces redeploy of workloads in app
# Forces redeploy of all workloads in app.
cpflow ps:restart -a $APP_NAME
# Forces redeploy of a specific workload in app.
cpflow ps:restart -a $APP_NAME -w $WORKLOAD_NAMEps:start
- Starts workloads in app
# Starts all workloads in app.
cpflow ps:start -a $APP_NAME
# Starts a specific workload in app.
cpflow ps:start -a $APP_NAME -w $WORKLOAD_NAMEps:stop
- Stops workloads in app
# Stops all workloads in app.
cpflow ps:stop -a $APP_NAME
# Stops a specific workload in app.
cpflow ps:stop -a $APP_NAME -w $WORKLOAD_NAME
# Stops a specific replica of a workload.
cpflow ps:stop -a $APP_NAME -w $WORKLOAD_NAME -r $REPLICA_NAMEps:wait
- Waits for workloads in app to be ready after re-deployment
- Use Unix timeout command to set a maximum wait time (e.g.,
timeout 300 cpflow ps:wait ...)
# Waits for all workloads in app.
cpflow ps:wait -a $APP_NAME
# Waits for a specific workload in app.
cpflow ps:wait -a $APP_NAME -w $WORKLOAD_NAME
# Waits for all workloads with a 5-minute timeout.
timeout 300 cpflow ps:wait -a $APP_NAMErun
- Runs one-off interactive or non-interactive replicas (analog of
heroku run) - Uses
Cronworkload type and either: -
cpln workload execfor interactive mode, with CLI streaming
-
- log async fetching for non-interactive mode
- The Dockerfile entrypoint is used as the command by default, which assumes
exec "${@}"to be present, and the args ["bash", "-c", cmd_to_run] are passed - The entrypoint can be overridden through
--entrypoint, which must be a single command or a script path that exists in the container, and the args ["bash", "-c", cmd_to_run] are passed, unless the entrypoint isbash, in which case the args ["-c", cmd_to_run] are passed - Providing
--entrypoint nonesets the entrypoint tobashby default - If
fix_terminal_sizeistruein the.controlplane/controlplane.ymlfile, the remote terminal size will be fixed to match the local terminal size (may also be overridden through--terminal-size) - By default, all jobs use a CPU size of 1 (1 core) and a memory size of 2Gi (2 gibibytes)
(can be configured through
runner_job_default_cpuandrunner_job_default_memoryincontrolplane.yml, and also overridden per job through--cpuand--memory) - By default, the job is stopped if it takes longer than 6 hours to finish
(can be configured though
runner_job_timeoutincontrolplane.yml) - Non-interactive jobs return the Control Plane cron job status even when the job finishes before Control Plane exposes a runner replica to attach logs to
# Opens shell (bash by default).
cpflow run -a $APP_NAME
# Runs interactive command, keeps shell open, and stops job when exiting.
cpflow run -a $APP_NAME --interactive -- rails c
# Some commands are automatically detected as interactive, so no need to pass `--interactive`.
cpflow run -a $APP_NAME -- bash
cpflow run -a $APP_NAME -- rails console
cpflow run -a $APP_NAME -- rails c
cpflow run -a $APP_NAME -- rails dbconsole
cpflow run -a $APP_NAME -- rails db
# Runs non-interactive command, outputs logs, exits with the exit code of the command and stops job.
cpflow run -a $APP_NAME -- rails db:migrate
# Runs non-iteractive command, detaches, exits with 0, and prints commands to:
# - see logs from the job
# - stop the job
cpflow run -a $APP_NAME --detached -- rails db:migrate
# The command needs to be quoted if setting an env variable or passing args.
cpflow run -a $APP_NAME -- 'SOME_ENV_VAR=some_value rails db:migrate'
# Uses a different image (which may not be promoted yet).
cpflow run -a $APP_NAME --image appimage:123 -- rails db:migrate # Exact image name
cpflow run -a $APP_NAME --image latest -- rails db:migrate # Latest sequential image
# Uses a different workload than `one_off_workload` from `.controlplane/controlplane.yml`.
cpflow run -a $APP_NAME -w other-workload -- bash
# Overrides remote CPLN_TOKEN env variable with local token.
# Useful when superuser rights are needed in remote container.
cpflow run -a $APP_NAME --use-local-token -- bash
# Replaces the existing Dockerfile entrypoint with `bash`.
cpflow run -a $APP_NAME --entrypoint none -- rails db:migrate
# Replaces the existing Dockerfile entrypoint.
cpflow run -a $APP_NAME --entrypoint /app/alternative-entrypoint.sh -- rails db:migratesetup-app
- Creates an app and all its workloads
- Specify the templates for the app and workloads through
setup_app_templatesin the.controlplane/controlplane.ymlfile - Use this for temporary apps like review apps and for first-time bootstrap of persistent staging or production apps; after a persistent app exists, use 'cpflow apply-template' for template updates
- Configures app to have org-level secrets with default name
"{APP_PREFIX}-secrets"using org-level policy with default name"{APP_PREFIX}-secrets-policy"(names can be customized, see docs) - Creates identity for secrets if it does not exist
- Binds the app identity to any configured
shared_secret_grantspolicies as part of the secrets setup flow; skipped when--skip-secrets-setupor--skip-secret-access-bindingis provided, orskip_secrets_setupis set - Use
--skip-secrets-setupto prevent the automatic setup of secrets, or set it throughskip_secrets_setupin the.controlplane/controlplane.ymlfile - Runs a post-creation hook after the app is created if
hooks.post_creationis specified in the.controlplane/controlplane.ymlfile - If the hook exits with a non-zero code, the command will stop executing and also exit with a non-zero code
- Use
--skip-post-creation-hookto skip the hook if specified incontrolplane.yml
cpflow setup-app -a $APP_NAMEterraform generate
- Generates terraform configuration files based on
controlplane.ymlandtemplates/config
cpflow terraform generateterraform import
- Imports terraform resources from the generated configuration files
cpflow terraform importupdate-github-actions
Regenerates the generated cpflow GitHub Actions wrappers and helper files
from the currently installed cpflow gem. Use this after updating the
cpflow gem so checked-in workflow wrappers move to the matching upstream
release tag, for example v5.1.1.
If the existing generated staging workflow uses a custom single staging
branch, the command preserves it. Pass --staging-branch BRANCH to set or
replace the generated staging branch explicitly.
# After updating the cpflow gem, refresh generated GitHub Actions wrappers
cpflow update-github-actions
# When running cpflow through Bundler
bundle exec cpflow update-github-actions
# Preserve or set a custom staging branch
cpflow update-github-actions --staging-branch developversion
- Displays the current version of the CLI
- Can also be done with
cpflow --versionorcpflow -v
cpflow version