π Dry-Run Mode
π Dry-Run Mode
Dry-run mode lets you preview exactly what Itamae would change on your system without actually applying any modifications. Itβs your safety net before every production run.
π Quick Start
# Long flag
itamae local --dry-run recipe.rb
# Short flag (-n)
itamae ssh -n -h web01.example.com recipe.rb
# Works with all backends
itamae docker -n --image ubuntu:22.04 recipe.rb
The startup banner confirms youβre in dry-run mode:
INFO : Starting Itamae... (dry-run)
βοΈ How It Works
Dry-run executes the full resource lifecycle β except the actual system mutation. Hereβs exactly what happens at each step:
| Step | Normal Mode | π Dry-Run Mode |
|---|---|---|
| 1οΈβ£ Initialize resource | β Runs | β Runs |
2οΈβ£ Evaluate guards (only_if/not_if) |
β Runs | β Runs |
3οΈβ£ pre_action (gather state) |
β Runs | β Runs |
4οΈβ£ set_current_attributes (query target) |
β Runs | β Runs |
5οΈβ£ show_differences (display diffs) |
β Runs | β Runs |
6οΈβ£ action_* method (apply changes) |
β Runs | β Skipped |
7οΈβ£ verify commands |
β Runs | β Skipped |
| 8οΈβ£ Detect differences | β Runs | β Runs |
| 9οΈβ£ Fire notifications | β Runs | β Runs* |
π‘ Key insight: Notifications do fire in dry-run mode (so you see the full chain), but the notified resourceβs action is also skipped by the same dry-run guard.
π What You See in Dry-Run Output
Dry-run output is identical to a normal run β you see every attribute change and file diff β but no actual changes are applied.
Attribute Changes
INFO : Recipe: /path/to/recipe.rb
INFO : package[nginx]
INFO : installed will change from 'false' to 'true'
INFO : service[nginx]
INFO : enabled will change from 'false' to 'true'
INFO : running will change from 'false' to 'true'
File Content Diffs
For file, template, and remote_file resources, Itamae shows a unified diff of what would change:
INFO : template[/etc/nginx/nginx.conf]
INFO : diff:
--- /etc/nginx/nginx.conf
+++ /tmp/itamae_tmp/...
@@ -1,3 +1,3 @@
-worker_processes 2;
+worker_processes 4;
events {
- worker_connections 512;
+ worker_connections 1024;
π Files marked with
sensitive truesuppress the diff output to protect secrets.
Notification Chain
INFO : template[/etc/nginx/nginx.conf]
INFO : Notifying restart to service resource 'nginx' (delayed)
π¬ Per-Resource Behavior
Different resources behave differently during dry-run because pre_action and set_current_attributes still run (they query the target to gather comparison data).
π¦ package
State is fully queried β you see exactly which packages would be installed or removed and their version changes.
β‘ execute
The execute resource always marks itself as changed (every run is considered an update). In dry-run, the command itself is skipped, but you will see:
INFO : execute[apt-get update]
INFO : executed will change from 'false' to 'true'
β οΈ Important: Guards (
only_if/not_if) on execute resources still run their commands in dry-run mode. This is necessary to determine whether the resource would execute.
π file / template / remote_file
These resources upload a temp file to the target and run diff even in dry-run mode, so you get full content diffs. The actual file is never moved into place.
For template resources, the ERB rendering happens locally before the diff comparison, so you see the rendered output.
π directory
Mode, owner, and group are queried from the existing directory. You see what permissions would change.
π link
The current symlink target is read. You see whether the link would be created or updated.
π€ user / π₯ group
Current uid, gid, home, shell are queried. You see exactly which attributes would be updated.
π service
Running and enabled states are queried. You see which services would start, stop, enable, or disable.
π http_request
β οΈ Note: The HTTP request is actually made during
pre_action(to fetch the response body for diff comparison). The downloaded content is compared but not written to the target path. Be aware of this side effect when dry-running recipes withhttp_request.
π remote_directory
The source directory is uploaded to a temp path on the target for comparison. A recursive diff -u -r shows what would change. The actual directory is not moved into place.
π git
The repository state is not deeply checked during dry-run. The destination directory existence is verified.
π gem_package
The gem list -l command runs to check installed gems. You see which gems would be installed, upgraded, or removed.
π» local_ruby_block
β οΈ Important: The Ruby block is not executed in dry-run mode (itβs inside the
action_runmethod, which is skipped). Thereβs no way to preview what alocal_ruby_blockwould do.
π Combining with --detailed_exitcode
Use both flags together for CI/CD pipelines to detect whether changes would be made:
itamae local --dry-run --detailed_exitcode recipe.rb
echo $?
# 0 = no changes needed
# 2 = changes would be applied
# 1 = error occurred
CI/CD Pattern
#!/bin/bash
set -e
# Preview first
itamae ssh --dry-run --detailed_exitcode \
-h "$HOST" -j "nodes/${HOST}.json" roles/web.rb
case $? in
0) echo "β
No changes needed" ;;
2) echo "β οΈ Changes detected β review above and run without --dry-run" ;;
*) echo "β Error during dry-run" ; exit 1 ;;
esac
π‘ Best Practices
β Always Dry-Run Before Production
# Step 1: Preview
itamae ssh --dry-run -h production.example.com -j nodes/prod.json recipe.rb
# Step 2: Review the output carefully
# Step 3: Apply
itamae ssh -h production.example.com -j nodes/prod.json recipe.rb
β Use with Log Levels
Combine --dry-run with --log_level debug for maximum visibility:
itamae local --dry-run --log_level debug recipe.rb
Debug mode shows:
- Every specinfra command being run
- SHA256 comparisons for file content
- Template rendering details
- Guard command output
β Test Node Attribute Changes
When modifying node JSON files, dry-run shows the impact:
# See what changing worker_processes from 2 to 4 would do
itamae ssh --dry-run -h web01 -j nodes/web01-updated.json cookbooks/nginx/default.rb
β οΈ Limitations to Be Aware Of
- Guards execute real commands β
only_ifandnot_ifcommands run on the target even in dry-run mode http_requestmakes real HTTP calls β the URL is fetched duringpre_actionexecuteresources canβt show what would happen β they always report βwill changeβlocal_ruby_blockis opaque β the block is not called, so you canβt preview its effects- State queries touch the target β
pre_actionandset_current_attributesrun real commands to gather current state (this is necessary for accurate diffs) - Dependent resources may see stale state β since earlier resources donβt actually apply changes, later resources in the same run may see pre-change state and report inaccurate diffs