BLOG/CONFIGURATION MANAGEMENT
THEME
CONFIGURATION MANAGEMENT

Feature flags from config

You don't always need a flag SaaS. Keep boolean flags in config, gate steps on them, and render a flags file your app reads at boot — flip a feature with one --var, no redeploy of the logic.

May 10, 2026 OrchStep Team 6 minROLE: Backend DeveloperSCALE: Any
RUNNABLE DEMO
Full source for this post: blog/feature-flags-config
VIEW SOURCE

Not every feature flag needs a vendor, a dashboard, and a network call on the hot path. Plenty of flags are really just deploy-time config: "is the new checkout on in staging yet?" You want to flip them per environment, gate some setup work on them, and hand the app a file it can read — without wiring up a SaaS for three booleans.

That's a config problem, and it fits neatly in a workflow.

Flags are just vars

Define the flags in defaults: so they have a known baseline, then override per run or per environment. Gating a step on a flag is a plain if:, and rendering the flags file is the render function.

orchstep.yml
name: feature-flags
defaults:
  new_checkout: "false"
  dark_mode: "true"
  beta_search: "false"
  stage: staging

tasks:
  # orchstep run flags --var new_checkout=true
  flags:
    desc: "Render the flag set and gate work on it"
    steps:
      # render produces text from a template. The output is the JSON the app loads.
      - name: render_flags
        func: render
        args:
          template: |
            {
              "new_checkout": {{ vars.new_checkout }},
              "dark_mode": {{ vars.dark_mode }},
              "beta_search": {{ vars.beta_search }}
            }
        outputs:
          json: "{{ result.output }}"

      - name: checkout_gate
        if: '{{ eq vars.new_checkout "true" }}'
        then:
          - name: on
            func: shell
            do: echo "new checkout flow ENABLED for {{ vars.stage }}"
        else:
          - name: off
            func: shell
            do: echo "new checkout flow disabled for {{ vars.stage }}"

      - name: search_gate
        if: '{{ eq vars.beta_search "true" }}'
        then:
          - name: index
            func: shell
            do: echo "warming the beta search index for {{ vars.stage }}"

      - name: write
        func: shell
        do: echo "wrote flags.json for {{ vars.stage }}"

Two things are happening. The render step turns the flag set into the JSON your app loads at boot — give it an args.output_file to also write it to disk. And the if: gates do the setup a flag implies: turning on beta_search also warms its index, so the flag and its side effects can't drift apart.

Flip a flag

orchstep run flags --var new_checkout=true

The checkout_gate takes its then: branch, the rendered JSON flips to true, and your standing config is untouched. To make a flag environment-specific instead of per-run, lift it into an environments: block and select with --env.

See which gates fire before you commit

The honest worry with flags is "wait, what does this combination actually turn on?" A dry-run answers it without running anything:

orchstep run flags --var new_checkout=true --var beta_search=true --dry-run

You see the resolved booleans and which branches are taken — the index warm-up included — before a single step executes.

What you gained

ConcernHard-coded if in appOrchStep config flags
Flip a flagedit code, redeploy--var new_checkout=true
Per-environment valueenv-var spaghettienvironments: override
Flag's side effectsscattered, easy to forgetgated in one if: block
The flags filehand-maintainedrender from one template
"What's on right now?"grep the code--dry-run shows it

Where a real flag service still wins

This pattern is for flags you flip at deploy time. If you need per-user targeting, gradual percentage rollouts, or flipping a flag live without a deploy, reach for a real flag platform — that's what they're good at. For the long tail of boolean config that ships with the release, this is less moving parts.

Where to go next

The project above is a runnable, echo-only demo — flip the flags with --var and watch the gates change.

#FEATURE-FLAGS#CONFIGURATION#RENDER#CONDITIONALS
Try it in two minutes — one binary, no signup.
curl -fsSL https://orchstep.dev/install.sh | sh

RELATED — CONFIGURATION MANAGEMENT