Skip to content

Command line

sadame has nine commands: plan, apply and drift run a recipe, validate checks one without any machine, hosts shows which machines a selection reaches and facts what recipes see on them, init starts a project, report turns JSON reports into Markdown or JUnit, and lsp serves editors. Each takes the same target and output options, so a command line that plans a change also applies it after replacing plan with apply. This page lists every command, option, environment variable and file; the exit codes have their own page.

Minimal example

sadame plan examples/01-hello     # on this machine: what would change
sadame apply examples/01-hello    # make the change

sadame --help prints the same reference in short:

$ sadame --help
Usage: sadame COMMAND [RECIPE] [options]
       sadame init [DIRECTORY] [--ci github|gitlab]
       sadame report FILE [--format markdown|junit]
       sadame lsp

Commands:
  init     Start a project: recipe.rb, vars.rb, hosts.rb, a template, and
           with --ci a daily drift check for GitHub Actions or GitLab CI.
  plan     Show what apply would change, as a diff. Changes nothing.
  apply    Make the hosts match the recipe.
  drift    Like plan, but exit 1 when a host differs from the recipe.
  validate Check the recipe for every selected host without connecting.
  hosts    Show the selected hosts with their connection options and vars.
  facts    Show the facts recipes see on each host: CPUs, memory, addresses.
  report   Turn the --json output of plan, apply or drift into Markdown
           (for a CI summary page) or JUnit XML (for test reports).
  lsp      Run a language server for editors, on stdin and stdout.

RECIPE is a recipe file or a directory containing recipe.rb
(default: ./recipe.rb). vars.rb and hosts.rb next to it are loaded if
present; hosts and facts only use those. Without -h, -g or --limit,
sadame runs on this machine.

Targets:
  -h, --host [USER@]HOST[:PORT],...   Run on these hosts over SSH
  -g, --group GROUP,...               Run on the hosts of these groups
  --limit GLOB                        Only hosts whose name matches, e.g. "web*"
  --inventory PATH                    Use this inventory instead of ./hosts.rb
  --sudo                              Run sadame with sudo on every target
  --forks N                           Work on at most N hosts at once (default: all)
  --serial N|N%                       Run in batches of N hosts (or N% of them), one
                                      batch after another; stop when a batch fails

Options:
  --platform NAME     validate as on debian, ubuntu, rocky, alpine or arch
                      (default: debian)
  --tags T,...        Only resources with one of these tags (plan, apply, drift)
  --skip-tags T,...   Leave out resources with one of these tags
  --var KEY=VALUE     Set vars[:KEY]; repeatable, overrides everything else
  --vars-file PATH    Load more vars from a Ruby file (local runs)
  --json              Print a JSON report instead of text
  --format FORMAT     report: markdown (default) or junit
  --ci SYSTEM         init: also write a drift check for github or gitlab
  --color             Always use colors
  --no-color          Never use colors (also: NO_COLOR environment variable)
  --host-name NAME    host_name in recipes and reports for local runs (default: local)
  --host-groups G,... host_groups in recipes for local runs
  --version           Print the version
  --help              Print this help

Exit status:
  0  success; for drift, every host matches the recipe
  1  apply failed, or drift found differences
  2  usage or recipe error, the state of a resource could not be read,
     or a host could not be reached

Synopsis

sadame COMMAND [RECIPE] [options]

Options may come before or after the command and the recipe.

Commands

Command Changes machines What it does
plan no Reads the current state of every resource and prints what apply would change, as a diff.
apply yes Changes what differs from the recipe, in order, then runs notifications.
drift no Same comparison as plan; exits 1 when anything differs. For scripts and CI.
validate no Evaluates the recipe for each selected host, with its vars, against a stand-in machine, and renders the templates: finds what would make plan fail, without connecting to anything. Exits 2 when a host's recipe has an error.
hosts no Prints the selected hosts, their connection options and vars, and where each var came from. Connects to nothing.
facts no Prints the facts recipes see: of this machine, or with -h, -g or --limit, of each selected host over SSH. Runs no recipe.
init no Writes recipe.rb, vars.rb, hosts.rb and templates/motd.erb into a directory (. by default); with --ci, also a daily drift check. Writes nothing if one of its files exists.
report no Reads the --json output of plan, apply or drift from a file and prints it as Markdown or JUnit XML; see Drift in CI.
lsp no A language server on stdin and stdout, which editors start; see Editor support.

plan, apply and drift are explained together in plan, apply, drift.

The recipe argument

Given sadame runs
nothing ./recipe.rb
a file, such as site.rb that file
a directory, such as examples/01-hello recipe.rb in that directory

vars.rb and hosts.rb in the recipe's directory are loaded when they exist. Templates and files are looked up next to the recipe, then in its templates/ and files/ directories. hosts and facts do not need a recipe; given one, or a directory, they use the hosts.rb and vars.rb next to it.

Targets

Without any of these options, sadame runs on the machine it is started on. With any of them, it runs on the selected hosts over SSH, copying itself and the recipe there first (see Remote hosts).

