BLOG/BY COMPANY SIZE
THEME
BY COMPANY SIZE

OrchStep for agencies and consultancies

Every client repo is a little different, but the delivery dance is the same. Here's how a consultancy turns build-package-ship-handoff into one workflow that travels from client to client.

Apr 2, 2026 OrchStep Team 7 minROLE: ConsultantSCALE: Agency

If you run delivery for an agency, you live in other people's repos. One client is on Kubernetes, one is on a single VPS, one still deploys by SSHing into a box named prod-final-2. The work is different every time. The shape of the work never changes: scaffold the project, build it, package it, ship it to the right place, and leave a handoff the client's own team can run after you're gone.

That last part — "after you're gone" — is the whole problem. A bash script you wrote on a Tuesday is institutional knowledge that walks out the door with the engagement. OrchStep gives you a single readable workflow file you can drop into any client repo, run yourself during the build, and hand off as the deliverable.

What automation looks like for an agency

You are not automating one pipeline. You are automating a pattern across a dozen engagements, each with its own quirks, none of which justify standing up a CI platform you'd then have to maintain for the client. What you want is:

  • One file per repo that anyone can read in 60 seconds.
  • No daemon, no account, no platform lock-in to explain in the SOW.
  • The same commands on your laptop and in whatever CI the client already has.
  • A --dry-run you can screen-share on a handoff call to prove what ships.

Where OrchStep fits

OrchStep is a single binary that runs shell steps with structure around them — ordered tasks, named variables, retries as syntax, rollback as a catch: block. Your docker and kubectl and rsync commands still do the real work. OrchStep is the part you used to keep in your head.

Here's a delivery workflow that works for one client and travels to the next by changing two defaults:

orchstep.yml
name: client-delivery
defaults:
  client: acme
  target: staging
  registry: ghcr.io/studio

tasks:
  # orchstep run scaffold --var client=globex
  scaffold:
    steps:
      - name: workspace
        func: shell
        do: echo "creating workspace for {{ vars.client }}"
        outputs:
          path: "clients/{{ vars.client }}"
      - name: report
        func: shell
        do: echo "scaffolded {{ steps.workspace.path }}"

  # orchstep run deliver --var client=globex --var target=production
  deliver:
    steps:
      - name: build
        task: scaffold
      - name: package
        func: shell
        do: echo "packaging {{ vars.registry }}/{{ vars.client }}:release"
        retry:
          max_attempts: 3
          interval: "1s"
          backoff_rate: 2.0
      - name: ship
        func: shell
        do: echo "delivering {{ vars.client }} to {{ vars.target }}"
        catch:
          - name: rollback
            func: shell
            do: echo "rolling back {{ vars.client }} on {{ vars.target }}"
        finally:
          - name: handoff
            func: shell
            do: echo "handoff note written for {{ vars.client }}"
      - name: gate
        if: '{{ eq vars.target "production" }}'
        then:
          - name: signoff
            func: shell
            do: echo "client signoff required for {{ vars.client }}"
        else:
          - name: preview
            func: shell
            do: echo "staging preview ready for {{ vars.client }}"

The client and registry are variables, so the same file serves every engagement. The production branch demands a signoff step; staging just leaves a preview. Nothing here is client-specific code — it's the delivery shape, and it's the same shape every time.

Three workflows worth stealing

1. The new-client scaffold. orchstep run scaffold --var client=globex stands up the workspace, names the artifacts consistently, and emits an output that later steps reuse by name (steps.workspace.path). New engagement, two minutes, same conventions as the last one. See Variables & Outputs.

2. The per-environment deliver. One deliver task, two destinations. The if: gate on the production target means staging and prod are the same workflow with different guardrails — no second script to drift. For clients with genuinely separate config (different registries, regions, hostnames), promote target to a named environment and select it with --env. See Environments.

3. The handoff dry-run. On the last call of the engagement, run orchstep run deliver --var client=globex --dry-run. It resolves every variable and prints the exact plan without executing. The client's team watches the deploy on screen, then owns a file they can read — not a 200-line script with a # do not touch comment. See Previewing with Dry Run.

What you gained

ConcernPer-client bash scriptsOrchStep
Reuse across clientscopy-paste, then divergeone file, change defaults
Retry on flaky registryhand-rolled loopretry: { max_attempts: 3, backoff_rate: 2.0 }
Rollback|| after the commandcatch: block
Staging vs prodtwo scriptsone if: gate, or --env
The deliverabletribal knowledgea readable workflow file
Handoff proof"trust me"--dry-run on the call

OrchStep is not a CI platform and won't replace the client's Jenkins or GitHub Actions — it runs inside whatever they have, identically to how it runs on your laptop. If a client's needs are fully covered by make, use make. The moment you're maintaining the same delivery dance across more than two repos, a portable workflow file pays for itself.

Where to go next

One delivery dance, every engagement? Drop a single orchstep.yml into your next client repo and hand it over as the deliverable.

#AGENCY#CONSULTING#DELIVERY#HANDOFF
Try it in two minutes — one binary, no signup.
curl -fsSL https://orchstep.dev/install.sh | sh

RELATED — BY COMPANY SIZE