Configuration reference

Maand reads configuration from the bucket root and workspace/. Edit these files, then run maand build (and usually maand deploy) for changes to take effect.

Run commands from the bucket directory (the directory created by maand init).

Related: build.md · concepts.md · commands.md


Prerequisites (CLI host and workers)

Where Required tools
CLI host maand (built with CGO_ENABLED=1), bash, ssh, rsync, python3; bun when any hook uses .ts/.js
Workers python3, make, rsync, bash, timeout; sudo when use_sudo = true

SSH: private key in secrets/<ssh_key> authorized for ssh_user on every worker. See quickstart.


maand.conf (bucket root)

SSH, certificate, and job-config selector settings. Created on first maand init; not overwritten on later inits.

ssh_user = "agent"
ssh_key = "worker.key"
ssh_port = 22
use_sudo = true
certs_ttl = 60
certs_renewal_buffer = 10
job_config_selector = ""
log_format = "kv"
Field Default Purpose
ssh_user agent Remote user for deploy, job control, and run_command
ssh_key worker.key Private key file under secrets/
ssh_port 22 SSH port for worker connections and worker health checks
use_sudo true Prefix remote rsync and some commands with sudo
certs_ttl 60 Days until generated leaf certificates expire
certs_renewal_buffer 0 if omitted Leaf: regenerate when within this many days of NotAfter (0 = only after expiry). CA: same window for expiring status in maand cat certs and stderr warning on maand build — CA is never auto-renewed; an expired CA fails build
job_config_selector "" Suffix for bucket.jobs.<selector>.conf (see below)
log_format kv Bucket log encoding: kv, json, or jsonl (JSON lines)

Full TLS guide: certs.md.

Path on disk: <bucket>/maand.conf. Worker key path used at runtime: secrets/<ssh_key>.


workspace/bucket.conf

Bucket-wide settings (TOML). Created on maand init with a default port pool:

port_range = "30000,39999"
my_setting = "value"
Key Purpose
port_range Inclusive "min,max" pool for maand-assigned ({}) job ports only; fixed manifest ports are not limited to this range. When omitted or empty, build uses 30000,39999.
Other keys Copied to KV namespace vars/bucket at build (including port_range when set or defaulted)

Job manifests declare ports as {} (assign from pool) or a fixed integer (any port number). See build.md.


Job memory and CPU: manifest bounds vs bucket overrides

Memory and CPU are configured in two places; maand.conf picks which override file applies to the current environment.

Layer File Role
Bounds workspace/jobs/<job>/manifest.jsonresources.memory / resources.cpu Min and max the job is allowed to use (checked into git with the job)
Reservation workspace/bucket.jobs.conf or workspace/bucket.jobs.<selector>.conf Actual memory/CPU for this bucket/environment — must be within manifest min/max
Environment pick maand.confjob_config_selector Chooses which bucket.jobs*.conf file build reads

Example manifest bounds:

"resources": {
  "memory": { "min": "256 mb", "max": "2 gb" },
  "cpu": { "min": "500 mhz", "max": "2000 mhz" }
}

Example reservation for job api in the current environment:

[api]
memory = "512 mb"
cpu = "1500 mhz"

Build stores the reservation as current_memory_mb / current_cpu_mhz and validates:

manifest min ≤ bucket.jobs value ≤ manifest max

Worker capacity in workers.json must cover the sum of reservations on each host.


workspace/bucket.jobs.conf (optional)

Per-job TOML sections. memory and cpu set the reservation for that job; other keys are copied to KV only.

[api]
memory = "512 mb"
cpu = "1500 mhz"
my_setting = "staging-only"

Environment selector (job_config_selector)

Set job_config_selector in maand.conf to switch environments without editing job manifests. Build loads one override file:

job_config_selector in maand.conf Override file read
"" (default) workspace/bucket.jobs.conf
"prod" workspace/bucket.jobs.prod.conf
"staging" workspace/bucket.jobs.staging.conf

Pattern: bucket.jobs.<selector>.conf where <selector> matches job_config_selector.

Example layout:

workspace/
├── bucket.jobs.conf           # default / dev reservations
├── bucket.jobs.staging.conf   # used when job_config_selector = "staging"
└── bucket.jobs.prod.conf      # used when job_config_selector = "prod"

Change job_config_selector, then maand build. Manifest min/max stay the same; only the active reservation file changes.

Overrides are written to vars/bucket/job/<job> at build. Only memory and cpu affect reservations and worker validation.

Full guide (validation, placement labels, examples): resources-and-placement.md.


workspace/jobs/<job>/vars.conf (optional)

Stable job-scoped variables synced into vars/bucket/job/<job> at build (merged with bucket.jobs*.conf; stale keys removed). See KV namespaces.


workspace/disabled.json (optional)

Drain or pause workloads without removing workspace files. Always follow edits with maand build.

Full guide: disable and drain.


workspace/workers.json

Worker catalog — hosts, labels, capacity, tags. See concepts.md and build.md.

To fill memory and cpu from live hosts, run maand collect facts --generate-workers > workspace/workers.json, then maand build — see collect.md.


workspace/jobs/<job>/manifest.json

Job definition — selectors, resources, commands, certs, health. Canonical reference: manifest.md.


Upgrading configuration after a maand binary upgrade

After installing a newer maand binary, run maand init before any other command. The CLI checks schema version on every command (except init) and refuses to run when the database is behind the binary.

maand init    # schema migrations; keeps bucket_id and CA
maand build
maand deploy

If you skip init, commands such as build or deploy print an error like database schema upgrade required … run maand init to upgrade.

See day-2 operations.