Option Value Effect
-h, --host [USER@]HOST[:PORT], comma-separated These hosts. A name from the inventory keeps its settings; the user and port given here win.
-g, --group group names, comma-separated The hosts of these groups in the inventory.
--limit glob, such as "web*" Only the selected hosts whose name matches (* any characters, ? one). Alone, it filters every host of the inventory.
--inventory path Use this inventory instead of hosts.rb next to the recipe.
--sudo Run sadame with sudo on every target, whatever the inventory says. Needs passwordless sudo. Only with remote targets.
--forks number Work on at most this many hosts at once (default: every selected host). Only with remote targets.
--serial number, or percentage such as 25% Run the hosts in batches of this size, one batch after another, in the order they were selected. When a host of a batch fails or cannot be reached, the hosts of later batches are skipped. Only with remote targets.

-h and -g add up: -g db -h web1.example.com selects both. --limit narrows the result. Selection rules and vars precedence are in Inventory.

Options

Option Effect
--tags T,... plan, apply, drift: only resources with one of these tags.
--skip-tags T,... plan, apply, drift: leave out resources with one of these tags.
--platform NAME validate: evaluate as on debian (the default), ubuntu, rocky, alpine or arch.
--format FORMAT report: markdown (the default) or junit.
--ci SYSTEM init: also write a daily drift check for github (.github/workflows/sadame-drift.yml) or gitlab (.gitlab/sadame-drift.yml), in the current directory.
--var KEY=VALUE Sets vars[:KEY]. Repeatable. Wins over every other source of vars. Whole numbers become Integers, true and false booleans; everything else stays a String.
--vars-file PATH Loads more vars from a Ruby file that returns a Hash. Local runs only.
--json Prints one JSON document instead of text; see JSON output.
--color Always use colors, even when the output is not a terminal.
--no-color Never use colors.
--host-name NAME host_name in recipes and reports for local runs (default local).
--host-groups G,... host_groups in recipes for local runs.
--version Prints the version.
--help Prints the help above.
$ sadame --version
sadame 0.5.1

validate

validate evaluates the recipe the way plan does, but against a machine that is not there: the platform given with --platform, made-up facts (2 CPUs, 2048 MB, 192.0.2.10), and a shell that refuses to run anything. It finds Ruby errors, misspelled resources and options, notifications to resources that are not declared, missing templates and files, and template errors, for every selected host with its own name, groups and vars:

$ sadame validate examples/08-multi-role-inventory/site.rb -g all --platform ubuntu
sadame 0.5.1 validate examples/08-multi-role-inventory/site.rb, as on Ubuntu 24.04 LTS

  web1.example.com  ok     6 resources
  web2.example.com  ok     6 resources
  db1.example.com   ok     6 resources

validate on 3 hosts: 3 ok.

An error names the file and line:

$ sadame validate
sadame 0.5.1 validate recipe.rb, as on Debian GNU/Linux 12 (bookworm)

  local  error  templates/app.conf.erb:3: undefined method 'port' for Sadame::Template::Scope

validate on 1 host: 0 ok, 1 error.

$ echo $?
2

What it cannot find is what depends on the machine: a package name that does not exist there, a service that is not installed, a file a line resource expects. plan finds those.

Environment variables

Variable Effect
NO_COLOR When set and not empty, no colors, as with --no-color.
SADAME_BINARY_DIR Push mode: copy sadame-OS-ARCH from this directory to targets, instead of the running binary or a download. Used with binaries built from source.
SADAME_DOWNLOAD_URL Push mode: download target binaries and SHA256SUMS from this URL instead of the GitHub release of the running version.
TMPDIR Where sadame keeps the error output of the commands it runs while they run (default /tmp).

Every command sadame runs, on the machine itself or on a target, runs with LC_ALL=C.UTF-8, so its output is in English whatever the system's language (see exec).

Files

Path On What
recipe.rb, vars.rb, hosts.rb your machine The recipe, its default vars, the inventory.
templates/, files/ your machine Templates and files next to the recipe.
~/.sadame/dist/VERSION/ your machine Target binaries downloaded for push mode.
~/.sadame/bin/SHA256/sadame each target The copied binary, kept between runs and reused while its checksum matches.

Nothing else is left on a target; the copied recipe is removed after each run. Details in What is copied where.

Scenarios

Preview a var change on one group. --var wins over hosts.rb, so a change can be tried without editing anything:

sadame plan site.rb -g db --var backup_hour=4

Roll out to part of a group first. Apply to the first web server, look at it, then apply to the rest:

sadame apply site.rb -g web --limit "web1*"
sadame apply site.rb -g web

Roll out one server at a time. With --serial 1, a change that breaks the first server stops before it reaches the second; --serial 25% keeps three quarters of the group untouched while the first quarter changes:

sadame apply site.rb -g web --serial 1

Check one machine that is not in the inventory. -h takes a full address, so a one-off check needs no hosts.rb:

sadame drift examples/07-hardening-baseline -h admin@203.0.113.10:2222 --sudo

Common mistakes

  • -h for help. -h is --host and needs a value; use --help.
  • Misspelled options. sadame stops with exit status 2 instead of guessing:

    $ sadame plan examples/01-hello --colour
    sadame: unknown option --colour
    Run 'sadame --help' for usage.
    
    $ echo $?
    2
    
  • --sudo on a local run. It applies to remote targets only; run sudo sadame ... on the machine itself.

  • Expecting a list from --var. --var workers=8 gives the Integer 8, but --var ports=80,443 gives the String "80,443", which the recipe has to split.

See also