This is the multi-page printable view of this section. .
Reference
- 1: Configuration
- 2: CLI
- 3: Images
- 4: Image Pipeline
- Configuration — discovery, accepted variables, defaults, disks, shares, naming, and drift.
- CLI — commands, important flags, output modes, and exit codes.
- Images — signed catalogs, aliases, cache layout, pulls, imports, and pruning.
- Image Pipeline — candidate validation and offline normalization.
Farrow exposes no supported Go library API. Packages under internal/ are
implementation details.
1 - Configuration
Discovery
Configuration lookup order is explicit -f, then farrow.yml,
farrow.yaml, pigsty.yml, and pigsty.yaml in the current directory. Every
name uses the same Pigsty-compatible YAML Inventory format.
The retired top-level version:/nodes: format is rejected with migration
guidance.
For plan, up, reload, and recreate, absence of a file falls back to
the applied spec when a deployment exists. validate has no fallback. A
configuration must be a regular non-symlink file no larger than 4 MiB.
What Farrow reads
Farrow reads host IPs, nodename, admin_ip, pg_cluster, pg_seq,
node_admin_username, node_admin_uid, and the documented vm_* variables.
admin_ip is read from all.vars; it selects the control node, or the first
managed host is used. All nodes must resolve the same login username. For the
default dba user, an explicit node_admin_uid must be 88.
Everything else is opaque and cannot create drift. This means unconsumed
fields such as pg_role, pg_version, repo_*, and node_packages, not all
possible pg_* or node_* names.
Inside this namespace validation is strict: unknown vm_* names, wrong
types, Jinja expressions, invalid addresses, and conflicting sibling-group
values are errors.
VM variables
| Variable | Default | Meaning |
|---|---|---|
vm_skip |
false |
do not virtualize this real/external host |
vm_image |
u24 |
image alias |
vm_arch |
native |
deployment-wide Guest architecture: native, amd64, or arm64 |
vm_cpu |
2 |
vCPU count |
vm_mem |
4096 |
MiB integer, or a size such as 8GiB |
vm_disk |
64 |
root disk GiB |
vm_disks |
[{path: /data}] |
extra disks |
vm_alias |
[] |
guest /etc/hosts, SSH-config, and optional host aliases |
vm_shares |
[] |
QEMU 9p host-directory shares |
An empty host entry is a complete VM. A deployment contains 1–20 managed
hosts; vm_cpu accepts 1–256 and memory must be at least 512 MiB.
vm_arch is stricter than ordinary per-host VM fields: when present it must
resolve to one value on every managed host, so define it once in all.vars.
Changing it is a deployment-envelope change and requires whole-deployment
recreation. Linux setup installs only the native emulator; a foreign
architecture also needs its matching qemu-system-* binary and firmware.
Data disks
path is the disk identity and mount point. fs is xfs or ext4.
persistent: true survives normal destroy. Use vm_disks: [] for no extra
disk.
Shares
Shares must be real, caller-owned, non-overlapping directories. They are for trusted development files, not PostgreSQL data.
Names and addresses
Node name order: nodename, then <pg_cluster>-<pg_seq>, then
node-<last-octet>. Names must be unique.
All managed hosts must be in one RFC1918 /24: .1 is the host, .2–.8
are reserved, and nodes use .9–.254.
Drift
Farrow hashes each resolved node. Added hosts are created by up; selected
existing stopped nodes are started; running peers stay untouched. Changed VM
definitions require per-node recreate; removed hosts are reported but never
destroyed. Deployment architecture, user, or subnet changes require whole-deployment
recreation. Changing a field used to derive a node name appears as a missing
old node plus a new node, so prefer stable explicit nodename values.
2 - CLI
The installed binary is the authoritative reference for its own version. Every visible command includes its operational boundary and copyable examples:
In text mode, bare farrow and namespaces such as farrow image display
contextual help and exit successfully. In JSON/YAML mode a bare namespace is a
structured usage error; explicit --help always renders human help.
Commands
| Area | Commands |
|---|---|
| Prepare | setup, init, validate, doctor |
| Lifecycle | plan, up, start, stop/halt, restart, reload, recreate, status, destroy |
| Access | ssh, exec, logs, provision, ssh-config, ss, hosts |
| Images | image list/info/pull/import/sync/prune/reset-manifest |
| Host network | network status/install/uninstall |
| Misc | version, completion |
Commands using applied state work from any directory. Configuration selection
is command-scoped; -f is deliberately not a global flag:
| Commands | Desired-state source |
|---|---|
setup [template] |
explicit -f, otherwise discovery, otherwise generate meta; template and -f are exclusive |
init [template] |
generate a new inventory; read no desired state |
validate |
explicit -f, then discovery; never applied state |
plan, up, reload, recreate |
explicit -f, then discovery, then the applied resolved specification |
| other lifecycle/access commands | no desired-state inventory; use applied or marker-owned state as applicable |
Important flags
| Flag | Meaning |
|---|---|
--json, --yaml |
machine-readable stdout; progress remains on stderr |
--verbose |
bounded diagnostics on stderr |
--yes |
apply a displayed host/setup plan |
--force |
skip the interactive destroy/recreate confirmation; required without a terminal |
--no-wait |
return after QMP/process identity without guest readiness |
--delete-persistent |
during whole destroy, also delete retained data disks; invalid with node selectors |
--purge |
whole-deployment disposal: delete disks, keys, and deployment state; keep images |
If a failing command has not already emitted a richer typed result, structured
mode writes one object containing error and message before returning the
documented non-zero exit code. Existing typed failure results are never followed
by a second JSON/YAML document.
plan is read-only and returns success even when its action is recreate or
blocked-removal; automation must inspect the action and create, recreate,
and missing fields. up creates additions and starts selected stopped nodes,
but returns a conflict instead of applying destructive drift.
status reports the persisted guest_arch and accelerator for each node.
TCG selection is therefore explicit in both text and structured output.
SSH passthrough and completion
farrow ssh [node] [--] [command ...] opens a session or runs an optional
command. farrow exec [node] [--] <command ...> requires a command and passes
through its exit status. Presentation flags before -- belong to Farrow;
arguments after -- belong to OpenSSH or the remote program.
Load farrow completion bash|zsh|fish|powershell for command and scoped-flag
completion. It also provides templates, image aliases, closed flag choices,
and best-effort node names from the desired or applied specification.
Exit codes
| Code | Meaning |
|---|---|
| 0 | success |
| 1 | runtime failure |
| 2 | usage or invalid configuration |
| 3 | missing host capability |
| 4 | state conflict or explicit convergence required |
| 5 | partial multi-node completion |
| 6 | resource conflict |
| 7 | integrity or ownership failure |
ssh and exec pass through the remote program’s exit code, except SSH’s
reserved transport-failure code 255, which Farrow maps to runtime failure 1.
3 - Images
Farrow uses a signed static-file Catalog plus immutable qcow2 artifacts. A Catalog update does not require a new Farrow binary, but the binary decides which signing keys and image safety rules are trusted.
EL7 is deprecated; every other built-in image is currently testing, not
supported. The warning printed by up is intentional: a successful pull
is an integrity result, not a production-support promise.
Aliases and pull order
The embedded Catalog contains 9 families and 17 artifacts: el7 is
amd64-only; el8, el9, el10, d12, d13, u22, u24, and u26
have amd64 and arm64 artifacts. u24 is the VM default.
| Alias | Distribution | Architectures | Boot | Status |
|---|---|---|---|---|
el7 |
CentOS Linux 7.9 / 2211 | amd64 | BIOS | deprecated |
el8 |
Rocky Linux 8.10 | amd64, arm64 | UEFI | testing |
el9, el10 |
Rocky Linux | amd64, arm64 | UEFI | testing |
d12, d13 |
Debian | amd64, arm64 | UEFI | testing |
u22, u24, u26 |
Ubuntu | amd64, arm64 | UEFI | testing |
For a pull, Farrow:
- refreshes
catalog.jsonand its adjacent.minisigfrom--repo, thenFARROW_REPO, then the compiled default repository; - verifies the signature with an embedded active or standby public key;
- selects the Catalog’s default release; standalone
image pulluses the native architecture, while lifecycle resolution honorsvm_arch; - reuses a local file only after size, SHA-256, and qcow2 checks pass;
- otherwise downloads the repository artifact, with the Catalog’s immutable HTTPS upstream URL as fallback.
The current compiled default is the development repository
https://m0/farrow; no public image service is claimed yet. An explicit
repository failure is fatal, while an unreachable compiled default falls back
to the embedded Catalog.
Runtime policy
Matching architectures use native HVF/KVM except one catalogued incompatibility:
the stock EL8 arm64 64K-granule kernel cannot run through Apple HVF, so Apple
Silicon uses visible same-architecture TCG automatically. Explicit foreign
vm_arch also uses TCG. amd64-on-arm64 uses a single translation thread to
preserve x86 memory ordering. TCG results are not performance evidence.
EL7 is deliberately limited to native Linux/amd64. Linux setup installs only
the native QEMU family; foreign architectures require the matching system
emulator and UEFI firmware before plan, up, or recreate can proceed.
Repository URLs may be HTTP or HTTPS because the Catalog signature and image digest remain authoritative; immutable upstream artifact URLs must be HTTPS.
Trust and verification
Current ordinary builds embed both production public verification keys. The private signing keys are external to the source repository. Catalog activation rejects unknown keys, malformed content, equivocation, and versions below the recorded high-water mark unless the operator explicitly allows a downgrade.
Every accepted image must be a size- and SHA-256-matched plain qcow2 with no backing file, external data file, encryption, or unknown incompatible feature. Verified base images become read-only; node root disks are overlays and never modify the base.
reset-manifest restores the embedded bootstrap Catalog but keeps the
anti-rollback high-water mark.
Local layout and imports
Images live under FARROW_HOME/images (default ~/.farrow/images): family
directories contain downloaded artifacts, manifests/ stores Catalog state,
and local/ plus local-images.json hold imports. There is no old
~/.farrow/cache or content-addressed sha256/ hierarchy.
Named local aliases must begin with local-, so a future signed Catalog cannot
shadow them. --boot and --source-user are required with --name; Farrow
does not guess the guest bootstrap contract.
Pruning
Prune lists exact unreferenced images and stale staging files before deletion.
An image referenced by applied deployment state is never a candidate. Images
remain cached after destroy, including destroy --purge.
The compiled Catalog can be exported byte-for-byte with
go run ./tools/catalogexport /absolute/new/catalog.json. A public Catalog at
the embedded version must use those exact bytes; same-version different bytes
are rejected as equivocation. Release signing and image Catalog signing remain
separate trust domains.
4 - Image Pipeline
packaging/image-pipeline/ accepts one already-downloaded immutable qcow2 and
an independently obtained SHA-256. It never downloads, uploads, touches Farrow
runtime/network state, reads signing keys, or marks an image supported.
Modes
validate: copy/re-hash, force qcow2 inspection, validate the single backing chain, runqemu-img check, and emit an explicitly unpublishable evidence bundle. Guest credentials are not changed.offline: additionally use libguestfsvirt-customize --no-networkandvirt-caton the staged copy. It rejects unrelated UID/GID 88 occupants, normalizes the lockeddba/adminidentity, disables password/root SSH, removes keys/history/host identity/cloud-init cache, restores targeted SELinux labels, and reads back a deterministic marker.
Source/output paths must be absolute; source is canonical, regular, non-symlinked, stable while copied, and at most 16 GiB. Output must not exist. The builder uses an exclusive adjacent lock, mode-0700 staging, and one final rename. Failure removes only its guarded staging directory.
Every successful bundle contains the read-only qcow2, recipe, SLSA provenance,
SPDX boundary SBOM, manifest-candidate.json (testing), validation evidence,
and checksums. Signing is deliberately outside this pipeline. Validate mode is
byte-reproducible for fixed inputs/tools; offline mutation must be built twice
and compared before release evidence is accepted.