Capabilities overview

Learning maand? Start with the guided tour. Use this page as a feature checklist once you know the basics.

Maand is an agentless workload orchestrator for Linux clusters — more than a simple deploy script, but narrower in scope than Kubernetes or Nomad.

A CLI host (laptop, CI runner, or bastion) holds a local bucket: SQLite catalog, encrypted KV, secrets, and staged files. Workers are ordinary Linux hosts reached over SSH and rsync. Nothing maand-specific is installed on workers except what you deploy (runner.py, job files, Makefiles).

That design fits small-to-medium fleets where you want declarative jobs, rolling deploys, and ops hooks without running a separate control-plane cluster.

Related: README.md (guided tour) · concepts.md · commands.md · configuration.md · quickstart.md


Core capabilities

Area What maand can do
Catalog & placement Declare workers (workers.json) with labels, tags, CPU/memory; declare jobs with selectors (label matching); auto-create allocations (job × worker). Three-layer resource model: manifest min/max, bucket.jobs*.conf reservations, worker capacity validation — see resources-and-placement.md
Environment overrides job_config_selector in maand.conf picks bucket.jobs.<env>.conf for per-environment memory/CPU without changing manifests — see configuration.md
Build Reconcile workspace → maand.db + KV; validate resources, ports, job dependencies; generate and auto-rotate TLS certs; sync disabled.json; run post_build hooks in deployment_seq order — see build.md, certs.md
Deploy Rsync to workers; Makefile start / restart / reload (or job_control); restart_policy, restart_globs, --sync-only; hash skip and partial resume; --dry-run, --force, --jobs; waves via deployment_seqdeploy.md
Rolling upgrades max_concurrent_starts / max_concurrent_upgrades, health gates, rollout_order; semver version tracking — guides/rolling-deploy.md
Dependencies Cross-job command demands with version constraints; build computes deployment_seq and detects circular demands — see deployment-sequence.md
Hooks & runtime API Python/Bun hooks on the CLI host: post_build, pre_deploy, post_deploy, job_control, health_check, after_allocation_started, after_allocation_stopped, clireference/cli/hooks.md
Health Built-in TCP/HTTP/SSH manifest probes and/or custom health_check commands (probes first); --wait — see health-check.md
Prometheus Per-job optional _prometheus/ (scrape, alerts, runbooks, dashboards); build validates and stores in job_files; scrape configs go to KV — see prometheus.md
Maintenance (disable) disabled.json drains workers, jobs, or single allocations without removing workspace or catalog rows. Disabled allocations still get build/KV/certs and deploy staging; maand never starts them — see disable and drain
Day-2 ops maand job start|stop|restart|run|status; maand collect facts for worker capacity; maand run_command for ad-hoc SSH; soft-remove via workspace edits + GC for purged rows and disk — see job.md, collect.md, gc.md, day-2-ops.md
State & secrets Layered KV: global (vars/bucket), worker, job config (vars.confvars/bucket/job/<job>), hooks (vars/job/<job>), allocation (certs, peers, version). Encrypted secrets/job/*. Inspect with maand cat — see KV persistence, KV namespaces
TLS Bucket CA at init; per-job certs declared in manifest; build-time renewal via certs_ttl / certs_renewal_buffer; staged to workers on deploy — see certs.md
Templates Go templates (.tpl) rendered at deploy with KV context (get, getSecret) — see templates.md

Typical flow:

edit workspace → maand build → maand deploy → health_check / job ops → gc

See README.md for the command sequence.


Capability map (by concern)

You need… Maand provides… Doc
Place jobs on labeled workers Selectors + allocation auto-match concepts.md, resources-and-placement.md
Different CPU/memory per env bucket.jobs.prod.conf + job_config_selector configuration.md
Ordered multi-job deploy Command demandsdeployment_seq waves deployment-sequence.md
Rolling restart without downtime max_concurrent_upgrades + health between batches rolling-deploy
Push config without restarting the process restart_policy: reload (+ optional restart_globs) deploy.md
Skip redeploy when nothing changed Content hashes + version promotion per allocation deploy.md, maand cat deployments
Bootstrap secrets before templates pre_deploy + put_job_secret / runtime API hook-api.md, KV namespaces
Custom canary or blue/green job_control command + env NEW_ALLOCATIONS hook-api.md, rolling-deploy
Single-writer migration across nodes Runtime semaphore (capacity=1) hook-api.md
Drain one node for maintenance disabled.json → build → deploy (stops, no restart) disable and drain
mTLS or app TLS on workers Manifest certs + auto-rotation on build certs.md
Inspect TLS expiry maand cat certs certs.md
Debug “why didn’t deploy run?” --dry-run, maand cat deployments, debugging-deploy.md debugging-deploy.md
One-off operator script maand hooks (event cli) hook-api.md

Where maand is strong

  1. No agents — workers only need SSH, make, python3, rsync, bash. Good fit when you do not want Nomad/K8s overhead.
  2. Declarative + incremental deploy — content hashes and version tracking mean redeploys skip promoted allocations and resume after partial failure. Dry-run and maand cat deployments make rollout state visible. See deploy.md and debugging-deploy.md.
  3. Multi-job orchestration — dependency graph, ordered deploy waves, rolling restarts with health gates between batches. See deployment-sequence.md and rolling-deploy.
  4. Extensibility without a custom agent — hooks, runtime HTTP API (KV, demands, semaphores), and layered configuration support migrations, secret bootstrap, custom lifecycle, and CLI-triggered ops on the orchestrator host. See hook-api.md and KV namespaces.
  5. Operational tooling built in — maintenance disable (not delete), GC, dry-run, hash inspection, cert renewal on build, resource validation, and structured deploy debugging.
  6. Environment portability — job manifests stay in git; bucket-level TOML and job_config_selector switch prod/staging reservations without forking job definitions. See resources-and-placement.md.

Intentional limits

Maand is a focused orchestrator, not a general container or platform scheduler:

Limit Detail
Single control point One bucket directory on one CLI host is the source of truth (not HA etcd/consul).
SSH-centric All worker interaction is SSH/rsync; no native service mesh, CNI, or container runtime.
Label-based placement Selectors + resource validation — not bin-packing, affinity rules, or dynamic scheduling beyond labels.
Makefile lifecycle Default deploy uses start/stop/restart/reload targets; you bring process supervision (systemd, containers, etc.) via Makefile or job_control commands.
Hook runtimes Python3 and Bun on the CLI host; scripts reach workers via SSH helpers (Python) or your own wiring. Workers run what you deploy.
In-process coordination Runtime API and semaphores exist only for the current maand CLI session — not a distributed lock service.
Linux workers Docs assume Linux hosts with standard Unix tooling.

Summary

Maand can run a multi-node cluster of stateful or stateless services with:

It is best understood as deploy orchestration plus a small job catalog rather than a full platform like Kubernetes. For teams that want dependency-aware rolling deploys, drain/disable, hook-driven ops, and inspectable rollout state over a fixed set of SSH workers — without agents or a cluster control plane — maand is designed to cover that niche end to end.


Further reading