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/ and cpflow-* 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-prompt

apply-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/templates directory
  • 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
# 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_NAME

build-image

  • Builds and pushes the image to Control Plane
  • Automatically assigns image numbers, e.g., app:1, app:2, etc.
  • Uses .controlplane/Dockerfile or a different Dockerfile specified through dockerfile in the .controlplane/controlplane.yml file
  • If a commit is provided through --commit or -c, it will be set as the runtime env var GIT_COMMIT
  • Accepts extra options that are passed to docker build
cpflow build-image -a $APP_NAME

cleanup-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_qty in the .controlplane/controlplane.yml file
  • Specify the amount of days through image_retention_days in the .controlplane/controlplane.yml file
  • If image_retention_max_qty is specified, any images that exceed it will be deleted, regardless of image_retention_days
  • Will ask for explicit user confirmation
  • Never deletes the latest image
cpflow cleanup-images -a $APP_NAME

cleanup-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 as long as both the identity and the policy exist (and are bound)
  • With --mode=stop: suspends all workloads via cpflow ps:stop — no GVC, volumeset, or image is removed; resume with cpflow ps:start
  • --mode=stop only suspends workloads listed in app_workloads + additional_workloads; workloads present in the live GVC but missing from the config are skipped silently
  • --mode=stop returns 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_days in the .controlplane/controlplane.yml file
  • If match_if_app_name_starts_with is true in the .controlplane/controlplane.yml file, 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=stop

config

  • 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_NAME

copy-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_UPSTREAM env var or upstream in the .controlplane/controlplane.yml file
  • The token for the source org must be provided through --upstream-token/-t or the CPLN_UPSTREAM_TOKEN env var
  • A cpln profile 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:123

delete

  • 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, as long as both the identity and the policy exist (and are bound)
  • Will ask for explicit user confirmation
  • Runs a pre-deletion hook before the app is deleted if hooks.pre_deletion is specified in the .controlplane/controlplane.yml file
  • 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-hook to skip the hook if specified in controlplane.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_NAME

deploy-image

  • Deploys the latest image to app workloads
  • Runs a release script before deploying if release_script is specified in the .controlplane/controlplane.yml file and --run-release-phase is provided
  • The release script is run in the context of cpflow run with 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_ref is true in the .controlplane/controlplane.yml file or --use-digest-image-ref option is provided, deployed image's reference will include its digest
cpflow deploy-image -a $APP_NAME

doctor

  • 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_NAME

env

  • Displays app-specific environment variables
cpflow env -a $APP_NAME

exists

  • 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"
fi

generate

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's Gemfile
  • preserves repo-defined asset precompile hooks, including React on Rails auto bundle generation
  • detects SQLite in config/database.yml and generates persistent db and storage volume templates instead of the default Postgres workload
# Creates .controlplane directory with Control Plane config and starter templates
cpflow generate

generate-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.

# 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

github-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-readiness

info

  • 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.yml file
# 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_NAME

latest-image

  • Displays the latest image name
cpflow latest-image -a $APP_NAME

logs

  • 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 30min

maintenance

  • Checks if maintenance mode is on or off for an app
  • Outputs 'on' or 'off'
  • Specify the one-off workload through one_off_workload in the .controlplane/controlplane.yml file
  • Optionally specify the maintenance workload through maintenance_workload in the .controlplane/controlplane.yml file (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_NAME

maintenance:off

  • Disables maintenance mode for an app
  • Specify the one-off workload through one_off_workload in the .controlplane/controlplane.yml file
  • Optionally specify the maintenance workload through maintenance_workload in the .controlplane/controlplane.yml file (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_NAME

maintenance:on

  • Enables maintenance mode for an app
  • Specify the one-off workload through one_off_workload in the .controlplane/controlplane.yml file
  • Optionally specify the maintenance workload through maintenance_workload in the .controlplane/controlplane.yml file (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_NAME

maintenance:set-page

  • Sets the page for maintenance mode
  • Only works if the maintenance workload uses the shakacode/maintenance-mode image
  • Will set the URL as an env var PAGE_URL on the maintenance workload
  • Optionally specify the maintenance workload through maintenance_workload in the .controlplane/controlplane.yml file (defaults to 'maintenance')
cpflow maintenance:set-page PAGE_URL -a $APP_NAME

open

  • 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_NAME

open-console

  • Opens the app console on Control Plane in the default browser
  • Can also go directly to a workload page if --workload is provided
cpflow open-console -a $APP_NAME

promote-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-upstream to copy the latest image from upstream
    • Runs cpflow deploy-image to deploy the image
    • If .controlplane/controlplane.yml includes the release_script, cpflow deploy-image will use the --run-release-phase option
    • 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_ref is true in the .controlplane/controlplane.yml file or --use-digest-image-ref option is provided, deployed image's reference will include its digest
cpflow promote-app-from-upstream -a $APP_NAME -t $UPSTREAM_TOKEN

ps

  • 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_NAME

ps: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_NAME

ps: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_NAME

ps: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_NAME

ps: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_NAME

run

  • Runs one-off interactive or non-interactive replicas (analog of heroku run)
  • Uses Cron workload type and either:
    • cpln workload exec for 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 is bash, in which case the args ["-c", cmd_to_run] are passed
  • Providing --entrypoint none sets the entrypoint to bash by default
  • If fix_terminal_size is true in the .controlplane/controlplane.yml file, 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_cpu and runner_job_default_memory in controlplane.yml, and also overridden per job through --cpu and --memory)
  • By default, the job is stopped if it takes longer than 6 hours to finish (can be configured though runner_job_timeout in controlplane.yml)
# 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:migrate

setup-app

  • Creates an app and all its workloads
  • Specify the templates for the app and workloads through setup_app_templates in the .controlplane/controlplane.yml file
  • This should only be used for temporary apps like review apps, never for persistent apps like production or staging (to update workloads for those, use 'cpflow apply-template' instead)
  • 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
  • Use --skip-secrets-setup to prevent the automatic setup of secrets, or set it through skip_secrets_setup in the .controlplane/controlplane.yml file
  • Runs a post-creation hook after the app is created if hooks.post_creation is specified in the .controlplane/controlplane.yml file
  • 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-hook to skip the hook if specified in controlplane.yml
cpflow setup-app -a $APP_NAME

terraform generate

  • Generates terraform configuration files based on controlplane.yml and templates/ config
cpflow terraform generate

terraform import

  • Imports terraform resources from the generated configuration files
cpflow terraform import

version

  • Displays the current version of the CLI
  • Can also be done with cpflow --version or cpflow -v
cpflow version