Farrow turns one Pigsty-compatible Ansible inventory into one fixed-IP local QEMU deployment on macOS or Linux. The inventory you hand to Pigsty is the same file that describes the virtual machines, so there is no second project format to keep in sync.
Authoritative documentation: https://farrow.pgsty.com/
farrow up # prepare the host and start your first VM
farrow ssh # connectOn first use in a terminal, up creates a one-node farrow.yml if no inventory
or applied deployment exists. To customize it first, run farrow init and edit
the file; use farrow init full for the four-node template.
- One inventory, one deployment.
vm_*host variables describe the guests; everything else in the file stays opaque and is passed through to Pigsty untouched. There is no separate VM manifest. - Fixed IPs, not DHCP. Nodes get the addresses the inventory names, on a
host-global private network Farrow installs once.
10.10.10.10is10.10.10.10across reboots and recreates. - Declarative, but never surprising.
farrow planshows the difference between the inventory and the applied state. Removing a host from the file never destroys a machine — deletion is always an explicitfarrow destroy. - Ubuntu 24.04 by default. New inventories use
u24:stable; setvm_imageexplicitly to choose another supported distribution. - Verified images. Guest images come from a signed catalog with SHA-256, qcow2, and virtual-size verification on every fetch. The selected repository supplies the final image bytes; immutable upstream URLs remain build-provenance markers.
- Recoverable lifecycle. Repeating
upcontinues unfinished work and keeps successful nodes available. QMP/process identity, image digests, and disk ownership are still verified before changing resources.
Farrow is a local development-lab runtime. It is not a cluster manager, not a cloud provisioner, and not a container runtime.
plan reads local configuration and catalog data without requiring host setup.
It shows exact images, resources, and disk effects; up checks host capabilities
before applying changes. Starting commands also refresh the Farrow-managed
hosts and SSH entries inside running guests. --no-wait skips readiness and
that guest refresh; a later up completes them.
Fast checks stay quiet. Longer operations share one live progress area with
elapsed time, download bytes/speed/ETA, and individual node readiness. A
successful start ends with a short result and a connection command. Partial
starts list usable and pending nodes, group repeated errors, and give a retry
command that retains the inventory path. status keeps the detailed table;
--verbose exposes diagnostic detail and time spent in each foreground stage,
including waits and prompts. Redirected output uses occasional plain
progress lines on stderr, and --json/--yaml keep stdout machine-readable.
Bare farrow shows the next few actions; farrow --help is the full reference.
up can install missing host tools and restore an inactive, verified Farrow
network during an interactive session. The hosts-file helper is installed only
when farrow hosts install needs it. A fresh, untouched default template can use
an available subnet; its original is saved as farrow.yml.before-network-change.
Explicit -f files, edited templates, and existing deployments keep their subnet.
For unattended first setup, run farrow setup --yes explicitly.
Interrupted image downloads retry and resume automatically. Official image
repositories can fail over to their counterpart; custom repositories stay
exclusive. Writable cache files are made read-only only after verification.
Damaged, unreferenced cache files are preserved as .corrupt-<timestamp> before
replacement; referenced backing files stay in place. Ctrl-C preserves completed
work and resumable downloads, so the same command can continue later.
An offline node-prepare failure can also be retried with up: recognized
uncommitted artifacts are cleaned first, while committed nodes and unexpected
files are preserved.
Guest readiness requires working management SSH and the expected login identity.
Data disks, shared directories, guest hostnames, node-to-node SSH, and private
network checks run independently with bounded waits. Unavailable features produce
ready · with limitations and exit 0, while other features remain usable. Each
failed disk or share is named; an unavailable data disk is never reported as
mounted. Internet access is optional, so offline labs can finish setup.
Repeating farrow up [node...] retries unfinished guest setup and updates old
guest helpers in place. Running VMs keep their process and root disk; healthy
setup stages are skipped. --no-wait skips these guest checks as well as waiting
for readiness. No separate repair command is needed.
Data disks are disposable test storage. Working filesystems are reused. If a
configured data disk has no recognizable filesystem, or cannot mount and a
filesystem check confirms damage, up resets it to an empty filesystem and
reports that previous data was discarded. This can erase a persistent data
disk too: persistent retains it across destroy/recreate, not after filesystem
failure. A missing device, failed probe, busy mount, or underlying I/O error is
reported without formatting; other guest features remain available. Root disks
and host shared directories are never reset by this recovery.
Writable shares use the guest user's identity for newly created files. If the
host directory does not permit guest writes, Farrow mounts it read-only and
reports the limitation. It does not recursively change host project ownership.
After fixing access, repeat farrow up to retry the writable mount.
If another process takes an automatically allocated management SSH port while a VM is stopped, the next start chooses another free port and updates VM state and SSH aliases together. Explicit application forwards keep their assigned ports. Running VMs keep their port and process.
SSH trust is keyed by VM instance UUID, allowing a recreated VM to reuse an SSH
port without inheriting its predecessor's host key. Changed keys for the same
instance still fail verification. Existing labs adopt this namespace on their
first connection with the new binary; legacy port entries remain until destroy.
SSH aliases, guest hostname refresh, and diagnostic event writes are optional
after a successful lifecycle operation: their failures produce warnings with a
retry command, while the VM result remains successful. Explicit commands such
as ssh-config --install still report their own failures. Lifecycle integration
waits have separate budgets: 2 seconds for the SSH configuration snapshot,
5 seconds for guest metadata, and 1 second for diagnostic events. A timeout
preserves the VM result and gives a warning; a later up retries the refresh.
| Host | Accelerator | Minimum QEMU | Tier |
|---|---|---|---|
| macOS arm64 | HVF | 8.2.1 | 1 |
| macOS amd64 | HVF | 8.2.1 | 2 |
| Linux amd64 | KVM | 6.2 | 1 |
| Linux arm64 | KVM | 6.2 | 2 |
Tier 1 is the dated, natively validated matrix; tier 2 is cross-built and
package-verified against the narrower status published at
https://farrow.pgsty.com/docs/about/status/. You also need qemu-img, UEFI
firmware for arm64 guests, and an OpenSSH client.
farrow doctor reports host dependencies, persisted state, and network setup;
farrow status audits live QMP/process identity and safely converges interrupted
transitions. farrow setup installs what it can and asks for administrator
access only when a host transaction genuinely needs it.
Farrow is pre-1.0. A successful build from source is not evidence of a tagged release, a published package, or a supported guest image.
Download install.sh, farrow.rb, or the native package from the
Farrow 0.7.0 release.
# From a release: user-scoped, no sudo, checksum-verified
curl -fLO https://github.com/pgsty/farrow/releases/download/v0.7.0/install.sh
chmod +x install.sh
FARROW_VERSION=0.7.0 ./install.sh
# Homebrew formula (shipped as a release asset)
brew install --formula ./farrow.rb
# Debian/Ubuntu and RHEL-family packages are release assets too
sudo apt install ./farrow_<version>_linux_amd64.deb
sudo dnf install ./farrow_<version>_linux_amd64.rpmGitHub does not expose pre-1.0 prereleases through /releases/latest, so
FARROW_VERSION is required until a stable release exists. The installer
always verifies the selected archive against the checksums.txt produced by
the GitHub release workflow.
When upgrading an existing Debian lab whose inventory omits vm_image, set
vm_image: d13 in all.vars to keep that choice. Upgrading Farrow does not
replace healthy VM disks; farrow plan shows configuration changes before you
apply them. In 0.7.0, up can reset damaged data filesystems, including persistent
disks, as described above. See the 0.7.0 release notes.
From source:
make build
export PATH="$PWD/bin:$PATH"farrow init full # a four-node inventory instead of one
farrow validate # parse and resolve without touching anything
farrow plan # what would change, and why
farrow up # converge
farrow update # fetch and activate a newer image catalog
farrow status # audit/converge selected runtime state, from anywhere
farrow ssh meta -- uptime # run something in a guest
farrow hosts install --yes # publish node names into the host hosts file
farrow destroy # explicit, confirmed teardown
farrow purge # no-confirmation disposal; images/network remainEvery command accepts --json or --yaml for stable machine-readable output.
Presentation flags never change an exit status.
farrow exec meta -- command arg... preserves argument boundaries, including
quoted spaces and empty values. Use sh -c 'script' for shell expressions.
The single-string shorthand (farrow exec meta -- 'uptime; id') and ordinary
farrow ssh shell semantics remain available.
| Code | Meaning |
|---|---|
| 0 | success |
| 1 | runtime failure |
| 2 | usage error |
| 3 | missing host capability |
| 4 | state conflict (no deployment, wrong phase) |
| 5 | partial completion across nodes |
| 6 | resource conflict (address or port in use) |
| 7 | integrity failure (digest, signature, or state mismatch) |
| 130 | cancelled: interrupted by SIGINT/SIGTERM, or a confirmation was declined |
ssh, exec, and a single-node provision pass the guest command's own exit
status through unchanged, so their non-zero codes are the remote program's,
not one of the categories above. Farrow's own failures on those paths still use
the table.
Everything Farrow owns lives under $FARROW_HOME (default ~/.farrow): the
applied deployment, per-node state and journals, the verified image cache, and
the signed catalog. Applied-state commands therefore work from any directory,
and removing that one tree removes Farrow's footprint apart from the host
network and the hosts-file entries, which have their own uninstall and
remove commands.
The image catalog ships inside each Farrow release, and Farrow never refreshes
it implicitly. farrow update fetches the configured repository's catalog;
farrow image sync activates an exact URL or file. Ordinary commands use the
active local catalog. The default repository is https://repo.pigsty.io/farrow;
--mirror selects https://repo.pigsty.cc/farrow, while an explicit --repo
overrides --mirror, FARROW_REPO, and the default.
Optional integration warnings appear in structured lifecycle results as
warnings with code, message, detail, and an optional next command.
Per-node ready: true records a successful guest readiness check in that startup
operation; ordinary status reports runtime state without claiming SSH readiness.
Per-node warnings contain {stage, detail} for limited guest features; these
survive CLI invocations and are refreshed on the next readiness check. These
limitations use a disposable cache separate from core VM state, so diagnostic
metadata does not prevent rolling back to 0.6.0. Per-node
repairs describe automatic actions taken in that operation, such as a changed
SSH port or a reset data disk. Automation that requires every configured guest feature should check
nodes[].warnings as well as the exit code.
make build # build into ./bin
make test # unit tests
make check # the complete source gate CI runsmake check covers module integrity, shell syntax, unit and race tests, vet,
Staticcheck, govulncheck, cross-compilation for all four targets, image and
installer trust boundaries, and the dependency-license inventory. See
CONTRIBUTING.md.
Farrow asks for root when installing Linux host packages, setting up the private network, or publishing node names into the system hosts file through a separate helper binary. See SECURITY.md for the privilege boundary and how to report a vulnerability.
Apache-2.0. Release tooling reconstructs dependency license texts from the
exact module versions pinned by go.mod, and ships them inside every archive
and package.