BLOG/BY COMPANY SIZE
THEME
BY COMPANY SIZE

OrchStep for scale-ups

Multiple services, three or four environments, and a deploy that has to behave differently in production than in staging. Here's how OrchStep keeps one workflow honest across every environment.

Apr 5, 2026 OrchStep Team 7 minROLE: AnySCALE: Scale-up

At scale-up size the shape of the problem changes. You're past "does it deploy" and into "does it deploy the same way to staging and production, every time, run by people who didn't write it." You have several services, three or four environments, and a deploy that genuinely needs to behave differently in prod — more monitoring, a manual approval — without becoming a different script per target.

The failure mode here is environment drift: a deploy-staging.sh and a deploy-prod.sh that started identical and quietly forked. One gets a fix the other doesn't, and the gap between them is exactly where the 2 a.m. incident lives. The job at this stage is to keep one workflow and let the environment supply the differences.

What automation looks like at this stage

You've outgrown one flat orchstep.yml with a single target. You want named environments that bundle their own settings — monitoring level, whether approval is automatic — selected at the command line. OrchStep's inline environments do exactly this: shared values live in an env_groups family, each environment selects a group and overrides a few vars, and you pick one with --env.

One deploy, many environments

Here nonprod and prod are groups; staging and production are concrete environments that inherit from them. The deploy task is written once. The gate reads approval — a value that comes from the environment, not from a branch in the code — so production holds for approval while staging auto-promotes, with zero duplicated steps.

orchstep.yml
name: platform
defaults:
  version: "3.4.0"
  artifact: ghcr.io/acme/api

# Shared settings for a family of environments.
env_groups:
  nonprod:
    vars: { monitoring: basic, approval: auto }
  prod:
    vars: { monitoring: full, approval: manual }

# Concrete targets. Each selects a group, overrides a few vars.
environments:
  staging:
    group: nonprod
    vars: { target: staging-cluster }
  production:
    group: prod
    vars: { target: prod-cluster }

tasks:
  # orchstep run deploy --env production
  deploy:
    steps:
      - name: build
        func: shell
        do: echo "building {{ vars.artifact }}:{{ vars.version }}"
      - name: push
        func: shell
        do: echo "pushing {{ vars.artifact }}:{{ vars.version }} ({{ vars.monitoring }} monitoring)"
        retry:
          max_attempts: 3
          interval: "2s"
          backoff_rate: 2.0
      - name: gate
        if: '{{ eq vars.approval "manual" }}'
        then:
          - name: hold
            func: shell
            do: echo "{{ vars.target }} requires approval before promotion"
        else:
          - name: auto
            func: shell
            do: echo "auto-promoting to {{ vars.target }}"

Run it against either environment and the only thing you change is the flag:

orchstep run deploy --env staging      # basic monitoring, auto-promote
orchstep run deploy --env production   # full monitoring, holds for approval

Same build, same push, same retry. The behavioral difference between staging and production is data in the environment block, not a forked script — so the two can't drift, because there's only one.

Catch differences before they ship

With more environments comes more ways to be surprised. The dry run resolves a specific environment and prints the exact plan, so you can confirm production really does take the approval branch before you run it:

orchstep run deploy --env production --dry-run

You can read the resolved monitoring: full and the hold step in the output. That's a real review artifact — paste it in a PR — instead of "trust me, prod is different."

What you gained

ConcernA script per environmentOne workflow + environments
Staging vs prod logictwo diverging scriptsone task, values from --env
Environment driftinevitableimpossible — single definition
Prod approvala separate prod scriptif: gate on an env-supplied var
Flaky registry pushre-run by handretry: with backoff
"Is prod actually different?"read both scripts--dry-run --env production
Adding an environmentcopy a whole scripta few lines under environments:

Where OrchStep is not the answer

If you've already standardized on a deployment controller — Argo CD, Spinnaker, a real progressive-delivery system — let it own rollout strategy and health gating; OrchStep shouldn't reimplement that. Where it fits at scale-up is the orchestration around those tools: the build, the promotion logic, the per-environment differences your team runs by hand and in CI. Use it for the glue, not as a replacement for a delivery platform you already trust.

Where to go next

One orchstep.yml, every environment. Define your targets under environments: and switch with --env.

#SCALE-UP#ENVIRONMENTS#DEPLOY#CI-CD
Try it in two minutes — one binary, no signup.
curl -fsSL https://orchstep.dev/install.sh | sh

RELATED — BY COMPANY SIZE