applad up twice produces the same result as running it once. Running it against an already-reconciled environment is a no-op that produces a clean recap confirming nothing changed.
This is guaranteed — not incidental, not aspirational, not “usually true.”
Why Idempotency Matters
Idempotency is not just a nice-to-have property. It’s essential for several critical use cases:CI/CD Pipelines
Pipelines that runapplad up on every push need to know that a no-change run won’t cause:
- Unnecessary service restarts
- Downtime
- Spurious audit entries
- False positive alerts
.github/workflows/deploy.yml
Recovery Scenarios
When you need to re-runapplad up after a partial failure, you need confidence that:
- Migrations that already ran won’t run again
- Services that are healthy won’t restart
- Resources that are correctly configured won’t be touched
Team Environments
When multiple developers might runapplad up in quick succession (or simultaneously), the system needs to handle it gracefully:
- No race conditions
- No duplicate operations
- No conflicting state changes
Idempotency Strategies
Each resource type in Applad has an explicit idempotency strategy:Containers
Containers
Compared by image digest and environment hash. Only restarted if either has changed.
Migrations
Migrations
Tracked by checksum in the migration history table. Never re-applied.Before running a migration, Applad checks if a row exists with matching name and checksum. If yes, skip. If no, run and record.
Config
Config
Compared against the last-applied config signature. No-op if unchanged.Applad stores a hash of the applied configuration for each resource. On reconciliation:
- Compute hash of desired config
- Compare to stored hash
- If identical, skip
- If different, apply and update stored hash
Cloud Resources
Cloud Resources
Checked for existence before provisioning. Existing resources with matching config are left untouched.
Caddy Config
Caddy Config
Generated deterministically from deployment config. Reloaded only if the output differs from running config.Applad synthesizes Caddy configuration from
deployments/*.yaml. Before reloading:- Generate Caddyfile from current config
- Compare to running Caddyfile
- If identical, skip reload
- If different, reload Caddy (graceful, zero-downtime)
SSH Sessions
SSH Sessions
Opened only if work needs to be done. A fully reconciled environment opens no SSH connections.Duration 0.8s — no SSH connection was made. Config was compared locally, everything matched, done.
The Handler Pattern
Applad uses a handler pattern for service restarts, modeled on Ansible’s handler system.When multiple config changes would each individually trigger a service restart, Applad batches them and restarts the service exactly once at the end of the reconciliation run.
Without Handlers
With Handlers
- Service restarts — only if one or more dependent configs changed
- Migration runs — all pending migrations for a connection run in a single transaction
- Config reloads — Caddy config reloaded once after all web deployment config changes are applied
- Post-deploy health checks — run once after all functions in a batch are deployed
.env.exampleregeneration — regenerated once after all config changes in a run
applad up is both correct and efficient. It does exactly the work that needs doing, in the right order, and no more.Drift Detection
Drift detection is the companion to idempotency.applad status --drift connects to every configured environment, compares the running state against the config tree, and reports what has drifted — without changing anything.
Predictability Contract
Idempotency is part of a larger predictability contract that Applad makes:--dry-run tells you exactly what will change
Every service that would start or restart. Every config change. Every migration that would run. Every cloud resource that would be provisioned. Every SSH connection that would open.
applad up changes exactly that and nothing else
If
--dry-run showed it, it happens. If --dry-run didn’t show it, it doesn’t happen.The recap tells you what happened
A clean summary of what was already correct (
ok), what changed (changed), and what failed (failed).Comparison to Other Tools
Ansible
- Claim: “Ansible is idempotent”
- Reality: Modules are usually idempotent, but there are edge cases. Complex playbooks with conditionals and loops can have subtle non-idempotent behavior.
- Applad: Idempotency is guaranteed by design at the reconciliation layer, not left to individual resource implementations.
Terraform
- Strength: Terraform is very good at idempotency for cloud resources.
- Weakness: Terraform’s state file can drift from reality. Running
terraform applyafter manual changes can produce unexpected results. - Applad: No separate state file. Applad compares desired state (from config) directly against running state (from infrastructure) on every reconciliation.
Docker Compose
- Behavior:
docker compose upis mostly idempotent, but:- Recreates containers even if nothing changed (depending on flags)
- No built-in drift detection
- No concept of “no-op” reconciliation
- Applad: Uses Docker Compose as the runtime, but adds an idempotency layer on top that prevents unnecessary recreations.
Testing Idempotency
You can verify Applad’s idempotency guarantee:Running it twice produces the same result as running it once. This is the idempotency contract in action.
Idempotency in CI/CD
A common CI/CD pattern:.github/workflows/deploy.yml
main. Because Applad is idempotent:
- Pushes with only README changes → no-op, no restarts
- Pushes with config changes → only affected services restart
- Multiple concurrent runs → safe (last one wins, intermediate states are valid)
Exit Codes for Automation
Applad’s exit codes help automation differentiate between types of success:| Code | Meaning | Use Case |
|---|---|---|
0 | Success — no changes needed | Skip downstream notifications |
2 | Success with changes | Trigger Slack notification, run smoke tests |
1 | Error — command failed | Alert on-call, roll back |
3 | Validation failure | Fix config, try again |
5 | Drift detected | Investigate manual changes |
Next Steps
Architecture
Learn about Applad’s overall system architecture
Agentless Operation
Understand how Applad connects via SSH without persistent agents