id, optional name, timeout, and if condition — and adds fields specific to its type. Wrap steps in parallel or group blocks to control how they run. See Pipelines overview.
Steps fall into three categories:
- Module steps act on a module instance through its build or deploy manager:
build,deploy,rollback. - CI steps run on provisioned compute that you configure with an
infrastructureblock:build:image,build:static,custom,terraform:plan,terraform:apply. - Control-flow steps shape the run itself:
approval,sleep,trigger:pipeline.
parallel and group have no id or type, and they hold steps rather than doing work.
For every field, default, and nested type, see the pipeline configuration schema.
Module steps
build
Triggers a build for a module instance using the module’s own build configuration. Reference the module instance by unique ID (mi_…), environmentGivenId.moduleGivenId, or projectGivenId.environmentGivenId.moduleGivenId. Pass build parameters through input; outputs such as image_digest are available to later steps.
build steps use value: 2 with behavior: cancel-intermediate, keyed by module instance and input.branch when set. This replaces young runs during a quick burst, then protects an older run so it can finish. See Concurrency for the 15-second replacement window and 30-second cap. An explicit concurrency block replaces this default; CI steps such as build:image do not inherit it.
See BuildStep schema.
deploy
Triggers a deployment for a module instance using the module’s deploy configuration. Commonly consumes a preceding build’s output to pin the exact artifact. Concurrency is managed by the deploy manager lock, not the pipeline scheduler.rollback
Reverts a module instance to a specific previous deployment bydeployment_id.
CI steps
Use sandboxes for CI steps by default. Setinfrastructure.type: sandbox, choose an execution environment or an AWS account and region, and set the CPU and memory. The examples below use ci-builders as an execution environment given ID; replace it with yours. Use ec2 or ec2-spot only when you need a custom AMI or direct access to a dedicated EC2 instance. See StepInfrastructure.
The
nixpacks builder is deprecated for pipeline CI steps. Use railpack for automatic framework detection.build:image
Builds a Docker image from a git source and pushes it to one or more ECR destinations. Build with adockerfile or let railpack auto-detect the language and framework.
build:static
Builds static assets from a git source and uploads them to one or more S3 destinations. Build with adockerfile or railpack.
custom
Runs arbitrary shell commands on provisioned compute — tests, linters, migrations, or any scripted task. Optionally check out a gitsource, install tools with setup actions, and pass environment_variables.
Publishing outputs
Acustom step can publish string values for later steps to consume. Declare the output names in the step’s outputs list, then have your commands append name=value lines to the file at $RAVION_OUTPUT:
outputs are published — lines written to $RAVION_OUTPUT for undeclared names are ignored. Each name must be a valid shell variable name, and values are always strings.
terraform:plan
Generates a Terraform or OpenTofu plan and uploads the plan file to S3 for a later apply. Setplan_type to destroy to plan a teardown. Supply input variables with terraform_variables or terraform_variable_files.
terraform:apply
Applies a plan file produced by a precedingterraform:plan step. Reference the plan with plan_file_uri, typically from the plan step’s output.
Runner permissions
Each CI step runs with an IAM role Ravion creates in the step’s AWS account. By default, the role gets these managed policies for its step type:
Change them with
infrastructure.permissions. Both forms take IAM managed policy ARNs: AWS managed policies (arn:aws:iam::aws:policy/<name>) or customer managed policies in the step’s AWS account (arn:aws:iam::<account-id>:policy/<name>).
attachkeeps the defaults and adds your policies.replacedrops every default for the step, includingAdministratorAccessonterraform:apply, and uses only your policies.
custom step that only updates a Lambda function’s code needs one customer managed policy that grants lambda:UpdateFunctionCode on that function:
AdministratorAccess, use replace on the terraform:apply step with a policy you scope yourself:
replace on a Terraform step, your policies must cover:
- Every resource your configuration creates, updates, or deletes. Apply also refreshes state, so it needs read access to those resources.
- Your own state backend, if you keep one — for example, the S3 state bucket, the DynamoDB lock table or S3 lock file, and
kms:Decryptandkms:GenerateDataKeyon the state bucket’s KMS key. - Any other AWS services your configuration’s data sources read.
- The S3 bucket that holds plan files.
terraform:planuploads the plan andterraform:applydownloads it fromplan_file_uri. By default this isravion-terraform-plans-<aws-account-number>in the step’s AWS account (us-east-1), underterraform-plans/, unless you setplan_file_directory_uri. Grants3:PutObject,s3:GetObject, ands3:ListBucketon it, pluss3:CreateBucketif the bucket doesn’t exist yet. The runner creates it on first use.
ec2 and ec2-spot runners, the role always has AmazonSSMManagedInstanceCore and CloudWatchAgentServerPolicy, which Ravion uses to run the step and stream its logs. When a step reads environment variables from Secrets Manager, Ravion also grants secretsmanager:GetSecretValue on those secrets.
For stacks, put these overrides in a custom pipeline and point the stack at it with change_pipeline_id and destroy_pipeline_id. See System pipelines and customization.
See AwsPermissions schema.
Control-flow steps
approval
Pauses the pipeline until a user approves through the dashboard or REST API. Use it to gate production deployments behind a manual review.sleep
Pauses execution for a fixedduration_ms. Useful for spacing out steps or for testing and debugging pipeline behavior.
trigger:pipeline
Starts another pipeline run and passesinputs to it. Optionally pin a pipeline_version_id or override behavior per variant with run.
Blocks
Steps in a list run sequentially in order. Two block types change that:parallel runs its children at the same time, and group labels a nested sequence. Most pipelines need neither — a flat list of steps is the default shape, and many pipelines need only parallel.
parallel
Runs every child concurrently. The block finishes when all children finish, so a step after the block waits for all of them. Children are action steps orgroup blocks; a parallel block can’t directly contain another parallel block. fail_strategy controls what happens to the other children when one fails: finish-all (default) lets the block finish its remaining work, finish-running lets in-flight children finish their current step, and cancel-all cancels them immediately.
parallel when the children are independent of each other but something later in the pipeline depends on all of them. If nothing downstream depends on them together, they don’t belong in the same pipeline — see When not to use blocks.
See ParallelStep schema.
group
Runs itssteps sequentially, exactly as they would run at the top level, and shows them under one label in the UI. A group adds no scheduling behavior to its children; its optional concurrency config controls how competing executions of the whole group block are admitted across runs (by key and scope), not how the steps inside one run are throttled. A run takes the group’s slot when the group’s first step starts and keeps it until every step in the group has finished. With value: 1, another run’s execution of the group can’t start until this one’s last step is done. The same applies to a parallel block’s concurrency. Its main job is to put a multi-step sequence inside a parallel block, whose children are otherwise single steps:
group just to name it — a pipeline with one group containing all its steps is the same pipeline with extra nesting. Groups are the rarest block; if you aren’t nesting a sequence inside parallel or gating the block with a concurrency key, leave it out.
See GroupStep schema.
When not to use blocks
A pipeline models one workflow: a set of steps that depend on each other through outputs, gates, or ordering. Blocks arrange the steps inside that workflow; they aren’t a way to run several unrelated workflows from one config. The pattern to avoid is a top-levelparallel whose children are group blocks that each build and deploy a different service, with no step that depends on more than one of them:
api-pipeline.yaml
docs-pipeline.yaml