8. Deploy
maand deploy pushes job trees to workers and runs lifecycle Makefile targets.
maand deploy
maand deploy --jobs api
maand deploy --dry-run
maand deploy --force --jobs api
Prerequisites: successful maand build, SSH access, host tools (ssh, rsync, python3).
Deploy phases (simplified)
Once per deploy (before any deployment_seq wave):
reconcile → stop removed/disabled allocations
after_allocation_stopped hooks
cleanup (removed job files; purge KV when job fully removed)
health_check → jobs that stopped a running allocation and still have survivors
For each deployment_seq wave (dependency order):
For each job in the wave:
pre_deploy → hook (CLI host; runs during plan-hash refresh, before skip check)
stage → tmp/workers/<ip>/jobs/<job>/ (plan hash / skip detection)
When at least one job in the wave still needs work:
update_seq (+1 once per deploy, before first rsync in this run)
prepare worker.json / jobs.json / bin/
For each job that needs rollout or post_deploy retry:
stage → tmp/workers/<ip>/jobs/<job>/ (again, with rendered .tpl + certs)
rsync → /opt/worker/<bucket_id>/
plan hash → update current_hash from staged tree
rollout → batched make start | restart | reload
after_allocation_started → promote batch → health_check
post_deploy → hook (CLI host; job-wide wave)
promote → idempotent sweep (batches already promoted during rollout)
After all waves:
final rsync → per successfully deployed job (refresh jobs.json on workers)
commit → partial deploy allowed (failed jobs retry on next run)
Rolling happens within rollout using max_concurrent_starts / max_concurrent_upgrades. Promote runs per batch during rollout, not only after post_deploy. post_deploy runs after rollout completes (or alone when retrying a failed post_deploy).
Reference: deploy.md · deployment-sequence.md.
First deploy vs upgrade
| Allocation state | After rsync | Lifecycle |
|---|---|---|
| New (no promoted hash) | Files on worker | make start |
| Existing, content or version changed | Updated files | make restart or make reload per policy |
| Unchanged hash + version | — | skip (unless --force) |
Inspect skip vs rollout:
maand deploy --dry-run
maand cat deployments --jobs api
restart_policy
Set in manifest.json:
| Policy | On upgrade |
|---|---|
always (default) |
make restart |
reload |
make reload, unless a changed file matches restart_globs → then restart |
never |
rsync only; no lifecycle |
Config-only push without lifecycle for one deploy:
maand deploy --sync-only --jobs api
Then manually: maand job run api --target reload if needed.
Reference: deploy.md.
Rolling batches
| Manifest field | Controls |
|---|---|
max_concurrent_starts |
Batch size for start on first rollout |
max_concurrent_upgrades |
Batch size for restart / reload on upgrades |
Within each batch, order comes from rollout_order (KV key, overridable in pre_deploy).
Guide: rolling-deploy.md.
Deploy hooks (preview)
| Event | Runs on | Typical use |
|---|---|---|
pre_deploy |
CLI host, per allocation | Secrets, put_rollout_order, migrations prep |
post_deploy |
CLI host, per allocation | Notify, KV updates |
after_allocation_started |
CLI host | Post-start validation |
job_control |
CLI host | Replace default start/restart with custom script |
Details: 10-hooks.md.
Version tracking
Each allocation tracks:
current_version— running (inhashtable after promote)new_version— target from manifest (inallocationsrow)
A version bump can trigger rollout even when file hash is unchanged. Makefile gets CURRENT_VERSION and NEW_VERSION.
When deploy goes wrong
maand deploy --dry-run
maand cat deployments
Guide: debugging-deploy.md.