Render Tutorials
Render CLI for power users

Output formats and non-interactive mode

⏱ 8 min

The CLI guesses what you want based on how it was invoked. That’s the right default for humans and a sharp edge for scripts. This step teaches you to never get cut.

The auto-detect rules

When you run render services in your terminal, you get a pretty paginated picker. When the same command runs in CI, you get tabular text. That’s not magic - it’s a single rule:

flowchart TD
  start["render <command>"]
  flag{"--output flag set?"}
  envvar{"RENDER_OUTPUT env set?"}
  tty{"stdout is a TTY?"}
  i["Output: interactive picker"]
  t["Output: text (tab-separated)"]
  flag -->|"yes"| useflag["Use that format"]
  flag -->|"no"| envvar
  envvar -->|"yes"| useenv["Use that format"]
  envvar -->|"no"| tty
  tty -->|"yes"| i
  tty -->|"no"| t

  start --> flag

In English: a flag beats an env var beats auto-detect. If nothing is set, the CLI checks whether stdout is a TTY and picks interactive (TTY) or text (pipe/CI).

That’s why piping accidentally “breaks” interactive mode - once stdout isn’t a TTY, the picker bails out.

The four output formats

FormatBest forCaveats
interactiveHumans, browsingRequires a TTY; won’t work in pipes or CI
jsonScripts, jq, anything programmaticThe format you’ll use most as a power user
yamlEyeballing complex objectsSlower to parse, fine for one-off inspection
textQuick grep, awk-style pipelinesField positions can shift between releases - don’t over-trust it

Pick json for anything you’d want to script against. It’s stable, parseable, and jq makes the rest easy.

Set the format three ways

Per command (-o / --output)

Terminal
render services -o json

Highest precedence. Use it when you’re mixing interactive and scripted use in the same session.

Per shell session (RENDER_OUTPUT)

Terminal
export RENDER_OUTPUT=json
render services
render deploys list srv-xxx

Set it once, every subsequent command emits JSON. Great for scripting sessions where you don’t want to repeat -o json on every line.

Per config file (the output field)

~/.render/cli.yaml
output: interactive

Lowest precedence; the default you fall back to when nothing else is set. Most people leave this on interactive.

--confirm and why CI needs it

Several mutating commands prompt you to confirm before running. Examples: render deploys create, render psql connecting to production. The prompt is a safety net interactively, and a hang in CI.

Terminal
render deploys create srv-xxx --wait --confirm -o json

Anything that mutates state, when scripted, gets --confirm. Read-only commands (services, logs, deploys list) don’t need it.

See the difference: interactive vs scripted

The same command, two ways:

Terminal
$render services
[picker UI: arrow keys to navigate, enter to select, q to quit] Name Type Plan URL > api web starter https://api.acme.onrender.com worker worker starter cache kv starter app-db psql basic-1gb

The picker filters as you type, lets you scroll, and - if you press Enter - takes you deeper into a service’s deploys, logs, env vars, and so on.

Terminal
$render services -o json --confirm | jq '.[] | {name, id, type}'
{ "name": "api", "id": "srv-xxxxxxxxxxxxxxxx", "type": "web_service" } { "name": "worker", "id": "srv-yyyyyyyyyyyyyyyy", "type": "background_worker" }

The same data, but as JSON your script can iterate, filter, and pipe.

A test you can run right now

Run this twice and look at the difference:

Terminal
render services # interactive picker
render services | head # text format (because stdout is a pipe)
render services -o json | jq '.[0]' # JSON (explicit flag)
RENDER_OUTPUT=json render services # JSON (env var)

If render services -o json | jq '.[0]' doesn’t print a JSON object, the most common cause is jq parsing the text format - make sure -o json is on the render call, not on jq.

Practical scripting template

Whenever you’re writing a script that calls render, start from this template:

render-script-template.sh
#!/usr/bin/env bash
set -euo pipefail
export RENDER_API_KEY="${RENDER_API_KEY:?must be set}"
export RENDER_OUTPUT=json
CONFIRM=( --confirm )
services=$(render services)
echo "$services" | jq -r '.[] | "\(.name)\t\(.id)"'
LineWhy
set -euo pipefailBash fails on error, undefined vars, and broken pipes - the right default for any operations script
${RENDER_API_KEY:?must be set}Fails fast if the key isn’t set, with a clear message
export RENDER_OUTPUT=jsonAll subsequent render calls emit JSON - no need to repeat -o json
CONFIRM=( --confirm )Lets you splat "${CONFIRM[@]}" into any mutating command, keeping it explicit and greppable

You’ll see this template again in step 10.

Common gotchas

SymptomRoot causeFix
CI job hangs foreverMissing --confirm on a mutating commandAdd --confirm
Script gets text output, not JSON--output flag missing, env var missingSet RENDER_OUTPUT=json at the top
jq: error (at <stdin>:1): Cannot iterate over nullrender printed an empty list as text, then jq chokedConfirm -o json is set, and check whether the resource actually exists
Pretty-printed output in CI logsSome commands default to text, which still includes ANSI color codesAdd NO_COLOR=1 to your CI env, or use -o json
A coworker writes a CI script that calls `render deploys create srv-xxx --wait` and the job hangs for 30 minutes before timing out. The logs show nothing after the deploy command. What's the most likely fix?

What you learned

  • Flag > env var > config file > TTY auto-detect - four levels of precedence, two of which you control per-command
  • `-o json` is the right default for anything scripted; `text` is for quick `grep` and `jq` is the right friend for `json`
  • `--confirm` is the magic word that prevents CI hangs on mutating commands
  • The scripting template (`set -euo pipefail`, `RENDER_OUTPUT=json`, explicit `--confirm` array) gets reused throughout this course