Skip to content

This is the multi-page printable view of this section. .

Return to the regular view of this page.

Barn documentation

Install Barn 1.2.0, create your first Linux or macOS VM, and find the guides and reference for your next task.

Barn provisions local virtual machines for development and testing. This documentation covers Barn 1.2.0; the 1.2.0 release notes summarize what changed since 1.1.0. Choose a starting workflow below:

  • Install Barn 1.2.0 — Install the CLI on macOS or Linux via Homebrew, the release installer script, or native packages.
  • Linux Quick Start — Boot a single Ubuntu 24.04 node or a multi-node fixed-IP lab from an Ansible YAML inventory on macOS (HVF) or Linux (KVM).
  • Native macOS VMs — Run isolated macOS 27+ machines on Apple Silicon with a native desktop window, SSH, VirtioFS shared folders, and clipboard sync.

Find every command, option, and example in the Command Reference. Its version baseline identifies options added since 1.1.0.

Common tasks

  • Daily operations — Start, stop, inspect, plan changes, and recreate Linux lab nodes.
  • Storage and service access — Add persistent data disks, mount Virtio-9p host shares, and reach guest ports.
  • Image selection — Choose from 9 Linux distribution families, update the catalog, or import custom qcow2 images.
  • Scripting and automation — Drive Barn from CI or shell scripts with --yes, --json, and structured exit codes.
  • Troubleshooting — Diagnose host readiness, networking, SSH, and interrupted lifecycle runs with barn doctor.
  • What’s new in 1.2.0 — Review resource overrides, root growth, concurrency, host names, and upgrade notes.

Browse by section

1 - Guides

Install Barn, start a Linux or macOS VM, then manage machines, files, images, and automation.

These guides cover Barn 1.2.0. The Barn 1.2.0 release notes summarize its changes and the upgrade from 1.1.0. You can run both Linux labs and isolated macOS VMs on the same Apple Silicon Mac—each workflow manages its own VMs under ~/.barn.

Get started

  • Install Barn — Install the stable CLI on macOS or Linux, check host requirements, and upgrade existing installations.
  • Linux Quick Start — Bring up an Ubuntu 24.04 VM, connect over SSH, and scale to a multi-node fixed-IP lab from one barn.yml.
  • macOS Virtual Machines — Create and manage macOS 27 guests on Apple Silicon with a native desktop, SSH, shared folders, and clipboard sync.

Day-to-day Linux operations

  • Daily Operations — Inspect status, stop and start nodes, preview inventory changes with plan, and recreate or destroy machines.
  • Storage and Access — Configure persistent test data disks, transfer files over SSH, forward ports, and mount Virtio-9p host shares.
  • Image Repositories — Pick from 9 distribution families, switch to the China mirror, build a static image repository, or import a local qcow2.
  • Automation — Run unattended host setup, validate --json output with jq, execute scripts via barn provision, and pair with Pigsty.

Diagnostics, source builds, and cleanup

  • Troubleshooting — Runbooks for host readiness, network repairs, guest stage warnings, SSH keys, and interrupted commands.
  • Build from Source — Compile the v1.2.0 CLI, barn-hosts-helper, and native Barn.app bundle, and run contributor test gates.
  • Uninstall and Clean Up — Tear down Linux and macOS VMs cleanly, prune image caches, remove host networking, and uninstall Barn.

1.1 - Download and Install Barn

Install Barn 1.2.0 on macOS or Linux, check requirements, and keep the application up to date.

Barn 1.2.0 ships archives for macOS and Linux on arm64 and amd64, along with DEB and RPM packages for Linux. Always run barn as your normal Unix user; after installing, continue with the Linux quick start or macOS VM guide.

Note

Barn 1.2.0 is the current stable release. It is published on GitHub’s regular release channel, so releases/latest, the installer without BARN_VERSION, and the Homebrew formula select it; set BARN_VERSION=1.2.0 to pin it. The Apple Silicon archive includes the matching barn, barn-hosts-helper, and native macOS app bundle (Barn.app), signed with Developer ID and notarized by Apple. Go and Xcode are only needed for source builds.

Component Purpose Included in 1.2.0 downloads
barn Go CLI for Linux labs and macOS VM lifecycle All four platform archives and Linux packages
barn-hosts-helper Companion binary for /etc/hosts name integration Packaged alongside its matching CLI
Barn.app Native Virtualization.framework runner and macOS guest desktop Included in the Darwin arm64 archive and Apple Silicon Homebrew install; macOS guests only

Install the release

Choose the operating system of your host machine. Use Homebrew on macOS or the release installer script on Linux.

Install Barn and its QEMU dependency with Homebrew as your normal user:

brew install pgsty/infra/barn
barn version

On Apple Silicon, the Homebrew formula also installs Barn.app under libexec, where the CLI discovers it automatically. On macOS 27+, run barn mac doctor and continue with the Mac guide. Intel Macs use the CLI and helper for Linux guests.

Download install.sh and run it. Without BARN_VERSION, it installs the latest stable release; set BARN_VERSION=1.2.0 to pin this one. The script detects amd64 or arm64, verifies the archive SHA-256 against checksums.txt, and installs the CLI and matching barn-hosts-helper under ~/.local/bin without sudo:

curl -fLO https://github.com/pgsty/barn/releases/latest/download/install.sh
bash install.sh
export PATH="$HOME/.local/bin:$PATH"
barn version

Add export PATH="$HOME/.local/bin:$PATH" to your shell startup file (such as ~/.bashrc or ~/.zshrc), and keep the versioned .barn-releases/ directory layout intact. You can also run this same installer on macOS if you prefer not to use Homebrew; barn setup will handle QEMU and host-network dependencies separately.

When you run barn up interactively for the first time, Barn checks for missing Linux-guest dependencies and host networking, shows the host-preparation plan, and asks once before installing anything; it requests administrator access (sudo) only when a step needs it. Without a terminal, barn up only reports what is missing, so run barn setup --yes for unattended preparation. To preview the host changes without applying them, run barn setup --dry-run.

System requirements

Guest Host Runtime
Linux macOS, Apple Silicon or Intel QEMU 8.2.1+, HVF
Linux Linux, arm64 or amd64 QEMU 6.2+, usable KVM, NetworkManager or systemd-networkd
macOS 27 Apple Silicon, macOS 27+, logged-in desktop session Barn.app native component; no QEMU required

A default Linux VM uses 1 vCPU and 2 GiB of memory, backed by thin-provisioned qcow2 disks (64 GiB root disk and 128 GiB test data disk) that grow as data is written.

The first macOS VM requires roughly 65 GiB of free disk space to hold Apple’s ~25 GiB restore image, the ~27 GiB installed base image, and initial guest writes. By default, a Mac VM uses 4 vCPUs, 8 GiB of memory, and a 100 GiB APFS clone disk. Later Mac VMs inherit the prepared base image’s capacity unless you pass --disk. See Platforms and limits for guest-specific constraints.

Download Barn 1.2.0

The stable release was published on 2026-11-12. Select the host operating system and CPU architecture:

Host OS Architecture Archive Debian / Ubuntu RHEL / Fedora
macOS Apple Silicon (arm64) tar.gz — —
macOS Intel (amd64) tar.gz — —
Linux x86-64 (amd64) tar.gz DEB RPM
Linux ARM64 (arm64) tar.gz DEB RPM

SHA-256 checksums · Release metadata · Installer script

Each archive and package has a matching .spdx.json dependency inventory on the GitHub Release. The Darwin arm64 archive includes the signed, notarized Barn.app.

Other installation methods

Linux packages

The DEB and RPM packages declare their QEMU, UEFI firmware, and OpenSSH dependencies automatically. On ARM64 hosts, replace amd64 with arm64:

Debian / Ubuntu
barn_release=https://github.com/pgsty/barn/releases/download/v1.2.0
curl -fLO "$barn_release/barn_1.2.0_linux_amd64.deb"
curl -fLO "$barn_release/checksums.txt"
sha256sum --check --ignore-missing checksums.txt
sudo apt install ./barn_1.2.0_linux_amd64.deb
barn version
RHEL / Fedora
barn_release=https://github.com/pgsty/barn/releases/download/v1.2.0
curl -fLO "$barn_release/barn_1.2.0_linux_amd64.rpm"
curl -fLO "$barn_release/checksums.txt"
sha256sum --check --ignore-missing checksums.txt
sudo dnf install ./barn_1.2.0_linux_amd64.rpm
barn version

Manual archives

Download barn_1.2.0_<os>_<arch>.tar.gz (darwin or linux, arm64 or amd64) and checksums.txt from Barn 1.2.0 on GitHub, then verify the SHA-256 digest before extracting:

barn_release=https://github.com/pgsty/barn/releases/download/v1.2.0
curl -fLO "$barn_release/barn_1.2.0_darwin_arm64.tar.gz"
curl -fLO "$barn_release/checksums.txt"
shasum -a 256 -c checksums.txt --ignore-missing

Extract the archive and add its bin/ directory to PATH. Keep the packaged layout intact: barn and barn-hosts-helper must stay paired, and the Darwin arm64 archive also contains bin/Barn.app. The CLI verifies the helper’s embedded SHA-256 before invoking it and discovers the app beside the binary.

Build from source

To work on Barn or compile the native Barn.app bundle for macOS guests, see Build from Source.

Upgrade Barn

Upgrading from 1.1.0 to 1.2.0 preserves VM disks, SSH keys, state, and configuration under ~/.barn. Stop running Mac VMs before replacing the CLI, helper, and Barn.app together. Linux inventories that omit CPU/memory now resolve to 1 vCPU / 2 GiB; explicitly set vm_cpu: 2 and vm_mem: 4GiB to preserve the old defaults and inspect barn plan. Existing applied VMs keep their resources until a resource-changing operation is requested. See the 1.2.0 release notes.

Upgrade using the same channel you used to install Barn. On macOS with Homebrew:

brew update
brew upgrade pgsty/infra/barn
command -v barn
barn version

On Linux, re-run install.sh (it selects the latest stable release unless BARN_VERSION is set) or install the newer .deb / .rpm package, then run command -v barn and barn version to confirm the active binary. If both a user-directory install and a system package/Homebrew install exist, update the copy selected by PATH, or adjust PATH and verify the version again.

Barn decouples CLI upgrades from guest image updates so existing VMs never change OS versions unexpectedly:

Target Command Effect on existing VMs
Barn CLI, helper & bundled app Reinstall or brew upgrade Preserves all VM disks and state
Linux image catalog barn update (or barn update --mirror) Updates catalog for new or recreated nodes
macOS base image barn mac image update Prepares a new base image; apply to an existing VM with barn mac recreate NAME --update

For proxy or PATH issues, see Troubleshooting. To remove Barn and its host state, see Uninstall and Clean Up.

1.2 - Linux Quick Start

Start an Ubuntu VM, connect over SSH, and expand it into a fixed-IP Linux lab with one inventory.

Create an Ubuntu 24.04 VM on your Mac or Linux host, connect over SSH, and scale it into a multi-node fixed-IP lab from a single YAML inventory. For macOS guests on Apple Silicon, see the macOS VM guide.

This walkthrough uses Barn 1.2.0, with 1 vCPU and 2 GiB per node by default. Set resource fields explicitly to customize the lab; see configuration defaults.

Before you start

Install Barn and make sure your host has at least 2 GiB of free memory for the guest. Run the commands below as your normal user in an interactive terminal: Barn asks once before it changes the host and prompts for sudo only for the steps that need it. For unattended CI runs, see Automation. In mainland China, add --mirror to the first barn up to download images from the China repository at repo.pigsty.cc.

Customize before the first start

The default lab is one Ubuntu node using the host architecture: arm64 on Apple Silicon, amd64 on Intel/AMD. No architecture setting is needed for normal use; see image and architecture settings for cross-architecture selection and precedence. To keep the defaults, skip to Start and connect. To choose the topology, subnet, or OS image first, write and edit the inventory before the first barn up:

  1. Generate a starter barn.yml (dual, trio, or full creates 2, 3, or 4 nodes; -c 10.20.30.0/24 selects a custom subnet, -I el9 writes one image for every node, and -A amd64 sets vm_arch):

    mkdir -p ~/barn-lab && cd ~/barn-lab
    barn init
  2. Edit barn.yml to choose your image and resources:

    barn.yml
    all:
      vars:
        admin_ip: 10.10.10.10
        vm_image: u24
        vm_cpu: 1
        vm_mem: 2GiB
      children:
        nodes:
          hosts:
            10.10.10.10: { nodename: meta }
  3. Validate the YAML and preview the plan:

    barn validate
    barn plan

barn validate and barn plan resolve catalog metadata without requiring QEMU or host-network setup, and the next section’s barn up uses this barn.yml. See Linux Configuration for all inventory variables.

Start and connect

mkdir -p ~/barn-lab && cd ~/barn-lab
barn up

When neither an inventory file nor an active deployment exists, barn up prints a short setup plan before it changes anything. Only the rows that apply appear: dependencies lists missing packages (QEMU, UEFI firmware, OpenSSH) and the package manager, network names the fixed-IP subnet it prepares, config names the one-node barn.yml it writes, and sudo says which steps need root. On a host that already has QEMU:

ITEM     STATE    MESSAGE
network  planned  prepare 10.10.10.0/24
config   planned  create /home/alice/barn-lab/barn.yml
sudo: network (root-owned barn0 bridge)
Continue with this setup? [Y/n]

Answer once; sudo may then ask for your password. Barn downloads and verifies the base image, updates the live progress table on a terminal, and waits until SSH is ready:

NAME  ADDRESS      STATE  MESSAGE
meta  10.10.10.10  ready  SSH ready: barn ssh meta
boot 1/1 nodes: 39s [DONE]

If barn up stops with an error instead, run barn doctor to check the host and barn logs meta to read the guest console, fix the reported problem, and run barn up again. See Troubleshooting for common failures.

Log in to the guest:

barn ssh

barn ssh logs you in as dba with key-based SSH and passwordless sudo. barn up also installs SSH aliases, so plain ssh meta works too. Type exit to return to your host shell; the remaining commands run on the host.

Default Value
Node and address meta at 10.10.10.10
Image Ubuntu 24.04 (u24:stable, native host architecture)
CPU and memory 1 vCPU, 2 GiB
Disks 64 GiB root overlay, 128 GiB test data disk mounted at /data

Both disks are thin-provisioned qcow2 files that grow as data is written. If 10.10.10.0/24 conflicts with an existing route on your machine, first-run setup automatically picks a free private /24 for a fresh default template—check the generated barn.yml and barn status for the assigned IP.

Note

One Linux deployment per BARN_HOME: Barn stores your active Linux lab under BARN_HOME (default ~/.barn). Changing working directories does not create a second lab; running barn up in a directory without an inventory simply resumes the existing deployment. See One Deployment, No Projects for the rationale.

Inspect and use the VM

barn status
NAME  ADDRESS      STATE    IMAGE             SPEC  ROOT
meta  10.10.10.10  running  u24@20261017.0.1  1c2g  64 GiB

barn status reports each node’s fixed address, recorded state, image release, and size. Run guest commands from the host by putting -- before the remote command:

barn exec meta -- hostname
barn exec meta -- df -h / /data

barn up verifies management SSH and reports guest-stage warnings such as an unavailable data disk or private interface. If setup is interrupted or you fix a host issue, run barn up again—healthy VMs stay running while Barn finishes the remaining steps.

Warning

Data disks are disposable test storage. During guest recovery, barn up resets an ext4 or XFS data filesystem when the filesystem checker reports recognized corruption, including on persistent disks. Keep important files on the host; see Storage and Access for disk retention and recovery boundaries.

Scale to a multi-node lab

To grow the lab to four nodes (8 GiB of guest memory at the default 2 GiB per node), add three host entries under all.children.nodes.hosts in your existing barn.yml. Use addresses from the same /24 as meta; if first-run setup chose another subnet, use that one:

barn.yml
all:
  vars:
    admin_ip: 10.10.10.10
  children:
    nodes:
      hosts:
        10.10.10.10: { nodename: meta }
        10.10.10.11: { nodename: node-1 }   # new
        10.10.10.12: { nodename: node-2 }   # new
        10.10.10.13: { nodename: node-3 }   # new

Preview the change and converge the new nodes:

barn plan
barn up

barn plan reports create: node-1, node-2, node-3, and barn up creates them without restarting meta.

If you change an existing node’s CPU or memory, apply the change with barn resize <node>; it preserves the VM identity and disks. Image changes still use barn recreate <node>, which replaces its root disk. Removing a host entry from barn.yml leaves the VM untouched on disk until you run barn destroy <node>.

Stop, resume, and clean up

barn stop
barn start

Both stop and start preserve all VM disks. When you finish with the lab, run barn destroy and type destroy to confirm. Barn deletes the VMs with their root overlays and non-persistent data disks, removes the deployment state and the SSH aliases it installed, and keeps persistent data disks, the deployment keys, cached base images, and the host network. Running barn up in ~/barn-lab again builds a fresh lab from barn.yml. For a full teardown, see Uninstall and Clean Up.

Next steps

  • Daily Operations — Inspect logs, restart nodes, apply inventory changes, and destroy VMs.
  • Storage and Access — Configure persistent disks, copy files with OpenSSH, and reach guest ports.
  • Image Repositories — Select Debian, Rocky Linux, Ubuntu, or local qcow2 images.
  • Automation — Run unattended setup --yes, parse --json output, run guest scripts, or pair pigsty.yml with Pigsty.

1.3 - macOS Virtual Machines

Run macOS 27 virtual machines on Apple Silicon with barn mac — create, connect, share files and the clipboard, and clean up.

barn mac provisions disposable macOS VMs on Apple Silicon Macs for building, testing, and reproducing macOS-specific issues. Each VM comes with an administrator account, passwordless sudo, pinned SSH keys, and a dedicated private IP, running directly under your user account via Apple’s Virtualization framework without sudo.

Mac VMs are managed independently from the Linux lab: they do not read barn.yml, they store their state under $BARN_HOME/mac (default ~/.barn/mac), and Linux barn destroy or barn purge commands never touch them.

Requirements

  • An Apple Silicon Mac running macOS 27 or later with an active logged-in desktop session (guests also run macOS 27).
  • Roughly 65 GiB of free disk space for the first VM—covering Apple’s ~25 GiB restore image (.ipsw), the ~27 GiB installed base image, and initial guest writes. Each VM clones the base image on APFS and grows up to its configured capacity (100 GiB by default).
  • The native Barn.app bundle paired with the CLI from the same release, included in the Apple Silicon release and Homebrew installation.
  • No sudo privileges required.

Apple’s Virtualization framework allows at most two running macOS VMs at a time per Mac across all virtualization tools (including an active macOS base-image installation). You can keep additional stopped VMs on disk and start any two at once, subject to the Apple software license.

Install

Install the prebuilt release with Homebrew:

brew install pgsty/infra/barn
barn version
barn mac doctor

Homebrew installs the signed, notarized Barn.app and matching CLI/helper together. No Go or Xcode installation is needed. The download installer and manual archive also include the app on Apple Silicon. Stop running Mac VMs before upgrading or replacing the app.

To develop Barn itself, see Build the Mac component.

Create your first machine

barn mac up

When no macOS base image exists yet, barn mac up displays the download and disk requirements before asking for confirmation:

info: macOS 27.0 (26A428) is not prepared on this Mac yet.
download:  24.8 GiB from Apple (updates.cdn-apple.com)
then:      install macOS once into a reusable base (about 20 minutes)
free:      551.2 GiB
Download macOS from Apple now? [Y/n]

Once you confirm, Barn completes three steps:

  1. Downloads the restore image from Apple (updates.cdn-apple.com), resuming any partial transfer and verifying Apple’s published SHA-256 digest.
  2. Installs macOS once into an unbooted base image, temporarily using one of the two macOS VM slots during installation.
  3. Clones mac1 from the base image using APFS copy-on-write, boots the guest, configures your user account, and waits until SSH and passwordless sudo are ready.
NAME  ADDRESS      STATE    MESSAGE
mac1  10.10.20.10  running  created and ready; macOS 27.0 (26A428), user alice
ready: barn mac ssh mac1
desktop: barn mac open mac1

Subsequent VMs clone the prepared base image in seconds without repeating the IPSW restore. Barn pins initial setup to macOS 27.0 (26A428); use barn mac image update when you want to adopt a newer macOS 27 build.

If you already have a local copy of Apple’s restore image, pass --ipsw to skip the download (Barn clones it without copying if it resides on the same APFS volume):

barn mac up --ipsw ~/Downloads/UniversalMac_27.0_26A428_Restore.ipsw

In non-interactive scripts, pass --yes to authorize the 25 GiB download, or run barn mac setup ahead of time to prepare the base image without creating a VM. If you start a new VM with --no-wait (which returns as soon as the VM process starts), run barn mac up NAME without --no-wait to finish SSH and sudo provisioning before calling ssh or exec.

Work in the machine

Shell and commands

barn mac ssh                                  # interactive shell
barn mac exec -- sw_vers                      # single program execution
barn mac ssh -- 'id; sudo -n true && echo sudo works'
ProductName:		macOS
ProductVersion:		27.0
BuildVersion:		26A428

By default, the guest administrator account matches your host macOS username (override with --user at creation time). Place -- before the remote command: ssh passes a command string to the guest shell, while exec passes the program and arguments directly (use sh -c for shell pipelines or redirection; see the SSH/exec contract). Both return the remote command’s exit status, and --json captures stdout, stderr, and exit_code:

barn --json mac exec -- sh -c 'echo out; exit 3'
{
  "command": "exec",
  "node": "mac1",
  "host": "10.10.20.10",
  "arguments": ["sh", "-c", "echo out; exit 3"],
  "success": false,
  "exit_code": 3,
  "stdout": "out\n"
}

See the Mac command reference for the complete JSON schema.

Desktop

barn mac open

barn mac open launches a native window that resizes the guest display dynamically (use View → Enter Full Screen for full-screen mode). Closing the window leaves the VM running in the background; run barn mac open again to reopen it, or to boot a stopped VM and open its display.

While the guest window has focus, keyboard shortcuts are sent to the guest OS, so use the macOS menu bar for host actions:

Menu item Action
Machine → Share Clipboard Toggle plain-text clipboard sync for the current session
Machine → Restart… Restart macOS inside the guest
Machine → Shut Down… Perform a clean guest shutdown (equivalent to barn mac stop)
Window → Keep Running in Background Hide the desktop window while keeping the VM running
Barn → Quit Barn… Choose whether to keep the VM running in the background or shut it down

When you log out of or shut down the host Mac, Barn requests a clean guest shutdown and powers off the VM if it does not stop before the timeout.

Each VM also generates a random password for the macOS lock screen and GUI administrator prompts. Copy it directly to your host clipboard without printing it to the terminal:

barn mac password --copy

Clipboard

Clipboard sharing syncs plain text across focus changes over the VM’s SSH channel without installing extra guest agents: focusing the guest window syncs text copied on the host Mac, and switching back to a host app syncs text copied inside the guest. Concealed items from password managers, images, and files are never transferred.

To disable clipboard sharing persistently for a VM, run barn mac configure mac1 --clipboard off (applied on the next start).

Shared folders

Pass one or more --share flags when creating a VM to mount host directories inside the guest under /Volumes/My Shared Files/<name>:

barn mac up dev --cpu 8 --memory 16G --share ~/src --share docs=~/Documents:ro
barn mac exec dev -- ls "/Volumes/My Shared Files"

The share name defaults to the directory’s basename, and appending :ro mounts it read-only. Each shared path must resolve to an existing directory (symlinks are supported); Barn never creates or deletes shared host directories. To add or remove shares on an existing VM, stop it first and run barn mac configure:

barn mac stop dev
barn mac configure dev --share data=/Volumes/Work/data --unshare docs
barn mac start dev

Because VirtioFS on macOS guests can briefly cache file contents after host-side edits, use scp or rsync to copy files onto the guest disk when a build requires immediate byte-level consistency.

SSH from other tools

When the first Mac VM becomes ready, Barn adds a marked Include line to ~/.ssh/config so ssh mac1, scp, rsync, and IDE Remote-SSH extensions work out of the box using each VM’s dedicated key and pinned host key:

ssh mac1 'uptime'
rsync -a ./project/ mac1:project/
barn mac ssh-config              # print the generated SSH config entries
barn mac ssh-config --remove     # remove Barn's Include and disable auto-install

If ~/.ssh/config is a symlink managed by a dotfile tool, Barn leaves the file untouched and prints the Include line for you to add. Re-enable automatic integration after --remove with barn mac ssh-config --install. If your personal ~/.ssh/config already defines a Host alias with the same name as a VM, Barn preserves your alias and writes its managed entry by IP address only.

Multiple machines

Assign each VM a lowercase alphanumeric name (starting with a letter, with optional internal hyphens). Creation flags (--cpu, --memory, --disk, --user, --share) apply only when creating a new VM:

barn mac up dev --cpu 8 --memory 16G
barn mac ls
NAME  ADDRESS      STATE    SSH    USER   OS          CPU  MEMORY  DISK                 SHARED
dev   10.10.21.10  running  ready  alice  macOS 27.0  8    16 GiB  504.0 MiB / 100 GiB  src,data
mac1  10.10.20.10  running  ready  alice  macOS 27.0  4    8 GiB   4.7 GiB / 100 GiB
limit: 2 of 2 macOS VMs are running; stop one before starting another

When you omit the VM name, commands target the sole existing VM or mac1 (and ask you to disambiguate when multiple VMs exist). If you request a --disk size that differs from the prepared base image’s capacity, Barn installs a new base image at that capacity from the restore image first.

Each VM gets its own /24 private subnet starting at 10.10.20.0/24 (guest IP .10), automatically skipping subnets that overlap with your LAN, VPNs, the Linux lab, or other Mac VMs. If two Mac VMs are already running, starting a third fails immediately and suggests a VM to stop:

barn mac up build --user ci
error: dev and mac1 are running; macOS allows 2 macOS virtual machines at a time
next: barn mac stop mac1

Everyday lifecycle

barn mac stop dev               # clean guest shutdown (powers off after 2m timeout)
barn mac start dev              # boot and wait for SSH
barn mac restart dev            # stop then start, applying pending configure changes
barn mac stop --all             # stop all running Mac VMs

Use barn mac stop --force for an immediate power-off, or barn mac start --recovery to boot into macOS Recovery with the desktop window open.

barn mac up never mutates an existing VM’s specification—passing changed creation flags on an existing VM returns a hint pointing to barn mac configure:

error: dev already exists, so --cpu would not apply; its configuration and data were preserved
next: barn mac configure dev --cpu 4

Stop the VM before changing CPUs, memory, shared folders, or subnet with configure (clipboard settings can be toggled while running):

barn mac stop dev
barn mac configure dev --cpu 6 --memory 12G --subnet auto
barn mac start dev

To reset a VM’s disk to a clean clone of the base image while preserving its name, account, hardware settings, shares, and IP, run barn mac recreate. To delete the VM completely, run barn mac destroy. Both commands ask for typed confirmation on a terminal; pass --yes in non-interactive scripts:

barn mac recreate dev
barn mac destroy dev
Operation Guest disk and installed apps Hardware settings, IP, and account
stop / start, restart, repeated up Preserved Preserved
configure Preserved Updated as requested
recreate Replaced with a fresh macOS clone Preserved (generates new login password and SSH keys)
destroy Deleted Deleted (base image is retained)

macOS versions and disk space

barn mac image ls
KIND  OS          BUILD   STATE  ON DISK   CAPACITY  USED BY
base  macOS 27.0  26A428  ready  26.7 GiB  100 GiB   mac1,default

Run barn mac image update to query Apple for the newest supported macOS 27 build, download its IPSW, and prepare a new default base image for future VMs. Existing VMs stay on their current build until you run barn mac recreate NAME --update (if a VM uses a custom disk size such as 200G, prepare the matching base image first with barn mac setup --disk 200G).

Use barn mac image prune to inspect unused non-default base images, adding --installers to include downloaded .ipsw files and --yes to delete them:

barn mac image prune --installers         # preview reclaimable images
barn mac image prune --installers --yes   # delete them

Because APFS clones share physical storage blocks with the base image, ON DISK and per-VM disk figures overlap and do not sum linearly.

Troubleshooting

Run barn mac doctor to check host compatibility, the native Barn.app bundle, base images, and every VM, and use barn mac logs [name] to inspect startup, network, and Virtualization framework logs.

Symptom Resolution
network … overlaps route … on start A VPN or new host route now overlaps the VM’s subnet. Run barn mac configure NAME --subnet auto.
macOS allows 2 macOS virtual machines at a time Stop one of the running Mac VMs (barn mac stop NAME) or quit another tool’s macOS VM.
Third-party SSH client reports No route to host for ssh mac1 Grant Local Network access to your terminal/IDE in System Settings → Privacy & Security → Local Network, or use /usr/bin/ssh (barn mac ssh and exec use Apple’s system binary).
mac_runner_missing or mac_runner_protocol Keep barn, barn-hosts-helper, barn-mac-runner, and Barn.app from the same make mac-build output directory together.
Starting a VM fails from a remote SSH session Run barn mac inside a terminal attached to the Mac’s logged-in graphical desktop session with the login keychain unlocked.

Apple Account sign-in inside macOS guests is not reliably supported by Apple’s Virtualization framework, and USB passthrough, snapshots, and VM suspend/resume are not supported.

Clean up

barn mac ls                                 # list existing Mac VMs
barn mac destroy mac1 dev                   # delete selected VMs
barn mac image prune --installers --yes     # delete cached IPSWs and unused base images

Destroying the last Mac VM automatically cleans up its ~/.ssh/config entries while keeping the default base image. For a complete removal of all Mac VM state:

  1. Destroy all Mac VMs (barn mac destroy ...) and run barn mac ssh-config --remove.
  2. Delete $BARN_HOME/mac (default ~/.barn/mac).
  3. Optionally remove ~/Library/Preferences/com.pgsty.barn.plist (io.pgsty.barn.mac-runner.plist in 0.9.0), which stores desktop window geometry.

1.4 - Daily Operations

The normal lifecycle for the one deployment: inspect, access, scale, change, stop, and destroy.

This guide covers day-to-day Linux lab management. For macOS guests, see the macOS VM guide.

Inspect and access

barn status
barn ssh meta
barn exec node-1 -- hostname
barn logs meta --source serial

These commands read the active deployment under BARN_HOME (default ~/.barn) and work from any directory when that path points to the same location. If you set BARN_HOME yourself, use an absolute path across working directories. barn status lists each node’s image and resource allocation (including any software-emulated tcg notice), while barn status --verbose adds guest architecture, accelerator, loopback SSH port, and QEMU PID.

When you run barn up (including on a subset of nodes), Barn refreshes the ~/.ssh/barn_config aliases for the entire applied deployment so ssh meta and peer aliases stay current. You can also regenerate those aliases at any time with barn ssh-config --install.

Commands that reconcile configuration—plan, up, reload, resize, and recreate—locate the inventory in this order:

  1. An explicit -f /path/to/inventory.yml flag (required when both barn.yml and pigsty.yml exist in the same directory and you want pigsty.yml).
  2. The first matching file in the current working directory (barn.yml, barn.yaml, pigsty.yml, then pigsty.yaml; parent directories are never searched).
  3. The previously applied deployment specification in BARN_HOME (except barn validate, which always requires a file).

Re-running barn up retries any unfinished guest setup stages and updates guest helper scripts in place without restarting healthy VMs. Any remaining guest limitations or automatic test-disk repairs are surfaced in the terminal summary and under nodes[].warnings / nodes[].repairs in JSON/YAML output (see Data disks).

Stop and start

barn stop
barn start
barn restart node-1
barn reload -f barn.yml       # validate inventory, stop selected nodes, and run up

barn start boots stopped VMs, re-verifies running ones, and updates SSH aliases if any automatic loopback port was reassigned. barn reload validates the inventory and startup dependencies before stopping selected nodes, then runs barn up for that selection.

reload is a controlled stop-and-boot cycle. It can add new nodes and start stopped nodes, but it refuses existing-node changes to CPU, memory, image, disks, shares, addresses, or other VM definitions before stopping anything.

For CPU or memory changes, edit vm_cpu or vm_mem and run barn resize. Supported increases can use hotplug; decreases and unsupported backends use a stop/start fallback while preserving VM identity and disks. Barn 1.2.0 also supports in-place root growth with resize -D. Image, data-disk, network, and share changes require recreate.

If online resize is interrupted after hotplugging, barn status reports that live CPU and memory may differ from the applied configuration (reason: resize_incomplete). Run barn resize NODE again, or run barn restart NODE to return to the committed resources. Offline resize holds the deployment lock while stopping and updating the node.

Passing --no-wait to a start command returns as soon as QEMU starts, skipping readiness checks, guest recovery, and /etc/hosts or control-node SSH updates until your next barn up.

Change the deployment

barn plan
barn up                         # create/start selected nodes and refresh SSH aliases
barn resize node-1              # apply vm_cpu/vm_mem changes without recreation
barn recreate node-1            # rebuild a node whose VM specification changed

barn plan compares your inventory against the applied state and reports image selections, aggregate resources, drift reasons, and disk impact:

Detected change Meaning How to apply
create Node is in the inventory but not yet provisioned barn up
resize Node CPU or memory changed barn resize <node>
recreate Node image, disk, share, or other VM specification changed barn recreate <node>
missing Provisioned node was removed from the inventory YAML Restore the YAML entry, or run barn destroy <node>

In JSON output, start, create, resize, recreate, and missing are node lists. The top-level action summarizes the plan as one of create, start, converge, resize, recreate, blocked-removal, repair, or none; blocked, when present, is an explanation string. whole_deployment: true marks a deployment-wide recreation; follow the suggested command so that new nodes are included alongside existing ones.

Editing or deleting YAML entries never destroys a VM automatically, and changes to service-level Pigsty variables that Barn does not consume report action: none. Interactive recreate and destroy prompt you to type the command name to confirm; pass --yes (or --force) in non-interactive scripts.

recreate is the explicit replacement path. Barn validates the new image, network, shares, and persistent-disk attachment contract first, then destroys the selected VM artifacts and boots a fresh VM from the desired inventory. Root and non-persistent data disks are replaced; compatible persistent disks remain attached. The replacement gets a new VM identity and refreshes SSH host-key state.

Without node names, recreate replaces every node in the deployment and lists them in the confirmation. It refuses an inventory that no longer lists an existing node before any VM stops (exit 4, reason: nodes_removed): remove that node explicitly with barn destroy <node> or add it back to the inventory. Barn checks the scope again under the destruction lock and reports deployment_changed if the applied state changed during preflight.

Apply changes to selected nodes

You can edit several nodes in the inventory and apply them in batches:

barn plan node-1 -f barn.yml
barn recreate node-1 -f barn.yml
barn plan node-2 -f barn.yml

plan, up, reload, recreate, and resize with explicit selectors retain every unselected peer’s applied definition, including peers changed or temporarily omitted in YAML. Images for uncreated, unselected nodes are resolved when you select those nodes. The entire inventory must still parse correctly, and selected changes must not conflict with applied addresses, aliases, control-node roles, or deployment-wide settings.

Without selectors, Barn evaluates the whole inventory: CPU and memory changes use resize, other changed existing VMs require recreate, and missing VMs require restoring their definitions or explicitly destroying them.

Concurrent commands

For parallel node work within one command, Barn 1.2.0 adds --parallel/-p with a CPU-based default; see Lifecycle concurrency for the supported commands and the difference from Barn 1.1.0.

Mutating commands (up, start, stop, restart, reload, resize, recreate, destroy, purge, and provision) acquire the deployment lock in BARN_HOME, waiting up to 10 minutes (provision --timeout can impose a shorter deadline) if another operation is in flight. While waiting, Barn prints the lock holder’s command, PID, and start time; if the wait times out, it exits with code 4 (error: conflict, reason: deployment_busy). The lock releases automatically when the holding process exits—never delete lock files manually.

Read-only and access commands (status, ssh, exec, ssh-config, and the state read in hosts) read the last published state without waiting on the lock. If a mutating command is currently running, barn status includes a note indicating the active operation. See Troubleshooting for interrupted runs.

Destroy

Pick the teardown command that matches the scope you want to remove:

Single node
barn destroy node-3
Keep persistent disks
barn destroy
Delete persistent disks
barn destroy --delete-persistent
Purge lab & keys
barn purge
  • barn destroy <node> deletes only the named node and refreshes SSH aliases for the remaining nodes. If it removes the control node, the first remaining node becomes the control node, so ssh-config, hosts, exec, and provision keep working; set admin_ip in the inventory to that node’s address (and remove the destroyed entry unless you want up to create it again), then run barn up to install its control SSH access.
  • barn destroy removes all VMs along with their root overlays and non-persistent data disks, the deployment state, and the SSH aliases Barn installed, while retaining persistent disks, deployment keys, cached images, and host networking. Run barn up again to build a fresh lab from the inventory.
  • barn destroy --delete-persistent and barn destroy --purge apply to the entire deployment (they cannot be combined with node names). --purge also removes persistent disks, SSH keys, and deployment state.
  • barn purge immediately removes the entire Linux lab, verified retained persistent disks, keys, and deployment state; the image cache and host network remain. To review the node list and deletion scope first, use barn purge --ask: y/yes confirms; n/no, an empty line, or EOF cancels. It accepts no --yes, -y, or --force.

To remove the shared Linux host network after all VMs are gone:

barn network uninstall --yes

See Image Repositories for image cache management, or Uninstall and Clean Up for a full host teardown.

1.5 - Storage, Files, and Service Access

Configure test data disks, understand retention, transfer files, and reach guest services through OpenSSH.

This guide covers disk configuration, file transfer, and service access for Linux labs. It builds on the Linux Quick Start using node meta at 10.10.10.10.

Configure disks before creating the VM

By default, every Linux node gets a 64 GiB root overlay disk and one 128 GiB non-persistent data disk mounted at /data. vm_disk sets the root disk capacity in GiB (as an integer like 64 or with a unit like 64GiB), while vm_disks replaces the default data-disk list (vm_disks: [] disables extra data disks).

For a new single-node lab, save the following inventory as storage.yml:

storage.yml
all:
  vars:
    admin_ip: 10.10.10.10
    vm_image: u24@20261017.0.1
  children:
    nodes:
      hosts:
        10.10.10.10:
          nodename: meta
          vm_cpu: 2
          vm_mem: 4096
          vm_disk: 64
          vm_disks:
            - {path: /data, size: 64, fs: auto, persistent: true}
            - {path: /scratch, size: 32, fs: ext4, persistent: false}

If meta already exists with a different disk layout, barn up reports drift; inspect barn plan and back up any needed files before running barn recreate meta (which replaces root and non-persistent disks).

Validate the inventory, provision the VM, and verify the mounted filesystems:

barn validate -f storage.yml
barn plan -f storage.yml
barn up -f storage.yml
barn exec meta -- findmnt /data
barn exec meta -- findmnt /scratch
barn exec meta -- df -h / /data /scratch

Disk sizes (size: 64 or size: 64GiB) are thin-provisioned virtual capacities that consume host space as data is written. With fs: auto, Barn formats new disks as XFS when mkfs.xfs is available in the guest and falls back to ext4 otherwise. Check barn up warnings and findmnt to confirm that every data disk mounted cleanly.

What survives each operation

Operation Root and non-persistent disks Persistent disks (persistent: true)
stop then start, or restart Retained Retained
Repeated up on a healthy node Retained Retained
Compatible recreate Replaced Retained and reattached
Ordinary destroy Deleted Retained
Whole-deployment destroy --delete-persistent or purge Deleted Deleted (including retained disks)

Barn matches retained persistent disks by disk identity and specification: to reattach a retained disk across recreate or after destroy, keep the node name, mount path, size, and fs type identical. Barn rejects mismatched retained disks rather than attempting in-place resizing, renaming, or filesystem conversion.

Warning

Persistent disks are still disposable test storage. persistent: true preserves the disk across ordinary destroy and recreate runs, but during guest recovery barn up still resets an ext4 or XFS data filesystem when the filesystem checker reports recognized corruption (reporting the reset in nodes[].repairs). A disk with a partition table, a signature without a UUID, unsupported filesystem features, or an unknown checker failure keeps its data and reports a warning, and transient device absences, failed probes, busy mounts, and host I/O errors never trigger formatting. Keep backups of valuable data on the host.

Newly formatted data filesystems are owned by root. You can verify that data written to /data persists across a stop and start cycle:

barn exec meta -- sudo -n sh -c \
  'printf "retention check\n" > /data/barn-retention.txt'
barn stop meta
barn start meta
barn exec meta -- cat /data/barn-retention.txt

For macOS guest folder sharing, see the macOS VM guide.

Copy files over managed SSH

Export a standalone OpenSSH configuration from the running deployment to use ssh, scp, or rsync directly:

barn ssh-config > barn-ssh.conf
ssh -F ./barn-ssh.conf meta hostname
scp -F ./barn-ssh.conf ./storage.yml meta:/tmp/storage.yml
scp -F ./barn-ssh.conf meta:/data/barn-retention.txt ./barn-retention.txt

The exported fragment points to the current loopback SSH port, deployment key path, and instance-scoped known_hosts file without embedding private key material. Regenerate barn-ssh.conf if you recreate a node or its loopback port changes, or run barn ssh-config --install so ssh meta and scp work without -F.

Reach services inside the guest

When a service inside the guest listens on the node’s fixed private IP (for example, PostgreSQL on 10.10.10.10:5432) and the guest firewall permits it, your host and peer nodes can connect to that IP directly.

If a service binds only to 127.0.0.1 inside the guest, forward a local host port over SSH:

ssh -F ./barn-ssh.conf -N \
  -L 127.0.0.1:15432:127.0.0.1:5432 meta

While that command remains running, connect local clients on the host to 127.0.0.1:15432 (press Ctrl-C to close the tunnel).

SSH aliases (ssh meta) apply only to OpenSSH tools. If you also want browsers, psql, or other host applications to resolve node names and vm_alias entries via /etc/hosts, preview and apply the optional hosts integration:

barn hosts install --json
barn hosts install --yes

barn hosts install --yes updates /etc/hosts on the local host (requesting sudo when needed); re-run it after adding nodes or changing IPs.

Barn inventories do not use vm_ports or vm_forwards—connect via the fixed-IP network or OpenSSH port forwarding. Because management SSH runs over an independent loopback port, barn ssh and -L tunnels remain usable even if the fixed-IP bridge reports a limitation.

Host directory sharing

Linux and macOS hosts can share directories with Linux guests through QEMU Virtio-9p. Set vm_shares under all.vars or a host entry before the first barn up:

barn.yml
vm_shares:
  - host: ./src
    guest: /workspace
    readonly: true

host accepts paths relative to the inventory’s real file, ~/, and directory symlinks. The directory must exist, be accessible, and not overlap BARN_HOME; it need not belong to the Barn user. Multiple guests may write the same tree, with writes coordinated by the user.

Host QEMU and the guest kernel must support 9p. Writable shares also depend on guest permissions; failed writable mounts fall back to read-only with a warning without changing host ownership. After changing existing shares, run barn recreate <node>. Native macOS guests use VirtioFS through barn mac up --share or barn mac configure --share.

See Linux Configuration for the complete disk and share schema.

1.6 - Image Repositories

Choose guest images, use a mirror, import a local qcow2, and prune the cache.

This guide covers Linux guest image selection, mirrors, static repositories, and cache management.

With the default inventory, barn up automatically resolves u24:stable for your host architecture and downloads the matching verified qcow2 image on demand. Barn starts with the signed image catalog embedded in the binary (revision 2026110502, 17 artifacts across 9 distribution families); run barn update whenever you want to fetch, verify, and activate the latest catalog from the repository (or barn image sync to load an exact catalog URL or local file).

Choose an image

Inspect the available distribution families and channels:

barn image list
barn image info u24
barn image info u24:stable

Barn provides nine built-in distribution families: el7, el8, el9, el10, d12, d13, u22, u24, and u26. A bare family name such as el9 selects the stable channel, name:channel selects an explicit channel, and name@version pins an exact immutable build. You can also set a numeric prefix in vm_version to pick the newest matching release along dot boundaries:

barn.yml
all:
  vars:
    vm_image: el9
    vm_version: "9.8"

Here "9.8" selects the newest 9.8.x build in the active catalog, while "9" selects the newest 9.x build (quote numeric vm_version values in YAML so they remain strings). Do not combine vm_version with a :channel or @version suffix in vm_image.

For strict reproducibility across catalog updates, pin the full version string reported by barn image info:

barn.yml
all:
  vars:
    vm_image: d13@20261103.2651.1

After changing vm_image or vm_version, preview the change with barn plan. Existing nodes keep their current root disk until you run barn recreate <node>; updating the catalog with barn update only affects newly created or recreated nodes.

Temporary overrides and architecture

Use -I (--image) and -A (--arch) to override image and architecture settings for one command invocation.

Guests default to the host architecture unless explicitly overridden by vm_arch or --arch/-A. Precedence is -A → vm_arch → host architecture. -A native overrides a fixed inventory architecture and follows the host again. amd64 accepts x86_64 and x64 aliases; arm64 accepts aarch64 and a64.

barn plan -I u24 -A amd64
barn up -I u24 -A amd64

-I u24 replaces selected nodes’ vm_image and vm_version without changing the architecture. These overrides leave the inventory intact. To keep them, set vm_image and all.vars.vm_arch, removing superseded vm_version, or generate a new inventory with barn init -I u24 -A amd64. Existing nodes still require recreate to change image or architecture; architecture changes must include all existing nodes. See the configuration reference.

barn image list displays all architectures. Standalone info and pull default to the host architecture without reading inventory vm_arch; use --arch/-A to override it (legacy -a also works). A missing architecture variant is an error, not a reason to select another architecture. Barn currently accepts only amd64 and arm64 guests:

  • el7 is supported only on native linux/amd64 hosts.
  • el8 on arm64 automatically runs under QEMU TCG software emulation on Apple Silicon because the stock RHEL/Rocky 8 arm64 kernel requires a 64 KiB page granule that macOS HVF does not support. See Runtime rules for foreign-architecture emulator requirements.
Note

All image versions in the embedded catalog carry supported status except the EOL el7 compatibility build, which is marked deprecated.

Use a mirror

Release builds default to https://repo.pigsty.io/barn. Pass --mirror to use the China mirror (https://repo.pigsty.cc/barn), or --repo to point at a custom repository URL or local directory:

barn image pull u24 --mirror
barn up --mirror
barn update --repo https://mirror.example/barn
barn image pull u24 --repo https://mirror.example/barn
barn up --repo https://mirror.example/barn

To set a default repository for your current shell session, export BARN_REPO:

export BARN_REPO=https://mirror.example/barn
barn update
barn up

Repository selection follows the precedence --repo → --mirror → BARN_REPO → global default (https://repo.pigsty.io/barn). Both official repositories and any plain http:// repository require a valid Minisign signature (catalog.json.minisig); explicit local directory paths and custom https:// endpoints may use an unsigned catalog.json. Every image download is verified against the catalog’s byte size, SHA-256 digest, and qemu-img structural checks.

When downloading from either official repository, Barn automatically retries transient network errors, resumes partial transfers, and falls back to the peer official mirror if an artifact is unreachable. Custom --repo endpoints never fall back to external servers.

Each repository root maintains its own active catalog state under BARN_HOME. When you point --repo at a custom repository that defines new image entries, run barn update --repo <root> first to activate its catalog before running plan or up.

Build a static repository

A Barn image repository is a plain directory that you can serve over static HTTP/HTTPS, copy with rsync, or read directly from a local path:

/srv/barn

  • barn/
    • repo.yamlHuman-maintained image definitions and channels
    • catalog.jsonGenerated catalog with SHA-256 and file sizes
    • catalog.json.minisigMinisign signature (required for official and HTTP repos)
    • images/
      • d13-1-arm64.qcow2Verified cloud-init qcow2 base image

repo.yaml is the sole human-edited file. For the single arm64 image above, create /srv/barn/repo.yaml:

/srv/barn/repo.yaml
schema: 1
revision: 1
defaults: { image: d13, channel: stable, arch: native, boot: uefi }
images:
  d13:
    channels: { stable: "1" }
    versions:
      "1":
        status: testing
        variants:
          arm64:
            source_user: debian

Place your cloud-init-capable qcow2 image at /srv/barn/images/d13-1-arm64.qcow2 (relative roots, directory symlinks, and shared writable directories are supported), then generate and verify catalog.json on a host with qemu-img installed:

barn repo scan /srv/barn
barn repo build /srv/barn
barn repo verify /srv/barn

barn repo scan inspects the directory layout read-only, while barn repo build runs qemu-img check and writes catalog.json with exact filenames, SHA-256 digests, compressed sizes, and virtual capacities. Increment revision whenever you update repo.yaml or add images, and upload immutable .qcow2 files before publishing the updated catalog.json (and .minisig signature).

Activate and test the local repository:

barn update --repo /srv/barn
barn image info d13 --arch arm64 --repo /srv/barn
barn image pull d13 --arch arm64 --repo /srv/barn

Pass --repo /srv/barn (or export BARN_REPO=/srv/barn) to plan, up, and recreate, and set vm_image: d13@1 in your inventory. To revert /srv/barn back to the binary’s embedded catalog while keeping its anti-rollback high-water mark, run barn image reset --repo /srv/barn.

Import a local image and prune the cache

To use a single custom qcow2 image without creating a repository, import it into Barn’s local cache with a local- alias. The recorded architecture defaults to the host:

barn image import --name local-mybase --boot uefi \
  --source-user ubuntu --sha256 <digest> /path/to/base.qcow2

--name (must start with local-), --boot (uefi or bios), and --source-user are required together when registering an alias; omitting them caches the verified qcow2 bytes by content digest without creating an alias. The image must already include cloud-init support for Barn’s first-boot provisioning. Named local-* aliases are immutable—register a new local-* name whenever you rebuild the image, and reference it in barn.yml with vm_image: local-mybase.

Named imports accept -A, for example barn image import /path/to/arm64.qcow2 --name local-arm64 --boot uefi --source-user ubuntu -A arm64. This records your declaration; it does not convert the image or detect its guest CPU. Set inventory vm_arch or the lifecycle command’s -A to match when using it. -A requires --name on import.

To reclaim disk space from unreferenced images and interrupted download staging files:

barn image prune --dry-run
barn image prune --yes

barn image prune (which requires qemu-img) preserves all images listed in the selected repository’s active catalog, all base images used by existing nodes in BARN_HOME, and all registered local-* aliases.

See Linux Images for catalog signature rules, anti-rollback protection, and cache layout, or Image Pipeline for building new base images from upstream cloud releases.

1.7 - Automation and Guest Scripts

Prepare an unattended lab, check JSON results, and run repeatable scripts inside selected guests.

This guide shows how to drive Barn from unattended scripts and CI pipelines. Run all commands as the Unix user who owns the deployment, and pass the same -f <inventory> and BARN_HOME across invocations.

Prepare a predictable lab

Keep your inventory file in version control and pin vm_image to an exact version (such as vm_image: u24@20261017.0.1) so catalog updates never alter your base image mid-pipeline. Passing -f explicitly also prevents first-run setup from relocating an unedited default template to a different subnet. If you use a custom image repository, activate its catalog first with barn update --repo URL and pass the same --repo to setup and up.

Generate and validate the inventory before running host setup:

mkdir -p ~/barn-lab
cd ~/barn-lab
barn init dual
# Edit barn.yml to pin vm_image and node settings.
barn version
barn validate -f barn.yml
barn plan -f barn.yml
barn setup -f barn.yml --dry-run

Once you have reviewed the host plan and ensured non-interactive sudo credentials (or a NOPASSWD policy) are available, prepare the host and bring up the lab:

barn setup -f barn.yml --yes
barn up -f barn.yml --json > up.json

For a generated inventory, barn init [template] --image <reference> writes all.vars.vm_image, so every node inherits the same image. References can be catalog aliases (el9), channels (u24:stable), or version selectors (u24@20261017.0.1). barn setup [template] --image <reference> also selects an image during generation, but rejects this generation flag when using an existing inventory.

--image/-I and --arch/-A share consistent spellings: barn init full -I u24 -A amd64 saves both choices in a new inventory; the same flags on validate, plan, up, reload, recreate, and resize only override desired state for that invocation. For example, preview barn plan -f barn.yml -I u24 -A arm64 --json, then use the same overrides when applying it. Setup’s -I/-A remain limited to template generation.

The same commands also accept --cpu/-C, --mem/-M, --scale/-S, and --disk/-D. Each specification follows CLI flags → nonempty BARN_* environment variables → inventory → defaults. Reuse the same flags and environment for validate, plan, and the operation that applies the plan.

Without an architecture flag, Barn uses BARN_ARCH, then inventory vm_arch, falling back to the host when unset; -A native overrides both and follows the host. Existing VMs require recreate to change image or architecture. For repeatable CI, save vm_image, vm_arch, and an exact image version; see the configuration reference.

barn setup --yes accepts the host-preparation plan without asking. On a terminal, up, start, restart, and reload show the same plan and ask once before installing packages or the Barn network; without a terminal they only report what is missing, so run barn setup --yes before them.

Validate JSON output alongside the exit code

Barn writes structured JSON/YAML payloads to stdout and human diagnostics to stderr. Because barn up returns exit code 0 when management SSH is ready—even if an optional stage such as a secondary data disk, Virtio-9p share, or peer SSH config reported a limitation (nodes[].warnings) or reformatted a damaged test disk (nodes[].repairs)—automation scripts should inspect the JSON payload alongside the exit code:

up-check.sh
#!/usr/bin/env bash
set -euo pipefail

if barn up -f barn.yml --json > up.json 2> up.stderr; then
  jq -e '
    (.nodes | type == "array" and length > 0) and
    all(.nodes[];
      .state == "running" and .ready == true and
      ((.warnings // []) | length == 0) and
      ((.repairs // []) | length == 0)) and
    ((.warnings // []) | length == 0)
  ' up.json || exit 1
else
  barn_exit=$?
  cat up.stderr >&2
  cat up.json
  exit "$barn_exit"
fi

When a subsequent pipeline step requires ready guests, omit --no-wait on barn up (barn status --json reads cached state without re-probing guest SSH).

Exit codes and command-specific payloads

Pair CLI exit codes with each command’s JSON schema:

  • Exit 2 (usage): Missing inventory on first run, invalid flags, unknown image aliases, or destroy/recreate without --yes when stdin is not a terminal (purge runs without asking).
  • Exit 4 (conflict): Inventory drift requiring explicit action (reason: recreate_required or nodes_removed), applied state that changed during a recreate preflight (reason: deployment_changed), or a deployment lock timeout (reason: deployment_busy after waiting up to 10 minutes).
  • Exit 5 (partial): Multi-node up or provision where some nodes succeeded and others failed—inspect nodes[] or results[] before retrying.
  • Exit 7 (integrity): A digest, signature, or identity check failed, including a catalog signature or signed revision that does not verify (reason: catalog_signature or catalog_equivocation).
  • Command-specific reports:
    • barn doctor --json exits 0 when host compute passes even if a class: network check reports an error; check checks[] and barn network status --json before relying on the private bridge.
    • barn ssh and barn exec return the remote command’s exit status (or 255 on OpenSSH transport failure) and record success, exit_code, stdout, and stderr in --json mode.
    • network and hosts install/uninstall without --yes only print the plan and exit 0 when stdin is not a terminal or the output is --json/--yaml; check applied in the result, or pass --yes.

Run single commands or batch scripts

Always place -- between the node selector and the remote command, and put Barn flags (--json, --verbose) before --:

barn exec meta -- hostname
barn exec node-1 -- sh -c 'id; df -h /data'
barn exec meta --json -- uname -a > uname.json

Pass the program and its arguments as separate tokens to barn exec, using sh -c explicitly for pipes, redirection, or shell variables (see SSH passthrough and completion).

To run a health check or setup script across multiple running Linux nodes, save a local Bash script (a regular file or symlink to one, up to 4 MiB):

check-lab.sh
#!/usr/bin/env bash
set -euo pipefail
hostname
id
findmnt /data
test -d /data

Execute it with barn provision:

barn provision --script ./check-lab.sh meta node-1
barn provision --script ./check-lab.sh --parallel 2 --timeout 5m --json > provision.json

Omitting node selectors runs the script on every node in the deployment (all target nodes must already be running). Barn streams the verified script bytes directly to Bash over SSH without writing a temporary script file in the guest, and records the script’s SHA-256 in the audit result.

  • Concurrency and timeouts: Serial by default; --parallel accepts positive integers, with workers capped at the selected node count. --timeout bounds the whole operation, defaults to 1h, and accepts any positive duration. --sudo uses non-interactive sudo -n.
  • Output capture and idempotency: Each node captures up to 1 MiB of stdout and 1 MiB of stderr (setting stdout_truncated / stderr_truncated when exceeded). Because partial runs leave completed guest changes in place, write idempotent scripts that are safe to re-run.
  • Exit codes: provision exits 5 when some nodes succeed and others fail, passes through a single target’s positive non-255 exit code on failure, and exits 1 otherwise.

Pair the lab with Pigsty

Barn and Pigsty share the same Ansible YAML format. While barn init generates VM hardware topology, Pigsty provides complete service inventories (pigsty.yml) for PostgreSQL, Redis, and monitoring stacks. Provision the VMs with Barn first:

barn validate -f pigsty.yml
barn plan -f pigsty.yml
barn up -f pigsty.yml
barn ssh

During barn up, Barn provisions the guest administrator account and installs the deployment private key on the control node designated by admin_ip, configuring its ~/.ssh/config so Ansible can reach all peer nodes over their fixed private IPs (see Security and Trust). Once barn up completes, SSH into the control node and run Pigsty’s deployment playbooks.

1.8 - Troubleshooting

Short, safe runbooks for setup, networking, images, drift, interrupted state, and SSH.

This guide covers Linux lab diagnostics. For macOS guests, run barn mac doctor and see the Mac troubleshooting section.

Start by collecting the three JSON diagnostic reports (barn status also reconciles interrupted process state when no other command holds the lock):

barn doctor --json
barn network status --json
barn status --json

barn doctor exits 0 whenever host compute checks pass, even if a class: network check reports an error—inspect both checks[] and barn network status --json before assuming barn up can attach to the private network.

Download and PATH problems

The release installer downloads binaries from GitHub Releases (--mirror only selects the guest image repository). If your network requires a proxy to reach GitHub, export HTTPS_PROXY or ALL_PROXY in your terminal session (macOS System Settings proxy entries are not inherited by CLI tools automatically).

The user-scoped installer places binaries in ~/.local/bin. If barn is not found or runs an older version after installation, verify your PATH:

export PATH="$HOME/.local/bin:$PATH"
command -v barn
barn version

Always keep barn and its packaged barn-hosts-helper from the same release in their relative directory layout.

No inventory found

When neither an inventory file nor an applied deployment exists, an interactive barn up generates a starter barn.yml. To use an existing file, run plan, up, or validate in the directory containing barn.yml / pigsty.yml or pass -f /path/to/file (or generate a template first with barn init).

Without -f, plan, up, reload, resize, and recreate check the current directory for barn.yml, barn.yaml, pigsty.yml, and pigsty.yaml in that order, falling back to the applied specification in BARN_HOME only when none of those files exist. If the first matching file contains syntax errors, Barn fails immediately rather than falling back; pass -f pigsty.yml when an older barn.yml sits in the same directory.

If barn status reports no deployment state found, the active BARN_HOME has no applied deployment (either fresh or previously purged).

init output paths and symlinks

barn init and barn setup work through directory symlinks, and barn init -o ./lab/config/barn.yml creates missing parent directories. An existing output file requires --force. Barn never writes through an output-file symlink: with --force it reports will not overwrite …: it is a symlink, so pass the link’s target path instead:

barn init -o "$(readlink -f barn.yml)" --force

Setup needs sudo

Before prompting for a password, barn setup prints the exact host mutation it is about to perform and hands the interactive terminal directly to sudo. Passing --yes auto-confirms Barn’s plan prompt, while sudo authentication itself still requires an interactive terminal, cached credentials, or a NOPASSWD sudoers rule. Run barn setup --dry-run to preview the host plan without executing it.

On macOS, barn setup downloads and verifies the pinned socket_vmnet package before requesting sudo, so a network failure never prompts for a password.

Native acceleration or compatibility runtime is unavailable

Barn requires HVF on macOS or KVM on Linux for native hardware acceleration, and uses QEMU TCG software emulation only when you explicitly request a foreign vm_arch or run an image covered by a built-in compatibility rule (such as el8 arm64 on Apple Silicon). It never falls back to TCG after a native HVF/KVM failure.

Homebrew’s qemu formula includes both arm64 and amd64 system emulators, whereas Linux barn setup installs only the host-native emulator—install the matching qemu-system-* binary and UEFI firmware manually before running foreign-architecture guests on Linux.

Network is partial or invalid

When the Barn host network is intact but stopped, an interactive barn up includes the restart in its host-preparation plan and asks once before applying it; without a terminal, run barn setup --yes. If barn network status reports a partial or invalid state, inspect and apply Barn’s managed cleanup plan instead of deleting system network files by hand:

barn network status --json --verbose
barn network uninstall --json
barn network uninstall --yes

If the Linux bridge smoke test fails during setup, Barn rolls back the network changes automatically; only an explicit automatic rollback failed message requires manual cleanup.

On macOS, barn setup automatically repairs a missing or overly restrictive /var/log/barn-vmnet directory (root:wheel 0755), while refusing to touch a symlink, foreign owner, or group/world-writable directory—follow the exact remediation command printed by barn network status.

Linux bridge helper fails

On Debian and Ubuntu hosts, check the ownership and setuid mode on /usr/lib/qemu/qemu-bridge-helper (expected root:<caller-accessible-group> 4750):

id
stat -c '%U:%G %a %n' /usr/lib/qemu/qemu-bridge-helper
dpkg-statoverride --list /usr/lib/qemu/qemu-bridge-helper

Plan reports recreate or missing

  • recreate: The node’s inventory specification changed. Inspect barn plan and back up any data on the root or non-persistent disks before running barn recreate <node> (pass --yes in non-interactive scripts).
  • missing: A provisioned node was removed from the YAML inventory. Either restore its YAML entry or run barn destroy <node> to delete the VM. While a node is missing, barn recreate without node names refuses to run before any VM stops (exit 4, reason: nodes_removed).

With node selectors, barn plan node-1 and barn recreate node-1 retain unselected peers’ applied definitions, so an unrelated peer edit does not have to be rebuilt at the same time. Real conflicts in addresses, aliases, control-node roles, or deployment-wide settings still need to be resolved.

A node did not become ready

Readiness requires a working management SSH connection and a verified guest instance UUID. When a node fails to create, boot, or become reachable over management SSH, barn up reports the failing node and stage and exits with code 5. Inspect its logs:

barn logs <node>                  # guest serial console
barn logs <node> --source qemu    # QEMU stderr and diagnostics
barn logs --source events         # host setup and deployment event log
barn status

Secondary guest stages—data disks, Virtio-9p shares, hostname, /etc/hosts, control-node SSH, and the fixed-IP private interface—run independently after management SSH comes up. If one of those stages fails, barn up exits 0 and records the limitation under nodes[].warnings:

Reported limitation Remediation
Data disk unavailable Resolve the missing device, filesystem tool, busy mount, or host I/O error, then re-run barn up. A disk with a partition table, a signature without a UUID, or unsupported filesystem features keeps its data and stays unmounted
Shared directory is read-only Check host and guest write permissions, then re-run barn up
Guest hosts or control-node SSH incomplete Re-run barn up to refresh the managed files inside the guest
Private interface unavailable Check barn network status, repair the host bridge/vmnet, and re-run barn up

Re-running barn up retries only the unfinished stages without restarting healthy VMs. It initializes blank data disks and resets an ext4 or XFS data filesystem only when the filesystem checker reports recognized corruption (see Data disks). Pass --rollback to barn up if you want failed initial preparation artifacts removed in the same run.

For Linux shared folders, both QEMU and the guest kernel need 9p support. A valid host path cannot make an unsupported guest kernel mount 9p; use SSH/SCP/rsync or a kernel with 9p support. This applies on macOS hosts as well as Linux hosts; native macOS guests use VirtioFS separately.

SSH fails

Check barn status, barn ssh-config, and barn logs <node>.

  • Deployment SSH keys: If the host’s .pub file is missing, Barn derives it from the intact private key on startup. If the private key itself is missing, restore it from backup—Barn never generates a replacement key for existing VMs. If the control node’s copy of the private key is missing, barn up reports a control-ssh warning while host-to-guest management SSH continues to work.
  • Port reassignment and host keys: If another process occupies a stopped VM’s loopback SSH port, the next barn start or barn up picks a free port and updates ~/.ssh/barn_config. SSH host-key verification is scoped to each VM instance UUID, so recreating a node never triggers stale known_hosts errors.
  • Dotfile-managed ~/.ssh/config: If ~/.ssh/config is a symlink or hard link, Barn writes ~/.ssh/barn_config and prints the Include line for you to add. If barn ssh meta works but ssh meta fails, verify that Include line.
  • Remote command syntax: Place -- between the node name and remote command (barn exec meta -- hostname); see SSH passthrough and completion.

Catalog or image verification fails

Barn embeds active and standby Minisign public keys and rejects unknown signers, catalog revision rollbacks, file size or SHA-256 mismatches, and malformed qcow2 files. A catalog signature or signed revision that does not verify exits 7 (error: integrity, reason: catalog_signature or catalog_equivocation); a permission or I/O error while reading a cached signature remains a runtime failure (exit 1). Always pull from a validly signed repository or register local images through barn image import --sha256 ... rather than copying files directly into ~/.barn/images.

A command was killed

Pressing Ctrl-C or sending SIGTERM cancels a command with exit 130 (cancelled). Barn sends SIGTERM to the processes it started, which sudo forwards to privileged commands, and waits at most 10 seconds for them; a descendant that ignores SIGTERM keeps running until you stop it.

After an interruption, run barn status first. If another Barn process currently holds the deployment lock, status displays a note—wait for that operation to finish before intervening.

When no command holds the lock, barn status verifies each recorded QEMU process identity (matching both PID and process start time) and reconciles the state automatically:

Interrupted state Automatic recovery / next step
Host rebooted or QEMU PID was recycled by an unrelated process barn status detects the mismatched process identity and marks the VM stopped; run barn start
barn stop was interrupted while QEMU stayed running barn status restores running state; re-run barn stop if you still want to shut it down
Online barn resize was interrupted after changing the live guest barn status reports resize_incomplete; re-run barn resize <node> or run barn restart <node>
Initial barn up failed during preparation Fix the inventory and re-run barn up -f barn.yml; Barn rolls back journaled unfinished artifacts
barn destroy was interrupted midway Re-run the same barn destroy or barn purge command to finish cleanup

If Barn cannot confirm whether a recorded process belongs to the node (or if a QEMU process is alive but its QMP socket is missing), it blocks further mutations so it never kills an unrelated process. Preserve the state directory and logs (barn logs <node> --source qemu --verbose) rather than deleting PID or lock files by hand.

When filing an issue on GitHub, include the exact command and exit code, barn version, host OS/architecture, QEMU version, and the JSON output of barn doctor, barn network status, and barn status.

1.9 - Build from Source

Build Barn 1.2.0 or the development branch, including the native Mac component, and run the contributor checks.

Build Barn from source when developing a change or auditing a release tag. For prebuilt CLI binaries and the native Barn.app included in the Apple Silicon release, see Install Barn.

Check out the source

Clone tag v1.2.0 to reproduce the documented release (or omit --branch v1.2.0 to work on main), and record git log -1 --oneline and git status --short alongside any build report:

git clone --branch v1.2.0 https://github.com/pgsty/barn.git
cd barn

Tag v1.2.0 reproduces the published release described in the Barn 1.2.0 release notes. Leave BARN_VERSION unset for a development build.

Build the CLI

Barn 1.2.0 pins Go 1.27.4 in go.mod and packaging/toolchain.env, and requires Git, Make, Bash, and standard C/POSIX build tools (compiling the CLI does not require QEMU or host-network setup):

BARN_VERSION=1.2.0 make build
export PATH="$PWD/bin:$PATH"
barn version

make build compiles both barn and barn-hosts-helper into bin/.barn-releases/, embeds the helper’s SHA-256 digest into the CLI, and creates entry-point symlinks under bin/. Always use make build rather than a bare go build ./cmd/barn, and keep bin/.barn-releases/ alongside the bin/ links if you move the build output.

Without BARN_VERSION, development builds report dev, and the commit field marks modified working trees as uncommitted.

To cross-compile the Go CLI and helper for all four supported targets (darwin_amd64, darwin_arm64, linux_amd64, and linux_arm64), run make cross. Keep build artifacts (bin/, dist/, .goreleaser-*/) and lab inventories out of version control; check ignore rules before generating them in the working tree.

Run the contributor checks below against the exact checkout you intend to use. Earlier dated acceptance records are kept in Validation History.

Follow packaging/toolchain.env in the checkout being built. Formal assets use the signed and notarized release procedure.

Build the Mac component

Before rebuilding a local Mac bundle, stop any running Mac VMs that use it.

On an Apple Silicon Mac running macOS 27+ with Xcode 27+ selected for xcrun (providing the macOS 27 SDK and Swift compiler), run:

BARN_VERSION=1.2.0 make mac-build
export PATH="$PWD/bin/mac:$PATH"
barn mac doctor

make mac-build produces a matched bundle under bin/mac (barn, barn-hosts-helper, standalone barn-mac-runner, and Barn.app) as well as bin/barn-mac-darwin-arm64.tar.gz. Run make mac-native-test to execute the Swift component test suite, and follow the macOS VM guide to provision a guest.

Source tag Native app bundle Bundle identifier
v0.9.0 Barn Mac.app io.pgsty.barn.mac-runner
v0.9.1 and later Barn.app com.pgsty.barn

The current CLI discovers both Barn.app and legacy Barn Mac.app bundles, whereas the 0.9.0 CLI only looks for Barn Mac.app—always use the barn CLI and native app built together in bin/mac.

Code signing and notarization

When signing environment variables are unset, make mac-build applies an ad-hoc signature for local development. For signed or notarized builds:

  • Set BARN_CODESIGN_IDENTITY to sign Barn.app with a Developer ID certificate.
  • Set BARN_NOTARY_PROFILE and BARN_MAC_REQUIRE_NOTARIZATION=1 to submit Barn.app for Apple notarization, staple the ticket, and verify Gatekeeper acceptance.
  • With BARN_CODESIGN_IDENTITY set, make mac-build also signs the Go helper before embedding its final SHA-256 in the CLI, then signs the CLI. The Mac release handbook adds joint notarization of the CLI/helper/app and verification of the exported release payload.

Run contributor checks

Running the full make check suite requires Python 3, jq, a C compiler (for -race), and the linter/scanner versions pinned in packaging/toolchain.env:

go install honnef.co/go/tools/cmd/staticcheck@v0.8.2
go install golang.org/x/tools/cmd/deadcode@v0.52.0
go install github.com/golangci/golangci-lint/v2/cmd/golangci-lint@v2.15.0
go install golang.org/x/vuln/cmd/govulncheck@v1.9.0

Ensure GOBIN (or $(go env GOPATH)/bin) is on your PATH, then run:

make test
make check

make check verifies go.mod, shell scripts, repository maintenance rules, unit and race tests, go vet, staticcheck, deadcode, golangci-lint, govulncheck, four-target cross-builds, image-pipeline and installer tests, and dependency licenses. See Contributing and Release Engineering for packaging snapshot checks.

1.10 - Uninstall and Clean Up

Remove Linux and macOS VMs deliberately, then clean up integrations, images, networking, and the application.

Barn manages Linux labs and macOS VMs independently. barn purge removes only the Linux deployment—it does not delete Mac VMs. Keep the barn CLI installed until you have finished removing VMs, integrations, cached images, and host networking.

Choose what to remove

Goal Command
Stop running VMs while keeping disks barn stop; barn mac stop --all for Mac VMs
Delete a single Linux node barn destroy <node>
Delete the Linux lab, keeping persistent disks barn destroy
Delete the Linux lab, persistent disks, and keys barn purge (deletes immediately; --ask enables confirmation)
Delete named Mac VMs barn mac destroy <name...> (prompts for confirmation)

Check barn status (for Linux nodes) and barn mac ls (for Mac VMs), and copy any files you want to keep onto the host before destroying machines.

Full teardown steps

  1. Destroy Linux and Mac virtual machines

    To discard the entire Linux lab—including retained persistent disks and deployment keys—run:

    barn purge

    This deletes immediately. Use barn purge --ask to review the VM list and deletion scope, then enter y/yes to confirm or n/no to cancel. It accepts no --yes, -y, or --force. If you also created macOS VMs, list and destroy them by name:

    barn mac ls
    barn mac destroy mac1 dev
  2. Remove SSH and /etc/hosts integrations

    Whole-deployment Linux destroy / purge removes the default ~/.ssh/barn_config integration automatically. If you installed a custom named SSH fragment, /etc/hosts entries, or Mac SSH aliases, remove them:

    barn ssh-config --remove --name lab
    barn hosts uninstall --json
    barn hosts uninstall --yes
    barn mac ssh-config --remove

    These commands remove only Barn’s managed blocks, leaving your personal ~/.ssh/config and /etc/hosts entries intact.

  3. Prune cached images

    Preview and delete cached Linux and macOS images:

    barn image prune --dry-run
    barn image prune --yes
    barn mac image prune --installers
    barn mac image prune --installers --yes
  4. Uninstall Linux host networking

    Preview and remove the shared Linux host network (it stops while any Linux VM is running, so destroy the lab first; Mac VM networks tear down automatically when the VM stops):

    barn network uninstall --json
    barn network uninstall --yes

    When Barn uses systemd-networkd, the uninstall leaves the global networkd unit state unchanged if other host links exist or exclusive Barn use cannot be established; the plan reports preserve_networkd, and the applied report explains the decision in checks.networkd.

  5. Uninstall the Barn application and remaining state

    If you installed Barn through a package manager, remove the package:

    Homebrew
    brew uninstall barn
    Debian / Ubuntu
    sudo apt remove barn
    RHEL / Fedora
    sudo dnf remove barn
    • User-scoped install.sh: Remove barn, barn-hosts-helper, .barn-current, .barn-releases/, and .barn-install.lock from ~/.local/bin (and remove the PATH entry from your shell rc file).
    • Manual archive or source build: After stopping all Mac guests, remove the extracted release directory, bin/mac, or any copied Barn.app (Barn Mac.app in 0.9.0), plus ~/Library/Preferences/com.pgsty.barn.plist (io.pgsty.barn.mac-runner.plist in 0.9.0).
    • Manually installed hosts helper: If /opt/barn/libexec/barn-hosts-helper was installed manually and no other user on the host uses Barn, remove it after running barn hosts uninstall --yes.
Warning

Delete ~/.barn (or your custom BARN_HOME) only after destroying both Linux and Mac VMs. ~/.barn holds active VM state, keys, persistent disks, and base images for both workflows; if a VM is still running or destroy fails, resolve the error with barn status / barn mac ls before deleting the directory.

2 - Reference

Barn 1.2.0 configuration, command options, output formats, and image repositories.

Use these pages to look up every command, configuration field, output schema, and image catalog rule in Barn 1.2.0. The version table identifies changes since 1.1.0. For task-oriented walkthroughs, start with the Guides.

  • Command Reference — Every Linux and macOS command, complete option tables, defaults, examples, and version availability
  • Linux Configuration — Inventory discovery, variables, defaults, data disks, host shares, and node drift rules
  • CLI Contracts — Global flags, Linux lifecycle commands, structured JSON/YAML output, and exit codes
  • Mac Runtime Reference — barn mac subcommands, VM settings, JSON schemas, error reasons, and runtime files
  • Linux Images — Embedded catalog 2026110502, aliases, repository verification, local imports, and cache pruning
  • Image Pipeline — Contributor tools for validating and offline-normalizing Linux qcow2 candidates

Barn’s public interface is the barn CLI (barn version, barn <command> --help); Go packages under internal/ are internal implementation details. The image tables reflect the catalog embedded in Barn 1.2.0, while barn update can activate newer signed repository metadata without replacing the binary. For release verification and build gates, see Release Engineering.

2.1 - Command Reference

Every Barn command with syntax, all local options, defaults, examples, and version availability.

Find a command below for its arguments, complete local option table, examples, and effects. This reference follows the command-index and command-family organization of PIG. Barn’s CLI contracts describe structured output and exit codes; Configuration defines inventory fields.

Version baseline

This reference covers Barn 1.2.0, released on 2026-11-12 from 90f6158. Its 57 operation commands and 174 local option entries were checked against the CLI help output in both languages.

Label Availability Behavior
1.1.0 Options available in both 1.1.0 and 1.2.0 1.1.0 used 2 vCPUs / 4 GiB by default and accepted CPU/memory resize through the inventory
1.2.0 Added in 1.2.0 Resource flags and environment overrides, vm_scale, root-disk growth, lifecycle --parallel, vm_hosts, and hosts install -f; defaults are now 1 vCPU / 2 GiB

To preserve the previous defaults when upgrading, set vm_cpu: 2 and vm_mem: 4GiB explicitly and inspect barn plan. Existing applied VMs keep their resources until a resource-changing operation is requested. Use barn version and barn COMMAND --help to check an installed build. See the release notes and release validation.

Command index

Task Commands and detailed reference
Prepare setup, init, validate, doctor
Plan and create plan, up
Start and stop start, stop, restart, reload
Change and inspect resize, recreate, status
Delete destroy, purge
Access Linux guests ssh, exec, logs, provision, ssh-config
Publish host names hosts install, hosts uninstall
Refresh catalog update, image sync, image reset
Manage Linux images image list, info, pull, import, prune
Maintain a repository repo scan, build, verify
Host network network status, install, uninstall
Native macOS VMs mac ls, up, start, stop, restart, configure, recreate, destroy
Mac access mac open, ssh, exec, password, ssh-config, logs
Mac preparation mac setup, doctor, image ls, image update, image prune
CLI utilities version, completion, help

Global options

barn [--json|--yaml] [-v|--verbose] COMMAND [arguments] [local options]
Option Meaning
-h, --help Human-readable help for the selected command; exits 0
--json Structured JSON on stdout; progress and diagnostics stay on stderr
--yaml Structured YAML on stdout; mutually exclusive with --json
-v, --verbose Bounded diagnostics on stderr
--version Root-command shortcut to display build identity; use barn version --json for a structured report

Global output options can appear before or after a subcommand, but must precede the remote-command -- separator used by ssh and exec. -f, -I, -A, -p, and --yes are local options: consult the selected command’s table. -f means follow for logs; -n means line count for mac logs. Long boolean options accept =true or =false.

BARN_OUTPUT and BARN_VERBOSE set presentation defaults. BARN_HOME selects private state (default ~/.barn); use an absolute path when changing directories. Linux and Mac machines share that data root but have independent lifecycles. See environment variables and exit codes.

barn and help

barn
barn --help
barn help resize
barn image pull --help
barn --json

With no arguments, text mode prints complete help and exits 0; JSON/YAML mode returns suggested actions[]. Explicit help always prints human-readable text. Groups image, repo, network, hosts, and mac image require a subcommand and exit 2 without one. The mac group is the exception: it defaults to mac ls. It is hidden from root help on Linux and Intel Macs, while explicit Mac help remains available.

Short aliases are scoped to their parent. barn im ls is barn image list; barn mac st is barn mac ls. The per-command pages list each alias. There is no rm alias for purge.

2.1.1 - Preparation and Utilities

Initialize inventories, prepare the host, validate configuration, and inspect the CLI.

Initialize inventories, prepare the host, validate configuration, and inspect the CLI.

This reference covers Barn 1.2.0. Option tables distinguish options also available in 1.1.0 from additions in 1.2.0; see the version baseline. All commands inherit the global options.

barn setup

barn setup [meta|dual|trio|full] [flags]

Prepare host packages, the fixed-IP network, images, and the selected inventory. It does not create the Linux VMs: run barn up afterward. With no template or -f, reuse the discovered inventory or generate meta. The templates meta, dual, trio, and full contain 1, 2, 3, and 4 nodes. -c and specification flags customize a generated template; explicit specification flags cannot be used to rewrite an existing inventory. In 1.2.0, specification environment variables are also ignored when setup reuses an inventory.

--dry-run previews the transaction. Applying it requires terminal confirmation or --yes; operating-system privilege prompts can still occur. On a fresh unedited default template, setup may choose a free private /24 and save barn.yml.before-network-change if the default subnet conflicts. Explicit inventories retain their subnet. On Linux, foreign-architecture QEMU emulators and firmware need separate installation.

Aliases: s.

Option Meaning / default Available in
-A, --arch string override deployment vm_arch: native, amd64, or arm64; environment overrides require 1.2.0 1.1.0
-c, --cidr string generated template network as a canonical RFC1918 IPv4 /24 1.1.0
-C, --cpu string Base vCPUs before scale, integer 1–256; effective CPU count is also limited to 256; overrides BARN_CPU 1.2.0
-D, --disk string Absolute root capacity, at least the base virtual size; bare integer means GiB; overrides BARN_DISK 1.2.0
-d, --dry-run show the resolved setup plan without changing anything 1.1.0
-f, --file string Inventory to prepare; mutually exclusive with a template argument 1.1.0
-I, --image string override vm_image/vm_version for selected nodes; environment overrides require 1.2.0 1.1.0
-M, --mem string Base memory before scale, minimum 512 MiB; bare integer means MiB; overrides BARN_MEM 1.2.0
--mirror use the China official repository at repo.pigsty.cc; overridden by --repo 1.1.0
-m, --mode string macOS fixed-IP network backend: host or shared (default “host”) 1.1.0
-r, --repo string image repository URL or local directory, also used for socket_vmnet; overrides --mirror and $BARN_REPO 1.1.0
-S, --scale string Integer 1–256 multiplying base CPU and memory only; disk does not scale; overrides BARN_SCALE 1.2.0
-y, --yes accept the one-time setup plan (required without a terminal) 1.1.0
barn setup --dry-run
barn setup full -I el9 --yes
barn setup -f pigsty.yml --yes
barn up

barn init

barn init [meta|dual|trio|full] [flags]

Generate a Pigsty-compatible YAML inventory. The default is the one-node meta template in ./barn.yml; dual, trio, and full add nodes at .11, .12, and .13. The control node is .10. Existing files require --force; -o - prints the inventory. This command does not download images or prepare the host.

Barn 1.2.0 defaults to 1 vCPU and 2 GiB per node, with a 64 GiB root and a separate 128 GiB /data disk. Generated templates persist all six specification fields, including vm_scale. Version 1.1.0 used 2 vCPUs and 4 GiB by default. See specification defaults.

Option Meaning / default Available in
-A, --arch string override deployment vm_arch: native, amd64, or arm64; environment overrides require 1.2.0 1.1.0
-c, --cidr string rebase the generated template to a canonical RFC1918 IPv4 /24 1.1.0
-C, --cpu string Base vCPUs before scale, integer 1–256; effective CPU count is also limited to 256; overrides BARN_CPU 1.2.0
-D, --disk string Absolute root capacity, at least the base virtual size; bare integer means GiB; overrides BARN_DISK 1.2.0
--force Overwrite the destination inventory; default preserves existing files 1.1.0
-I, --image string override vm_image/vm_version for selected nodes; environment overrides require 1.2.0 1.1.0
-M, --mem string Base memory before scale, minimum 512 MiB; bare integer means MiB; overrides BARN_MEM 1.2.0
-o, --output string write to this path instead of ./barn.yml; ‘-’ writes to stdout 1.1.0
-S, --scale string Integer 1–256 multiplying base CPU and memory only; disk does not scale; overrides BARN_SCALE 1.2.0
barn init
barn init full -I el9 -A amd64 -o lab.yml
barn init dual -c 10.20.30.0/24 -o -

barn validate

barn validate [flags]

Read one inventory, validate the fields Barn consumes, and report the resolved nodes. It accepts no node selectors. Discovery checks barn.yml, barn.yaml, pigsty.yml, then pigsty.yaml in the current directory; no inventory means an error even if applied state exists. Overrides affect this validation only. Use --json to inspect the resolved specification, and plan to compare it with an existing deployment.

Aliases: v.

Option Meaning / default Available in
-A, --arch string override deployment vm_arch: native, amd64, or arm64; environment overrides require 1.2.0 1.1.0
-C, --cpu string Base vCPUs before scale, integer 1–256; effective CPU count is also limited to 256; overrides BARN_CPU 1.2.0
-D, --disk string Absolute root capacity, at least the base virtual size; bare integer means GiB; overrides BARN_DISK 1.2.0
-f, --file string Inventory to validate; otherwise use local discovery, with no applied-state fallback 1.1.0
-I, --image string override vm_image/vm_version for selected nodes; environment overrides require 1.2.0 1.1.0
-M, --mem string Base memory before scale, minimum 512 MiB; bare integer means MiB; overrides BARN_MEM 1.2.0
-r, --repo string image repository whose catalog checks the images; overrides $BARN_REPO 1.1.0
-S, --scale string Integer 1–256 multiplying base CPU and memory only; disk does not scale; overrides BARN_SCALE 1.2.0
barn validate -f lab.yml
barn --json validate -f lab.yml -I u24 -A arm64

barn doctor

barn doctor [flags]

Probe QEMU, native acceleration, firmware, OpenSSH, a short accelerated boot test, and host networking. This is a host diagnostic and takes no inventory or node arguments. Missing compute capabilities fail the command; missing network setup is reported as informational. Inspect barn network status for the detailed network plan and barn mac doctor for native macOS VMs.

Aliases: dt.

No local options beyond --help; global presentation options still apply.

barn doctor
barn --json doctor
barn doctor --verbose

barn version

barn version [flags]

Print version, source commit, build time, host OS, and architecture. Development builds normally report dev; release identity comes from the build stamp. Use this report when matching documentation or filing an issue. The root shortcut barn --version is also available.

Aliases: ver.

No local options beyond --help; global presentation options still apply.

barn version
barn --json version

barn completion

barn completion [bash|zsh|fish|powershell] [flags]

Require one shell name: bash, zsh, fish, or powershell. Print its completion script to stdout; the command does not install the script. Completion includes scoped flags, templates, image aliases, and best-effort node names. Reload it after upgrading Barn. Use text output when sourcing shell code.

No local options beyond --help; global presentation options still apply.

source <(barn completion bash)
barn completion zsh > "${fpath[1]}/_barn"
barn completion fish > ~/.config/fish/completions/barn.fish
barn completion powershell

Implementation

command_setup.go · command_misc.go · command_root.go

The version baseline identifies the released source and the changes since 1.1.0.

2.1.2 - Linux Lifecycle

Plan, create, start, resize, inspect, and explicitly remove Linux virtual machines.

Plan, create, start, resize, inspect, and explicitly remove Linux virtual machines.

This reference covers Barn 1.2.0. Option tables distinguish options also available in 1.1.0 from additions in 1.2.0; see the version baseline. All commands inherit the global options.

Node arguments are resolved names such as meta and node-1. Omit them to select all nodes in the command’s inventory/applied scope. IP addresses, groups, and vm_alias are not selectors. plan, up, reload, resize, and recreate read -f, then the current directory’s inventory, then applied state. Other lifecycle commands use applied state directly.

Specification overrides, scaling, and their environment variables are defined in Configuration. The 1.2.0 concurrency option applies only where --parallel is listed; plan, resize, and status do not accept it. For structured results and exit codes, see CLI contracts.

barn plan

barn plan [node...] [flags]

Compare the effective desired inventory with applied state without changing VMs or downloading images. Report exact images, resources, per-disk actions, and nodes needing creation, resize, recreation, start, or explicit removal. Catalog images need only local metadata; inspecting a named local image also needs qemu-img.

Exit 0 means the plan was produced, including plans blocked by omitted nodes. Automation must inspect action, blocked, and the change arrays. Explicit selectors retain the applied definitions of unselected peers. Planning output describes the result.

Aliases: pl.

Option Meaning / default Available in
-A, --arch string override deployment vm_arch: native, amd64, or arm64; environment overrides require 1.2.0 1.1.0
-C, --cpu string Base vCPUs before scale, integer 1–256; effective CPU count is also limited to 256; overrides BARN_CPU 1.2.0
-D, --disk string Absolute root capacity, at least the base virtual size; bare integer means GiB; overrides BARN_DISK 1.2.0
-f, --file string desired inventory; defaults to the discovered inventory, then the applied state 1.1.0
-I, --image string override vm_image/vm_version for selected nodes; environment overrides require 1.2.0 1.1.0
-M, --mem string Base memory before scale, minimum 512 MiB; bare integer means MiB; overrides BARN_MEM 1.2.0
-r, --repo string image repository URL or directory; overrides $BARN_REPO 1.1.0
-S, --scale string Integer 1–256 multiplying base CPU and memory only; disk does not scale; overrides BARN_SCALE 1.2.0
barn plan
barn plan -f pigsty.yml meta
barn --json plan -I u24 -A amd64

barn up

barn up [node...] [flags]

Create missing nodes, start stopped nodes, and retry unfinished guest setup while keeping healthy running VMs alive. It waits for management SSH unless --no-wait is set, and refreshes host SSH aliases and guest host/control-node entries. Non-fatal guest setup problems appear in warnings; ready nodes still succeed.

On an interactive first run, it can prepare missing host dependencies and generate meta when no inventory or applied deployment exists. Scripts should run setup --yes first. Existing definition changes require resize or recreate; inventory omissions require explicit destroy. --rollback only removes safe, uncommitted artifacts from preparation failures in this run.

Option Meaning / default Available in
-A, --arch string override deployment vm_arch: native, amd64, or arm64; environment overrides require 1.2.0 1.1.0
-C, --cpu string Base vCPUs before scale, integer 1–256; effective CPU count is also limited to 256; overrides BARN_CPU 1.2.0
-D, --disk string Absolute root capacity, at least the base virtual size; bare integer means GiB; overrides BARN_DISK 1.2.0
-f, --file string desired inventory; defaults to the discovered inventory, then the applied state 1.1.0
-I, --image string override vm_image/vm_version for selected nodes; environment overrides require 1.2.0 1.1.0
-M, --mem string Base memory before scale, minimum 512 MiB; bare integer means MiB; overrides BARN_MEM 1.2.0
--mirror use the China official repository at repo.pigsty.cc; overridden by --repo 1.1.0
-n, --no-wait return once QEMU is running, without waiting for the guest to boot 1.1.0
-p, --parallel int Node concurrency: omitted = max(1, logical CPUs - 2); 0 = all selected nodes; positive = limit 1.2.0
-r, --repo string image repository URL or directory; overrides --mirror and $BARN_REPO 1.1.0
--rollback remove safe artifacts from nodes that fail to prepare 1.1.0
-S, --scale string Integer 1–256 multiplying base CPU and memory only; disk does not scale; overrides BARN_SCALE 1.2.0
barn up
barn up -f pigsty.yml meta
barn up --mirror --rollback
barn up meta --no-wait

barn start

barn start [node...] [flags]

Start selected stopped nodes from applied state and wait for SSH. It works from any directory using the same BARN_HOME, without reading an inventory or creating nodes. It refreshes SSH access; use up to retry unfinished guest setup on an already-running VM. An unavailable share fails its node while independent nodes continue.

Option Meaning / default Available in
-n, --no-wait return once QEMU is running, without waiting for the guest to boot 1.1.0
-p, --parallel int Node concurrency: omitted = max(1, logical CPUs - 2); 0 = all selected nodes; positive = limit 1.2.0
barn start
barn start meta --no-wait

barn stop

barn stop [node...] [flags]

Stop selected running Linux VMs using their recorded process identities. It retains root disks, data disks, keys, and deployment state. With no names, stop the whole Linux deployment; it does not stop named macOS VMs or remove the host network.

Option Meaning / default Available in
-p, --parallel int Node concurrency: omitted = max(1, logical CPUs - 2); 0 = all selected nodes; positive = limit 1.2.0
barn stop meta
barn stop

barn restart

barn restart [node...] [flags]

Stop and start existing nodes using applied state. Inventory edits are not read. Use reload to re-read an inventory, or resize/recreate to apply changed definitions. It waits for SSH unless --no-wait is set.

Option Meaning / default Available in
-n, --no-wait return once QEMU is running, without waiting for the guest to boot 1.1.0
-p, --parallel int Node concurrency: omitted = max(1, logical CPUs - 2); 0 = all selected nodes; positive = limit 1.2.0
barn restart
barn restart meta --no-wait

barn reload

barn reload [node...] [flags]

Validate the desired specification and startup dependencies before stopping selected nodes, then converge them through up. It can create newly added nodes. Changes requiring resize or recreation, and omitted applied nodes, fail preflight before any VM stops. Use restart when you want only the recorded configuration.

Option Meaning / default Available in
-A, --arch string override deployment vm_arch: native, amd64, or arm64; environment overrides require 1.2.0 1.1.0
-C, --cpu string Base vCPUs before scale, integer 1–256; effective CPU count is also limited to 256; overrides BARN_CPU 1.2.0
-D, --disk string Absolute root capacity, at least the base virtual size; bare integer means GiB; overrides BARN_DISK 1.2.0
-f, --file string desired inventory; defaults to the discovered inventory, then the applied state 1.1.0
-I, --image string override vm_image/vm_version for selected nodes; environment overrides require 1.2.0 1.1.0
-M, --mem string Base memory before scale, minimum 512 MiB; bare integer means MiB; overrides BARN_MEM 1.2.0
--mirror use the China official repository at repo.pigsty.cc; overridden by --repo 1.1.0
-n, --no-wait return once QEMU is running, without waiting for the guest to boot 1.1.0
-p, --parallel int Node concurrency: omitted = max(1, logical CPUs - 2); 0 = all selected nodes; positive = limit 1.2.0
-r, --repo string image repository URL or directory; overrides --mirror and $BARN_REPO 1.1.0
--rollback remove safe artifacts from nodes that fail to prepare 1.1.0
-S, --scale string Integer 1–256 multiplying base CPU and memory only; disk does not scale; overrides BARN_SCALE 1.2.0
barn reload -f pigsty.yml
barn reload meta --mirror

barn resize

barn resize [node...] [flags]

Apply CPU/memory changes and grow the root disk while preserving VM identity, disks, and SSH keys. Desired resources come from the inventory, environment, or CLI overrides. Supported CPU/memory increases use hotplug; decreases or unsupported live changes use a stop/start fallback. Image, architecture, data-disk, network, share, create, and remove changes cannot be applied by resize.

Use -D 128GiB to grow the existing root to 128 GiB. Root growth stops QEMU, expands its image, boots the guest to grow and verify a last plain ext4/XFS root partition, then restores an initially stopped VM to stopped. Targets must grow and align to 512 bytes. Retry an interrupted growth with the same target; shrinking, LVM, encrypted roots, and non-last partitions are unsupported. See root growth and recovery. CLI overrides do not rewrite the inventory.

Option Meaning / default Available in
-A, --arch string override deployment vm_arch: native, amd64, or arm64; environment overrides require 1.2.0 1.1.0
-C, --cpu string Base vCPUs before scale, integer 1–256; effective CPU count is also limited to 256; overrides BARN_CPU 1.2.0
-D, --disk string Absolute root capacity, at least the base virtual size; bare integer means GiB; overrides BARN_DISK 1.2.0
-f, --file string desired inventory; defaults to the discovered inventory, then the applied state 1.1.0
-I, --image string override vm_image/vm_version for selected nodes; environment overrides require 1.2.0 1.1.0
-M, --mem string Base memory before scale, minimum 512 MiB; bare integer means MiB; overrides BARN_MEM 1.2.0
-S, --scale string Integer 1–256 multiplying base CPU and memory only; disk does not scale; overrides BARN_SCALE 1.2.0
barn plan -f pigsty.yml meta
barn resize -f pigsty.yml meta
barn --json status meta

barn recreate

barn recreate [node...] [flags]

Replace the selected VMs from the desired inventory. This deletes their root disks and non-persistent data disks; compatible persistent data disks are reattached. Inspect plan first. A terminal asks you to type recreate; scripts require --yes or --force (no -y).

No selector means the whole inventory. If it omits an applied node, the operation fails before stopping VMs; explicitly destroy that node or restore it in the inventory. Selected recreation preserves unselected peers. A successful recreation also refreshes SSH access.

Aliases: rc.

Option Meaning / default Available in
-A, --arch string override deployment vm_arch: native, amd64, or arm64; environment overrides require 1.2.0 1.1.0
-C, --cpu string Base vCPUs before scale, integer 1–256; effective CPU count is also limited to 256; overrides BARN_CPU 1.2.0
-D, --disk string Absolute root capacity, at least the base virtual size; bare integer means GiB; overrides BARN_DISK 1.2.0
-f, --file string desired inventory; defaults to the discovered inventory, then the applied state 1.1.0
--force recreate without the interactive confirmation (required without a terminal) 1.1.0
-I, --image string override vm_image/vm_version for selected nodes; environment overrides require 1.2.0 1.1.0
-M, --mem string Base memory before scale, minimum 512 MiB; bare integer means MiB; overrides BARN_MEM 1.2.0
--mirror use the China official repository at repo.pigsty.cc; overridden by --repo 1.1.0
-n, --no-wait return once QEMU is running, without waiting for the guest to boot 1.1.0
-p, --parallel int Node concurrency: omitted = max(1, logical CPUs - 2); 0 = all selected nodes; positive = limit 1.2.0
-r, --repo string image repository URL or directory; overrides --mirror and $BARN_REPO 1.1.0
-S, --scale string Integer 1–256 multiplying base CPU and memory only; disk does not scale; overrides BARN_SCALE 1.2.0
--yes same as --force 1.1.0
barn plan meta
barn recreate meta
barn recreate -f pigsty.yml meta --yes

barn status

barn status [node...] [flags]

Inspect recorded node configuration and live QEMU process identity without requiring the inventory directory. running reports a verified VM process, not an SSH readiness probe. --verbose adds runtime details; --json exposes node records, active-operation notes, and failures. A degraded node can produce exit 5 while healthy peers are still listed. The 1.2.0 text output uses compact SPEC values such as 2c4g.

Aliases: st.

No local options beyond --help; global presentation options still apply.

barn status
barn status meta --verbose
barn --json status

barn destroy

barn destroy [node...] [flags]

Delete selected VMs, or the whole deployment without selectors. Root and non-persistent data disks are deleted; persistent data disks are retained unless a whole-deployment deletion flag is explicit. Terminal confirmation requires typing destroy; scripts need --yes or --force, with no -y.

--delete-persistent and --purge require whole-deployment scope. --purge also removes deployment keys and state. The image cache, host network, and native Mac VMs remain. Removing the control node promotes the first remaining node; update admin_ip and run up to configure its control SSH access.

Aliases: de.

Option Meaning / default Available in
--delete-persistent also delete owned persistent data disks 1.1.0
--force destroy without the interactive confirmation (required without a terminal) 1.1.0
-p, --parallel int Node concurrency: omitted = max(1, logical CPUs - 2); 0 = all selected nodes; positive = limit 1.2.0
--purge terminal disposal: also delete persistent disks, keys, and deployment state 1.1.0
--yes same as --force 1.1.0
barn destroy meta
barn destroy --yes
barn destroy --yes --purge

barn purge

barn purge [flags]

Immediately delete the entire Linux deployment, including root disks, persistent data disks, keys, state, and the default SSH fragment. It takes no node names and reads no inventory. It has no --yes, -y, or --force: use --ask when you want to review the scope and confirm with y/yes (Enter cancels).

Cached images, host networking, and Mac VMs remain. An absent deployment is an idempotent success; ambiguous retained artifacts still require identity checks. The 1.2.0 --parallel flag controls node work during disposal.

Option Meaning / default Available in
--ask show the deletion scope and ask for y/yes before purging 1.1.0
-p, --parallel int Node concurrency: omitted = max(1, logical CPUs - 2); 0 = all selected nodes; positive = limit 1.2.0
barn purge --ask
barn --json purge

Implementation

command_lifecycle.go · main.go

The version baseline identifies the released source and the changes since 1.1.0.

2.1.3 - Guest Access and Hosts

Connect to Linux guests, run scripts, read logs, and publish SSH or hosts entries.

Connect to Linux guests, run scripts, read logs, and publish SSH or hosts entries.

This reference covers Barn 1.2.0. Option tables distinguish options also available in 1.1.0 from additions in 1.2.0; see the version baseline. All commands inherit the global options.

barn ssh

barn ssh [flags] [node] [-- command...]

Open an interactive SSH session on the selected prepared node, defaulting to the prepared control node (or first prepared node). Remote commands require --; arguments after it are joined for the remote shell like ordinary ssh. Put Barn options before the separator. Node selectors are resolved node names, not IP addresses, aliases, or groups.

A remote command returns its exit status unchanged. Structured interactive SSH sends session output to stderr and the final report to stdout. See SSH argument rules.

No local options beyond --help; global presentation options still apply.

barn ssh
barn ssh meta
barn ssh meta -- 'df -h /data; id'

barn exec

barn exec [flags] [node] -- command...

Run one remote command on the selected prepared node, with the same default-node selection as ssh. -- and a non-empty first command argument are required. Separate arguments preserve spaces, quotes, and later empty values. Use sh -c for pipelines, redirection, or variable expansion; a single command string is also accepted as compatibility shell shorthand.

Text mode forwards the child output and exit status. Structured output includes success, exit_code, stdout, and stderr. A nonzero remote exit is passed through, so inspect that result before interpreting the number as a Barn error class.

Aliases: ex.

No local options beyond --help; global presentation options still apply.

barn exec meta -- hostname
barn exec meta -- printf '<%s>\n' 'a b' ''
barn --json exec meta -- sh -c 'echo out; exit 17'

barn logs

barn logs [node] [flags]

Read the selected node’s serial console or QEMU diagnostic log, defaulting to the prepared control/first node. --source events selects the deployment-wide bounded event log and takes no node argument; it can be read before deployment state exists. Following logs emits a stream rather than one final document. -f means follow here, not inventory. QEMU argv is hidden unless verbose output is enabled.

Aliases: l.

Option Meaning / default Available in
-f, --follow continue streaming appended log data 1.1.0
-s, --source string log source: serial, qemu, or events (default “serial”) 1.1.0
barn logs meta
barn logs meta --source qemu
barn logs --source events --follow
barn --json logs meta --follow

barn provision

barn provision [node...] [flags]

Stream a local Bash script over the verified deployment SSH connection to selected running nodes, or all nodes if no names are supplied. The script is not left behind as a guest file; its digest is recorded for auditing. It does not create or start VMs.

--script is required. The default concurrency is 1 and the timeout is one hour for the operation. Unlike development lifecycle concurrency, --parallel 0 is invalid. --sudo uses noninteractive guest sudo -n; ensure the guest user has the required permission. Review per-node output and failures in structured mode.

Option Meaning / default Available in
-p, --parallel int Positive concurrency limit; default 1; zero is invalid 1.1.0
-s, --script string local Bash script to stream to each selected guest 1.1.0
--sudo run the guest script through sudo -n 1.1.0
-t, --timeout duration hard deadline for the operation (default 1h0m0s) 1.1.0
barn provision --script ./check.sh meta
barn provision --script ./bootstrap.sh --sudo --parallel 2 --timeout 30m

barn ssh-config

barn ssh-config [node...] [flags]

Print the Linux deployment’s OpenSSH fragment. --install writes a fragment and one Include in ~/.ssh/config; printing and installation accept node selectors. --remove removes only the named Barn fragment, needs no deployment state, and takes no nodes. Install and remove are mutually exclusive.

The default prefix is barn and its fragment is ~/.ssh/barn_config. Normal lifecycle commands maintain the default fragment automatically. If the user’s SSH configuration cannot be safely edited, Barn reports the manual Include instruction; direct barn ssh remains usable.

Aliases: sc.

Option Meaning / default Available in
-i, --install install the deployment fragment and one Include in ~/.ssh/config 1.1.0
--name string SSH Host and fragment prefix (default “barn”) 1.1.0
--remove remove the Include and fragment Barn installed 1.1.0
barn ssh-config
barn ssh-config --install meta
barn ssh-config --install --name lab
barn ssh-config --remove --name lab

barn hosts install

barn hosts install [flags]

Publish applied node names and aliases in the Barn-owned block of the host’s /etc/hosts, using the digest-matched privileged helper. On a terminal it shows the change and asks before applying; in noninteractive or structured mode it only previews unless --yes is passed. An unchanged block requires no privilege. This file is not automatically published by up.

Barn 1.2.0 adds -f pigsty.yml, publishing inventory nodes plus all.vars.vm_hosts before VM creation. It replaces the whole Barn block, so a later call without -f drops inventory-only service names. See local service names.

Aliases: i.

Option Meaning / default Available in
-f, --file string Inventory nodes, aliases and vm_hosts; usable before VM creation 1.2.0
-y, --yes apply the displayed privileged plan without confirmation (sudo may still ask for a password) 1.1.0
barn hosts install --json
barn hosts install --yes

barn hosts uninstall

barn hosts uninstall [flags]

Remove only the Barn-owned /etc/hosts block while retaining other entries. It needs no inventory or deployment state. Terminal mode displays a plan and asks; noninteractive/structured mode previews unless --yes is supplied. Removing the block does not delete VMs or change guest DNS configuration.

Aliases: u.

Option Meaning / default Available in
-y, --yes apply the displayed privileged plan without confirmation (sudo may still ask for a password) 1.1.0
barn hosts uninstall --json
barn hosts uninstall --yes

Implementation

command_root.go · command_access.go · main.go

The version baseline identifies the released source and the changes since 1.1.0.

2.1.4 - Image Commands

Refresh signed catalogs, inspect or download images, import local files, and prune caches.

Refresh signed catalogs, inspect or download images, import local files, and prune caches.

This reference covers Barn 1.2.0. Option tables distinguish options also available in 1.1.0 from additions in 1.2.0; see the version baseline. All commands inherit the global options.

Official repositories and HTTP sources require a trusted detached signature. An explicitly selected custom HTTPS repository or local directory can be unsigned, provided the source is exactly that repository’s catalog.json. Any supplied signature must verify. See catalog trust.

barn update

barn update [flags]

Fetch catalog.json and its signature when required by repository policy, verify trust and revision rules, then atomically activate the catalog. Ordinary commands use local metadata and never update it implicitly. This updates image metadata, not the Barn executable. Repository precedence is --repo > --mirror > BARN_REPO > the global official repository. See repository selection and trust.

Option Meaning / default Available in
--mirror use the China official repository at repo.pigsty.cc; overridden by --repo 1.1.0
-r, --repo string image repository URL or directory; overrides --mirror and $BARN_REPO 1.1.0
barn update
barn update --mirror
barn --json update --repo /srv/barn

barn image list

barn image list [flags]

List every catalog architecture and registered local image. It has no image selector or architecture filter. The active local catalog is used; run barn update explicitly to refresh it. Aliases barn images and barn im select the same image command group.

Aliases: ls.

Option Meaning / default Available in
-r, --repo string image repository URL or directory; overrides $BARN_REPO 1.1.0
barn image list
barn --json image list
barn image list --repo /srv/barn

barn image info

barn image info [image[:channel]|image@version-prefix] [flags]

Inspect metadata and verified cache state for one image, using the catalog default if omitted. Select a family (u24), channel (u24:stable), or numeric version prefix (el9@9). Architecture defaults to native. The compatibility spelling --architecture/-a is also accepted for --arch/-A. It does not download the artifact. Supported aliases and exact embedded versions are in Linux images.

Aliases: in.

Option Meaning / default Available in
-A, --arch string artifact architecture (defaults to native): native, amd64 (x86_64, x64), or arm64 (aarch64, a64) 1.1.0
-r, --repo string image repository URL or directory; overrides $BARN_REPO 1.1.0
barn image info
barn image info u24 -A arm64
barn --json image info el9@9

barn image pull

barn image pull [image[:channel]|image@version-prefix] [flags]

Download the selected artifact and verify its SHA-256, byte size, qcow2 structure, and virtual capacity before activating the cache entry. Omitted image and architecture select the catalog default and native host architecture. Existing valid cached bytes are reused. The catalog’s upstream URL is provenance, not a download fallback. --architecture/-a remains a compatibility alias. A foreign artifact does not install the corresponding QEMU emulator.

Aliases: p.

Option Meaning / default Available in
-A, --arch string artifact architecture (defaults to native): native, amd64 (x86_64, x64), or arm64 (aarch64, a64) 1.1.0
--mirror use the China official repository at repo.pigsty.cc; overridden by --repo 1.1.0
-r, --repo string image repository URL or directory; overrides --mirror and $BARN_REPO 1.1.0
barn image pull
barn image pull u24 --mirror
barn image pull d13 -A amd64 --repo /srv/barn

barn image import

barn image import <path> [flags]

Copy a local regular qcow2 image into the private cache and validate its container, optionally against an expected digest. Relative paths, ~/, and symlinks to regular files are accepted. qemu-img is required; importing does not boot the image, add cloud-init, or change credentials.

A local-* name registers an immutable inventory reference and requires boot mode plus the source login user. Named imports can record a foreign architecture with -A; qcow2 metadata does not identify CPU architecture, and unnamed imports cannot use -A. Set both vm_image and a matching vm_arch when using it. See import and cache rules.

Aliases: i.

Option Meaning / default Available in
-A, --arch string Guest architecture recorded with --name; defaults to native; does not convert the image 1.1.0
-b, --boot string required with --name: bios or uefi 1.1.0
--name string Immutable local-* alias; requires --boot and --source-user 1.1.0
-s, --sha256 string optional expected SHA-256 1.1.0
-u, --source-user string required with --name: source image login user 1.1.0
barn image import ./base.qcow2
barn image import ./base.qcow2 --name local-dev --boot uefi --source-user ubuntu -A arm64

barn image sync

barn image sync <url|path> [flags]

Activate a catalog from one exact URL or local file, reading its adjacent .minisig signature under the trust policy above. This is the recovery/import path; use update for the configured repository’s normal endpoint. --repo chooses which repository’s active metadata to replace. An unsigned custom source must be exactly <repo>/catalog.json; an arbitrary recovery file requires a trusted signature.

Older revisions require --allow-downgrade. For custom repositories that allow unsigned catalogs, switching from a previously signed catalog to an unsigned one also requires this flag. It never permits an invalid signature or bypasses the official-repository signature requirement and revision-content checks. Future downgrade protection still remembers the highest revision.

Aliases: sy.

Option Meaning / default Available in
--allow-downgrade activate a catalog older than the newest revision seen so far 1.1.0
-r, --repo string Repository whose active catalog is replaced 1.1.0
barn image sync /srv/barn/catalog.json
barn image sync --repo /srv/barn /srv/recovery/catalog.json
barn image sync --allow-downgrade /srv/recovery/catalog.json

barn image reset

barn image reset [flags]

Reactivate the catalog embedded in the current binary for the selected repository. It keeps the highest accepted revision used by downgrade protection. Cached images and VM disks are retained; this is not a cache deletion command.

Option Meaning / default Available in
-r, --repo string Repository whose active catalog is reset to the embedded catalog 1.1.0
barn image reset
barn --json image reset --repo /srv/barn

barn image prune

barn image prune [flags]

List reclaimable cache files by default. --yes deletes candidates; it is mutually exclusive with --dry-run. The selected active catalog, applied node base-image digests, and registered local aliases protect their referenced files. Stale recognized staging files can also be removed; quarantined .corrupt-* files remain.

Preview with the same repository selection you will use for deletion. Deletion is sequential; an error on a later file does not restore files already removed. See pruning rules.

Aliases: pr.

Option Meaning / default Available in
-d, --dry-run show unreferenced images and stale staging files without deleting 1.1.0
-r, --repo string image repository URL or directory; overrides $BARN_REPO 1.1.0
-y, --yes Delete the listed unreferenced files; mutually exclusive with --dry-run 1.1.0
barn image prune --dry-run
barn image prune --repo /srv/barn --dry-run
barn image prune --repo /srv/barn --yes

Implementation

command_image.go · images.go

The version baseline identifies the released source and the changes since 1.1.0.

2.1.5 - Repository Commands

Scan, build, and verify static image repositories from repo.yaml and qcow2 artifacts.

Scan, build, and verify static image repositories from repo.yaml and qcow2 artifacts.

This reference covers Barn 1.2.0. Option tables distinguish options also available in 1.1.0 from additions in 1.2.0; see the version baseline. All commands inherit the global options.

barn repo scan

barn repo scan <root> [flags]

Scan one repository root containing repo.yaml and a flat images/ directory. Report tracked, missing, untracked, and unsafe qcow2 files without changing them or requiring qemu-img. Successful scanning exits 0 even with findings; automation must inspect missing and unsafe rather than rely on the exit code alone.

No local options beyond --help; global presentation options still apply.

barn repo scan /srv/barn
barn --json repo scan /srv/barn

barn repo build

barn repo build <root> [flags]

Validate repo.yaml, inspect the declared qcow2 artifacts with qemu-img, calculate final digests/sizes, and atomically generate catalog.json. Missing or unsafe artifacts fail the build; untracked files are omitted. repo.yaml and image bytes are preserved.

This command does not sign or publish a catalog. Increment the revision for changed contents, keep artifact filenames immutable, and use the separate contributor signing tools before publication. See repository format.

No local options beyond --help; global presentation options still apply.

barn repo build /srv/barn
barn --json repo build /srv/barn

barn repo verify

barn repo verify <root> [flags]

Rebuild the expected catalog in memory and require byte-for-byte agreement with the existing catalog.json, using repo.yaml and actual image bytes. Requires qemu-img. It checks repository consistency; detached-signature verification belongs to catalog activation and the signing tools.

No local options beyond --help; global presentation options still apply.

barn repo verify /srv/barn
barn --json repo verify /srv/barn

Implementation

command_repo.go

The version baseline identifies the released source and the changes since 1.1.0.

2.1.6 - Host Network Commands

Inspect, install, and remove the fixed-IP network used by Linux guests.

Inspect, install, and remove the fixed-IP network used by Linux guests.

This reference covers Barn 1.2.0. Option tables distinguish options also available in 1.1.0 from additions in 1.2.0; see the version baseline. All commands inherit the global options.

barn network status

barn network status [flags]

Inspect the one host-wide Linux-guest network: backend, installed CIDR, ownership, health, and lifecycle preflight findings. --cidr checks an expected subnet; omitting it inspects the installed one. It is read-only. This network is separate from each native Mac VM’s private network.

Aliases: st.

Option Meaning / default Available in
-c, --cidr string Optional expected private /24; otherwise inspect the installed subnet 1.1.0
barn network status
barn --json network status --cidr 10.10.10.0/24

barn network install

barn network install [flags]

Inspect or install the host-wide backend: socket_vmnet on macOS or barn0 through the active Linux network manager. On a terminal it shows a privileged plan and asks; structured/noninteractive calls only preview unless --yes is supplied. On a fresh Mac, use setup to obtain the pinned socket_vmnet archive first.

--archive and --interface-id are macOS-only. host/shared are macOS vmnet modes; Linux rejects shared. An existing network must match the chosen configuration; review status before changing subnet or mode.

Aliases: i.

Option Meaning / default Available in
-a, --archive string macOS: pinned socket_vmnet archive 1.1.0
-c, --cidr string network: an RFC1918 IPv4 /24 (default “10.10.10.0/24”) 1.1.0
-i, --interface-id string macOS: persistent vmnet UUID 1.1.0
-m, --mode string macOS vmnet mode: host or shared (default “host”) 1.1.0
-y, --yes apply the displayed privileged plan without confirmation (sudo may still ask for a password) 1.1.0
barn network install --json
barn network install --cidr 10.10.10.0/24 --yes

barn network uninstall

barn network uninstall [flags]

Remove only Barn-owned network configuration and restore recorded host settings. Stop running Linux guests first: the operation refuses a live deployment. Terminal mode asks after showing the plan; structured/noninteractive calls preview unless --yes is set. This does not delete VM disks or native Mac networks.

Aliases: u.

Option Meaning / default Available in
-y, --yes apply the displayed privileged plan without confirmation (sudo may still ask for a password) 1.1.0
barn stop
barn network uninstall --json
barn network uninstall --yes

Implementation

command_network.go

The version baseline identifies the released source and the changes since 1.1.0.

2.1.7 - macOS Commands

Manage named native macOS VMs, desktop access, restore images, and shared bases.

Manage named native macOS VMs, desktop access, restore images, and shared bases.

This reference covers Barn 1.2.0. All options below were also available in 1.1.0.

Native macOS VMs require Apple Silicon, macOS 27+, and the matching Barn.app native app bundle, invoked as the logged-in desktop user. Their state lives under $BARN_HOME/mac and is independent of the Linux inventory.

For an optional name, Barn selects the sole existing VM or mac1; multiple VMs without mac1 require an explicit name. configure, recreate, and destroy always require names. start and stop accept multiple names or --all. At most two macOS VMs can run concurrently across apps on the host, including an active base installation. See machine rules, networking, output, and files.

Mac resource sizes use binary units even for GB, unlike Linux inventory decimal GB; a bare Mac size is bytes. These commands ignore Linux BARN_IMAGE/BARN_ARCH/BARN_CPU/BARN_MEM/BARN_SCALE/BARN_DISK overrides.

barn mac ls

barn mac ls [flags]

Show every machine: its state, address, SSH readiness, macOS version and resources. It reads local records and asks each running machine, and never creates, downloads or starts anything.

Running barn mac without a subcommand also lists machines.

Aliases: list, status, st.

No local options beyond --help; global presentation options still apply.

barn mac ls
barn --json mac ls

barn mac up

barn mac up [name] [flags]

Create the named machine (mac1 by default), start it, and wait until SSH and passwordless sudo work. Without a prepared macOS base, up first downloads the restore image from Apple after confirmation and installs it once; later machines clone that base.

Creation options apply to a new machine. For an existing machine, up starts it and rejects options that differ from its configuration; use configure for supported setting changes.

--no-wait returns once the VM is running. First-boot provisioning may still be in progress; run barn mac up NAME again to wait for SSH and finish initialization.

Option Meaning / default Available in
--clipboard on|off on/off, true/false, or yes/no; default on, active while the desktop window is focused 1.1.0
--cpu int vCPUs, default 4; minimum 2, maximum host logical CPU count 1.1.0
--disk size Base capacity, minimum 32 GiB; default uses the prepared base, or 100 GiB initially 1.1.0
--ipsw string install macOS from this restore image instead of downloading from Apple 1.1.0
--memory size Memory, default 8 GiB; a whole MiB from 4 GiB to host RAM 1.1.0
-n, --no-wait return once the VM runs; first boot may still be initializing 1.1.0
--open show the desktop as soon as the VM runs 1.1.0
--share [name=]path[:ro] Repeatable [name=]path[:ro|:rw]; default writable, mounted under /Volumes/My Shared Files 1.1.0
--subnet string Canonical private /24, or auto; default selects the first free subnet 1.1.0
--user string Guest administrator; default is the host username, or barn if that name is invalid 1.1.0
-y, --yes download macOS from Apple without asking (required without a terminal) 1.1.0
barn mac up
barn mac up dev --cpu 8 --memory 16G --share ~/src --open
barn mac up build --yes --no-wait

barn mac start

barn mac start [name...] [flags]

Start existing machines and wait for SSH without creating machines or downloading images. --recovery boots macOS Recovery with its desktop shown.

Select names or --all, not both. Ordinary start requires completed first-boot initialization; use up to finish an uninitialized VM.

Option Meaning / default Available in
--all start every machine 1.1.0
-n, --no-wait return once the VM runs, without waiting for SSH 1.1.0
--open show the desktop as soon as the VM runs 1.1.0
--recovery boot macOS Recovery and show its desktop 1.1.0
barn mac start mac1
barn mac start --all
barn mac start mac1 --recovery

barn mac stop

barn mac stop [name...] [flags]

Shut machines down normally through macOS. A machine that has not stopped after two minutes is powered off, and the result says so. --force powers off at once, like holding a power button; unsaved work in the guest is lost.

Option Meaning / default Available in
--all stop every machine 1.1.0
--force Power off immediately instead of requesting shutdown; unsaved guest work is lost 1.1.0
barn mac stop dev
barn mac stop --all
barn mac stop dev --force

barn mac restart

barn mac restart [name] [flags]

Shut a machine down normally and start it again, applying configuration changes.

Accepts at most one name; there is no --all. Omitted names follow the default-machine rule above.

Option Meaning / default Available in
-n, --no-wait return once the VM runs, without waiting for SSH 1.1.0
--open show the desktop as soon as the VM runs 1.1.0
barn mac restart dev
barn mac restart dev --open

barn mac configure

barn mac configure <name> [flags]

Change settings of an existing machine without touching its disk or account. CPUs, memory, shared folders and the network change while the machine is stopped; the clipboard setting can change anytime. Saved configuration, including clipboard changes, applies at the next start; use the menu-bar control to toggle clipboard sharing in the current session.

An explicit name is required. Omitted flags retain their values. --share adds or replaces a named share; --unshare removes one.

Option Meaning / default Available in
--clipboard on|off on/off, true/false, or yes/no; omitted retains the setting 1.1.0
--cpu int vCPUs, minimum 2 and maximum host logical CPU count; omitted retains current value 1.1.0
--memory size Memory, a whole MiB from 4 GiB to host RAM; omitted retains current value 1.1.0
--share [name=]path[:ro] Add or replace a named share; repeatable, writable by default, optional :ro or :rw 1.1.0
--subnet string move to this private /24 network, or auto for the first free one 1.1.0
--unshare stringArray stop sharing the named folder (repeatable) 1.1.0
barn mac stop dev
barn mac configure dev --cpu 8 --memory 16G --share ~/src
barn mac configure dev --unshare src --clipboard off
barn mac start dev

barn mac recreate

barn mac recreate <name> [flags]

Erase the machine’s disk and start a fresh macOS with the same name, account, resources, shared folders and address. --update uses the newest prepared base (see barn mac image update). Its files, apps and settings are lost.

A terminal asks you to type recreate; scripts require --yes or --force, with no -y shorthand.

Option Meaning / default Available in
--force skip the confirmation (required without a terminal) 1.1.0
-n, --no-wait return once the VM runs, without waiting for SSH 1.1.0
--update use the newest prepared macOS base 1.1.0
--yes Same as --force; no -y shorthand 1.1.0
barn mac recreate dev
barn mac image update --yes
barn mac recreate dev --update --yes

barn mac destroy

barn mac destroy <name...> [flags]

Power machines off and delete them: disk, keys, password and settings. The shared macOS base and other machines are kept; barn mac image prune removes unused bases.

Explicit names are required. A terminal asks you to type destroy; scripts require --yes or --force, with no -y. Shared host folders are retained.

Option Meaning / default Available in
--force skip the confirmation (required without a terminal) 1.1.0
--yes Same as --force; no -y shorthand 1.1.0
barn mac destroy dev
barn mac destroy mac1 mac2 --yes

barn mac open

barn mac open [name] [flags]

Show the machine’s desktop window, starting the machine first when it is stopped. Closing the window keeps the machine running in the background; the clipboard is shared while the window is focused.

An uninitialized running VM can be viewed here; for a stopped uninitialized VM use mac up NAME --open.

No local options beyond --help; global presentation options still apply.

barn mac open
barn mac open dev

barn mac ssh

barn mac ssh [flags] [name] [-- command...]

Connect with the machine’s own key and pinned host key. Everything before – must be empty or one known machine name, apart from Barn flags. A remote command requires –, and its first argument must not be empty. For exec, pass the program and its arguments separately to preserve argument boundaries, including later empty values. Use sh -c explicitly for shell expressions. A single command string remains supported as shell shorthand for compatibility. exec requires a command. ssh passes the command line to the remote shell like plain ssh. Without a command, ssh opens an interactive session.

No local options beyond --help; global presentation options still apply.

barn mac ssh dev
barn mac ssh dev -- 'sw_vers; id'

barn mac exec

barn mac exec [flags] [name] -- command...

Connect with the machine’s own key and pinned host key. Everything before – must be empty or one known machine name, apart from Barn flags. A remote command requires –, and its first argument must not be empty. For exec, pass the program and its arguments separately to preserve argument boundaries, including later empty values. Use sh -c explicitly for shell expressions. A single command string remains supported as shell shorthand for compatibility. exec requires a command. ssh passes the command line to the remote shell like plain ssh. Without a command, ssh opens an interactive session.

No local options beyond --help; global presentation options still apply.

barn mac exec dev -- sw_vers
barn mac exec dev -- printf '<%s>\n' 'a b' ''
barn --json mac exec dev -- sh -c 'echo out; exit 3'

barn mac password

barn mac password [name] [flags]

Show the password of the machine’s login account, needed for the lock screen and administrator prompts in its desktop. It is stored next to the machine’s SSH key with the same protection. --copy puts it on the clipboard instead of printing it.

Option Meaning / default Available in
-c, --copy copy to the clipboard instead of printing 1.1.0
barn mac password dev
barn mac password dev --copy

barn mac ssh-config

barn mac ssh-config [flags]

Print the OpenSSH fragment that lets ssh, scp, rsync and editors reach each machine by name with its own key and pinned host key. The first machine to become ready installs it as one Include in ~/.ssh/config, and lifecycle commands keep it current. –remove deletes only what Barn installed and keeps it out until –install. A name that ~/.ssh/config already uses for another host is published by address only.

--install and --remove are mutually exclusive. This command takes no machine selectors.

Option Meaning / default Available in
-i, --install install the entries as one Include in ~/.ssh/config 1.1.0
--remove remove the entries Barn installed 1.1.0
barn mac ssh-config
barn mac ssh-config --install
barn mac ssh-config --remove

barn mac logs

barn mac logs [name] [flags]

Show the last lines of the machine’s runtime log: startup, network, shutdown and Apple Virtualization errors.

--lines/-n must be positive and defaults to 100. There is no follow flag; -n means lines, not no-wait.

Option Meaning / default Available in
-n, --lines int number of recent lines (default 100) 1.1.0
barn mac logs dev
barn mac logs dev --lines 200

barn mac setup

barn mac setup [flags]

Download macOS from Apple after you confirm, or read –ipsw, and install it once into the unbooted base that every machine is cloned from. up does this automatically; setup lets you do it ahead of time.

Option Meaning / default Available in
--disk size Base capacity, minimum 32 GiB and default 100 GiB 1.1.0
--ipsw string install macOS from this restore image instead of downloading from Apple 1.1.0
-y, --yes download macOS from Apple without asking (required without a terminal) 1.1.0
barn mac setup --yes
barn mac setup --ipsw ~/Downloads/restore.ipsw --disk 200G

barn mac image ls

barn mac image ls [flags]

List restore images and bases with their state, disk usage and the machines that use them.

This local-state query does not invoke the native runner.

Aliases: list.

No local options beyond --help; global presentation options still apply.

barn mac image ls
barn --json mac image ls

barn mac image update

barn mac image update [flags]

Ask Apple for the newest macOS 27 restore image, download it after you confirm, and install it as the default base for new machines. Existing machines keep their base until barn mac recreate –update.

Option Meaning / default Available in
--ipsw string use this restore image instead of downloading from Apple 1.1.0
-y, --yes download without asking (required without a terminal) 1.1.0
barn mac image update --yes
barn mac image ls

barn mac image prune

barn mac image prune [flags]

List unused bases except the current default base. --installers also includes downloaded restore images and partial downloads. Deletion requires --yes.

Option Meaning / default Available in
--installers include downloaded restore images and partial downloads 1.1.0
-y, --yes Delete the listed unused images; otherwise only list candidates 1.1.0
barn mac image prune
barn mac image prune --installers --yes

barn mac doctor

barn mac doctor [flags]

Check macOS and Apple Silicon support, the Mac component’s signature, free disk space, the prepared base, and every machine: its files, keys, shared folders, network and SSH. It changes nothing and starts nothing.

No local options beyond --help; global presentation options still apply.

barn mac doctor
barn --json mac doctor

Implementation

command_mac.go

The version baseline identifies the released source and the changes since 1.1.0.

2.2 - Configuration

The Pigsty-compatible inventory fields Barn reads, their defaults, and node-level drift behavior.

This reference covers Barn 1.2.0. Linux defaults are 1 vCPU and 2 GiB. To preserve the 1.1.0 defaults on upgrade, explicitly set vm_cpu: 2 and vm_mem: 4GiB. See the version baseline and check the installed build with barn version and --help.

Discovery

Barn resolves the inventory path by checking an explicit -f argument first, then searching the current working directory for barn.yml, barn.yaml, pigsty.yml, and pigsty.yaml in that order. All four filenames use the same Pigsty-compatible YAML inventory format.

When no inventory file is present in the current directory, plan, up, reload, recreate, and resize fall back to the applied specification stored in BARN_HOME (default ~/.barn) if a deployment already exists. validate always requires an inventory file on disk. The file must resolve to a regular file no larger than 4 MiB; symlinks are supported. Relative shared-directory paths resolve beside the inventory’s real file. Barn stops at the first matching filename and reports any parse or validation error directly rather than falling through to the next candidate.

Barn stores applied deployment state in BARN_HOME. Renaming the inventory does not change the deployment; use an absolute BARN_HOME to address the same deployment across working directories.

A complete inventory

barn init [meta|dual|trio|full] generates a 1-, 2-, 3-, or 4-node topology template. meta uses .10, and larger templates add node-1 at .11, node-2 at .12, and node-3 at .13 (defining VM topology while leaving Pigsty service roles to your upper-layer playbook). Pass --cidr to change the /24 prefix while keeping the host octets, or run barn init full -o - to print a template to stdout without writing a file.

barn.yml
all:
  vars:
    admin_ip: 10.10.10.10
    vm_image: u24
    vm_cpu: 2
    vm_mem: 4GiB
    vm_disk: 64
  children:
    lab:
      hosts:
        10.10.10.10: { nodename: meta }
        10.10.10.11:
          nodename: worker
          vm_mem: 8GiB
          vm_disks:
            - { path: /data, size: 128, fs: auto, persistent: true }

This inventory defines two managed nodes with meta as the control node. Save it as barn.yml and inspect the resolved configuration before starting any VMs:

barn validate -f barn.yml
barn --json validate -f barn.yml
barn plan -f barn.yml

validate checks YAML syntax, field rules, and image catalog references (pass --repo to check against another repository’s catalog) without starting VMs or downloading images. Its JSON output includes valid, source, spec_hash, and resolved; if the local image catalog cannot be read, it emits a warning and leaves image selectors unresolved. During up, Barn verifies local-* imported image bytes, host capabilities, shared directories, networking, and guest SSH readiness.

What Barn reads

An inventory must contain a top-level all: group, from which Barn traverses hosts and nested children mappings. Barn parses static YAML directly—without evaluating Ansible inventory plugins, Jinja expressions, group_vars, or host_vars. If the same IPv4 address appears in multiple groups, Barn merges its inherited variables into a single node definition.

Barn reads node IPv4 keys, nodename, admin_ip, pg_cluster, pg_seq, node_admin_username, node_admin_uid, and the vm_* variables listed below:

  • Control node: Barn reads admin_ip from all.vars. If admin_ip is omitted or does not match a managed node, Barn selects the first managed node encountered in YAML traversal order (reading each group’s hosts before its children). Set admin_ip explicitly so the control node stays fixed when you reorder groups.
  • Login account: All nodes share one login username matching [a-z_][a-z0-9_-]{0,31}. For the default dba user, an explicit node_admin_uid must be 88. For a custom username, Barn verifies that node_admin_uid is an integer if present, while the guest assigns the account’s UID at creation time.
  • Upper-layer Pigsty fields: Variables outside Barn’s scope—such as pg_role, pg_version, repo_*, and node_packages—are ignored by Barn and never trigger VM configuration drift.
  • Inheritance and validation: Variables inherit along all.vars → nested children.<group>.vars → host variables. Host-level list variables such as vm_disks replace inherited lists wholesale. If two sibling groups at the same depth set conflicting values for the same node, override the variable on the host entry. Unknown vm_* keys, invalid types, Jinja templates, duplicate YAML keys, and multi-document YAML streams fail validation with line numbers, violated rules, and spelling suggestions for mistyped vm_* names. YAML anchors and << merge keys are supported (explicit keys win, followed by the first mapping in a merge sequence).
  • Skipped nodes: Hosts with vm_skip: true may use hostnames or IPv6 keys and do not participate in the managed /24; at least one managed node must remain. Skipping an existing node marks it missing in plan without destroying it.

VM variables

Variable Default Meaning
vm_skip false Skip virtualization for this physical or external host
vm_image u24 Image family, :channel reference, or image@version selector
vm_version unset Newest numeric version matching this prefix, such as 9 or 9.7
vm_arch native Deployment-wide guest architecture; follows the host unless explicitly set to amd64 or arm64
vm_cpu 1 Base vCPU count; the effective count after scaling must be 1–256
vm_mem 2048 Base memory size: bare integer in MiB, or an explicit unit string such as 8GiB (minimum 512MiB)
vm_scale 1 Positive integer multiplying CPU and memory only
vm_hosts [] Extra local hosts mappings, declared only in all.vars
vm_disk 64 Root disk capacity: bare integer in GiB, or an explicit unit string such as 64GiB
vm_disks [{path: /data}] Extra data disks (defaults to one 128 GiB non-persistent disk mounted at /data)
vm_alias [] Additional guest /etc/hosts, SSH config, and optional host /etc/hosts aliases
vm_shares [] QEMU 9p host-directory shares on Linux and macOS hosts

An empty host mapping (10.10.10.10: {}) is enough to define a complete VM with default settings. Managed nodes require distinct usable addresses in the configured /24.

Bare integers specify MiB for vm_mem and GiB for disks. Size strings accept positive integers with case-insensitive units, including 8G, 512m, 4gib, and 64GB. K/M/G/T and KiB/MiB/GiB/TiB use binary powers; KB/MB/GB/TB use decimal powers. Fractions such as 1.5GiB and unitless strings such as "8192" are invalid. up checks that the root disk is at least as large as the base image’s virtual size.

When vm_image is omitted, Barn defaults to u24 (Ubuntu 24.04) from the binary’s embedded catalog regardless of any default declared in a custom repository. Built-in aliases such as ubuntu → u24 are normalized against the embedded catalog during inventory parsing, so custom repositories should use their canonical family names in vm_image rather than reassigning built-in aliases.

Use vm_version when you want to keep the image family and version prefix in separate fields:

barn.yml
vm_image: el9
vm_version: "9.8"

If an exact version match exists in the catalog, Barn selects it; otherwise it matches dot-separated numeric prefixes and picks the highest version (9.8 selects the latest 9.8.* build, 9 selects the latest 9.x release, and 9.10 sorts after 9.9). Use vm_version only with a bare family name in vm_image (without :channel or @version).

Resource scaling in 1.2.0

Resolve vm_cpu, vm_mem, and vm_scale independently: host variables override the deepest group, which overrides parent groups and all.vars. Missing values default to 1 vCPU, 2048 MiB, and a scale of 1. Multiply once after inheritance:

CPU    = resolved(vm_cpu, default=1) × resolved(vm_scale, default=1)
Memory = resolved(vm_mem, default=2048 MiB) × resolved(vm_scale, default=1)
Settings Effective CPU Effective memory
None 1 2 GiB
vm_scale: 3 3 6 GiB
vm_cpu: 2, vm_scale: 3 6 6 GiB
vm_mem: 4GiB, vm_scale: 3 3 12 GiB
vm_cpu: 2, vm_mem: 4GiB, vm_scale: 3 6 12 GiB

A host’s vm_scale: 1 replaces a group’s vm_scale: 3; group and host scales do not compound. Equal-depth conflicts require a host override. Scale accepts integers from 1 to 256, subject to the effective 256-vCPU limit and memory overflow checks. Quoted numbers, fractions, booleans, null, zero, and negative values fail validation. Base memory still requires at least 512 MiB. Root disks, data disks, and shares are unaffected. Use the exact keys vm_cpu and vm_mem; vm_memory is not an alias.

Inspect barn plan -f pigsty.yml, then apply CPU/memory changes with barn resize -f pigsty.yml. Existing deployments that omit these fields will show a resource decrease when their inventory is read by the new build. Add vm_cpu: 2 and vm_mem: 4096 before upgrading to preserve the previous defaults. Commands using applied state without an inventory keep the recorded resources.

Local service names in 1.2.0

Inventory-based hosts publication is available in Barn 1.2.0.

Declare finite service mappings in all.vars.vm_hosts. Barn combines them with node names and vm_alias when publishing from an explicit inventory:

pigsty.yml
all:
  vars:
    vm_hosts:
      - {address: 10.10.10.10, names: [i.pigsty, m.pigsty]}
      - {address: 10.10.10.100, names: [pg-dns-service]}
  hosts:
    10.10.10.10: {nodename: meta}
barn hosts install -f pigsty.yml --json  # preview, including before VM creation
barn hosts install -f pigsty.yml --yes  # publish; sudo may ask for a password
barn hosts uninstall --yes

Each entry requires address and a non-empty names list. Addresses must be canonical IPv4 addresses in the inventory’s private /24, within .9–.254; .1–.8 are reserved for the host and DHCP. Identical name/address pairs deduplicate; names compare without case, and different destinations fail validation. Existing conflicting names outside the Barn-owned block also stop publication. A hosts record does not configure a VIP in a guest.

These mappings affect only the host’s /etc/hosts. Editing them does not change the VM spec hash, configure guest DNS, or trigger VM drift. Without -f, hosts install publishes only names and aliases from applied VM state, replacing the whole Barn block; repeat the explicit inventory command to retain service names. up never publishes this file automatically. An unchanged plan needs no sudo; each changed system hosts file requires privilege. Wildcards, SRV records, and live primary discovery require a DNS service.

Pigsty’s development bin/sandbox-dns extracts its portal, node, PostgreSQL, VIP, and MinIO names for this interface. See the migration audit.

Specification overrides

Barn 1.2.0 accepts unified CLI and environment inputs for six specification fields. Version 1.1.0 supports the image/architecture flags but not the four resource flags or these environment variables.

Inventory field CLI Environment variable Default
vm_image --image/-I BARN_IMAGE u24
vm_arch --arch/-A BARN_ARCH native
vm_cpu --cpu/-C BARN_CPU 1
vm_mem --mem/-M BARN_MEM 2048 MiB
vm_scale --scale/-S BARN_SCALE 1
vm_disk --disk/-D BARN_DISK 64 GiB

Each field follows CLI > nonempty environment > host > deepest child group > parent group > all.vars > default. CPU and memory always specify bases; multiply each once by the final scale after applying overrides. Disk capacity does not scale. -S 3 replaces the previous multiplier; add -S 1 for exact final CPU/memory. CLI and environment override host values on selected nodes. Put common defaults in all.vars when host exceptions should remain effective. Inventory syntax, field types, and inheritance conflicts must remain valid.

barn init full -C 2 -M 4GiB -S 2 -D 128GiB
barn plan -f barn.yml -S 3
BARN_SCALE=2 barn validate -f barn.yml -S 1

init and template-generating setup write all six choices, including defaults, and save CPU/memory bases and scale. The first interactive up also saves its choices if it needs to generate an inventory. Lifecycle overrides on an existing file change only the current desired specification without rewriting YAML; reading the original file later reports drift. When only applied state remains, --scale or BARN_SCALE requires -f; CPU/memory can set absolute values with an implicit scale of 1.

Environment variables affect only commands accepting the corresponding specifications. status/start/stop/restart/destroy/purge, independent image management, and barn mac ignore these six variables. So does setup when reusing an inventory. resize accepts CPU/memory/scale, root capacity, and its existing I/A consistency checks; BARN_DISK only grows the root disk. Whitespace-only environment values mean unset, and invalid values shadowed by CLI do not fail: BARN_CPU=bad barn plan -C 2 uses 2. Effective environment overrides are preserved as explicit flags in follow-up commands.

--disk sets the absolute system root capacity, which must be at least the base image’s virtual size. Planning checks catalog metadata; creation checks the actual image again. It applies to creation, explicit recreation, and in-place root growth through resize -D. Growth supports last plain ext4/XFS root partitions and never shrinks them; see root disk growth for the workflow and recovery. Recreation replaces root and nonpersistent data disks. The default 128 GiB /data disk remains separate.

Image and architecture overrides

Image and architecture follow the same specification precedence as the resource fields: CLI flags, nonempty environment variables, inventory values, then defaults.

Guests default to the host architecture: arm64 on Apple Silicon and amd64 on Intel/AMD hosts. Another architecture can be selected through the CLI, environment, or inventory. Precedence is --arch/-A → BARN_ARCH → inventory vm_arch → native (host architecture). Use -A native to override the environment and inventory and follow the host again.

Architecture Aliases Meaning
native — Follow the current host; also the default when unset
amd64 x86_64, x64 Select an x86-64 guest
arm64 aarch64, a64 Select an ARM64 guest

vm_arch applies to the entire deployment; define it once under all.vars. Every managed node must resolve to the same architecture. Node variables and selectors cannot create a deployment with mixed architectures.

--image/-I overrides BARN_IMAGE and the selected nodes’ vm_image/vm_version without changing architecture. Without the flag, a nonempty BARN_IMAGE overrides the inventory image. The catalog must provide the requested architecture variant; a missing variant is an error and never selects another architecture automatically.

barn plan -I u24                 # Ubuntu 24.04; retain the environment, inventory, or host architecture
barn up -I u24 -A amd64          # Use amd64 Ubuntu 24.04 for this invocation
barn plan -A native              # Override the inventory architecture with the host's
barn init full -I u24 -A arm64   # Save both choices in a new inventory

Overrides on up, plan, reload, recreate, resize, and validate change only the desired configuration for that invocation. To keep a choice, set all.vars.vm_arch and the relevant node or group vm_image, removing any superseded vm_version. init and template-generating setup write the choices into the new inventory. Setup’s -I/-A flags cannot be used with an existing inventory.

When no inventory file exists, lifecycle commands use the saved deployment specification before applying explicit overrides. start and restart always use applied state. Omitting -A or editing the inventory never converts an existing VM: up reports drift, and switching architecture requires recreate with all existing nodes selected. resize applies CPU/memory changes and grows supported root disks; image and architecture changes require recreate.

Guests whose architecture differs from the host use QEMU TCG. On Linux, barn setup installs the native emulator; install the target qemu-system-* binary and UEFI firmware before starting a foreign guest. See Runtime policy for acceleration and image exceptions.

Data disks

barn.yml
vm_disks:
  - path: /data
    size: 128
    fs: auto
    persistent: false

Each entry in vm_disks configures an additional virtual block device:

  • path: Absolute mount path, normalized automatically; spaces, Unicode, and shell punctuation are supported. Simple paths retain existing disk names; other paths get stable digest-based names. Identities must be unique within a node. System directories and overlapping mount paths remain invalid.
  • size: Disk capacity, defaulting to 128 (128 GiB). Accepts a GiB integer or an explicit size string.
  • fs: Filesystem type—auto (default), xfs, or ext4. On a blank auto disk, Barn formats with XFS when mkfs.xfs is available in the guest and falls back to ext4 otherwise. Explicit xfs or ext4 settings require that filesystem tool and never fall back. Healthy existing filesystems are mounted as-is.
  • persistent: When true, ordinary barn destroy and barn recreate keep the disk image on the host so the recreated node reattaches it when the disk identity and definition remain compatible. Set vm_disks: [] to create a node with no extra data disks.
Important

Data disks are disposable test storage. During up, Barn formats a blank data disk with the configured fs. It resets an existing ext4 or XFS filesystem, persistent: true disks included, only when the guest’s read-only check (e2fsck -fn or xfs_repair -n) reports recognized corruption, and records the reset in nodes[].repairs. A disk with a partition table, a signature without a filesystem UUID, another filesystem type, features the guest tools do not support, or an unrecognized checker failure keeps its data and gets a warning in nodes[].warnings; so do transient probe errors, missing block devices, busy mounts, and host I/O errors. Root disks and host shares are never formatted.

Shares

barn.yml
vm_shares:
  - host: ./src
    guest: /src
    readonly: true

vm_shares mounts host directories into Linux guests through QEMU Virtio-9p on Linux and macOS hosts, with readonly: true by default. Host QEMU and the guest kernel must support 9p; backend device limits apply. Native macOS guests use VirtioFS through barn mac --share.

Shared sources use the invoking user’s actual filesystem permissions:

  • host accepts absolute paths, ~/, inventory-relative paths, and directory symlinks. guest must be absolute and is normalized automatically; spaces, Unicode, and shell punctuation are accepted, while control characters are rejected.
  • Resolved host directories must exist, be accessible, and not overlap BARN_HOME; ownership by the invoking user is not required.
  • Host sources may overlap and may be shared by multiple readers or writers. Users coordinate concurrent writes. Guest mount targets within one node must not overlap.
  • guest paths cannot overlap data-disk mounts, system directories, or the login user’s .ssh directory. Use 9p shares for source code and build artifacts rather than PostgreSQL data directories.
  • If a writable share (readonly: false) cannot be written by the guest user, Barn mounts it read-only, reports a warning, and leaves host file ownership untouched; fix host permissions and re-run barn up to upgrade the mount.
  • Changing vm_shares on an existing node requires barn recreate. Missing host directories during up or start fail only the affected node, while restart, reload, and recreate verify share paths before stopping running VMs.

Names and addresses

Barn resolves each node’s name from the first available source: explicit nodename, <pg_cluster>-<pg_seq>, or node-<last-octet>. Node names must be unique across the deployment, be 1–63 characters long, contain only lowercase letters, digits, and hyphens, and neither start nor end with -. vm_alias accepts additional lowercase DNS names (dots allowed) that must be distinct from all node names and other aliases.

All managed nodes in a deployment must belong to a single RFC1918 /24 subnet: .1 is the host gateway, .2–.8 are reserved, and .9–.254 are available for nodes. Inside the guest, the fixed-IP NIC carries the configured inventory address (inspectable via ip -br addr).

Drift

Explicit node selectors apply only selected changes: plan node-1, up node-1, reload node-1, recreate node-1, and resize node-1 retain the applied definitions of unselected peers, even when edited or omitted in the inventory. The entire inventory must still parse correctly. Images for uncreated, unselected nodes are not resolved. Conflicts with applied addresses, aliases, control-node roles, or deployment-wide settings are still rejected.

Barn computes a specification hash for every resolved node:

  • barn up creates newly added nodes, starts stopped nodes, and retries unfinished guest provisioning on running nodes without restarting their QEMU processes.
  • Modifying a node’s CPU or memory uses barn resize <node> and preserves its identity and disks. Barn 1.2.0 also supports grow-only root capacity through resize -D. Image, data-disk, share, or other VM definition changes require barn recreate <node>. Removing a node from the inventory (or setting vm_skip: true) reports the node as missing in plan without automatically destroying it.
  • Changing deployment-wide settings (vm_arch, login username, or the /24 subnet) requires recreating the entire deployment.
  • Because plan and up resolve image channels and version prefixes against the active catalog, running barn update can introduce image drift even when barn.yml has not changed. Always keep an explicit nodename on each host so renaming pg_cluster does not turn an existing node into a missing old node plus a new node.

2.3 - CLI Contracts

Barn commands, important flags, structured output, and exit codes.

This page describes Barn 1.2.0 global presentation, Linux lifecycle semantics, structured output, and exit codes. For syntax, options, defaults, and examples, use the Command Reference. Native Mac runtime contracts are in Mac Reference.

barn [--json|--yaml] [-v|--verbose] <command> [flags] [node...]

Every command documents its local flags and examples through --help:

barn --help
barn setup --help
barn image pull --help

Running barn with no arguments prints the same complete help as barn --help and exits 0; with --json or --yaml it returns an actions[] array of suggested next commands instead. Invoking a command group without a subcommand—such as barn image—exits 2 (printing help in text mode or a usage error object in JSON/YAML mode). Explicit --help always prints human-readable help and exits 0. Use barn --version or barn version to inspect the build identity.

Commands

Area Commands
Prepare setup, init, validate, doctor
Lifecycle plan, up, start, stop, restart, reload, resize, recreate, status, destroy, purge
Access ssh, exec, logs, provision, ssh-config, hosts install/uninstall
Images update, image list/info/pull/import/sync/prune/reset, repo scan/build/verify
Host network network status/install/uninstall
macOS guests mac … — see Mac Commands
Misc version, completion

Ordinary commands read the active local image catalog without contacting remote repositories. Run barn update to fetch, verify, and activate the configured repository’s catalog, or barn image sync to recover a catalog from an exact URL or local file. Neither command replaces the barn executable.

Frequently used commands define short aliases within their scope:

Command Aliases Command Aliases
setup s validate v
plan pl recreate rc
status st destroy de
ssh-config sc image images, im
doctor dt network n, net
exec / logs ex / l version ver

up, ssh, init, start, stop, restart, reload, resize, provision, hosts, purge, update, repo, mac, and completion have no top-level shorthand (rm is not an alias for purge). Inside subcommand groups, hosts and network map i/u to install/uninstall, network status accepts st, and image maps list=ls, info=in, pull=p, prune=pr, sync=sy, and import=i.

Barn manages a single deployment per BARN_HOME (default ~/.barn; see Configuration), so commands that operate on applied state work from any directory when BARN_HOME points to the same location. Use an absolute BARN_HOME when changing working directories. Each command resolves its desired-state source independently (-f is a per-command flag rather than a global flag):

Commands Desired-state source
setup [template] Explicit -f, then local discovery, otherwise generates meta (template and -f are mutually exclusive)
init [template] Generates a new inventory without reading existing state; --force overwrites an existing file
validate Explicit -f, then local discovery; never falls back to applied state
plan, up, reload, recreate, resize Explicit -f, then local discovery, then the applied deployment specification
Other lifecycle & access commands Operate directly on applied state in BARN_HOME

When a host dependency or the Barn network is missing, up, start, restart, and reload on a terminal prepare the host as barn setup does: they show the host-preparation plan and ask once before installing packages or the network (sudo may still prompt for your OS password). When neither an inventory file nor an applied deployment exists, an interactive barn up also generates the default meta inventory first. Without a terminal these commands only report what is missing. Run barn setup --dry-run to inspect the plan, and barn setup --yes to prepare the host from a script.

If the default 10.10.10.0/24 subnet conflicts with an existing host route while preparing a fresh, unedited default template, setup selects another free RFC1918 /24 and backs up the prior template as barn.yml.before-network-change. Explicit -f inventories, edited files, and existing deployments always keep their configured subnet.

Important flags

Flag Meaning
--json, --yaml Emit structured output on stdout while keeping progress on stderr (long-form only)
-v, --verbose Emit bounded diagnostic logs on stderr
-c, --cidr Set the RFC1918 /24 subnet for init/setup templates or network inspection/installation
-I, --image Choose an image for a generated template or override selected nodes’ vm_image/vm_version on commands listed below
-A, --arch Select guest or artifact architecture: native, amd64 (x86_64, x64), or arm64 (aarch64, a64)
-f, --file Select an inventory file for commands that read desired state
-r, --repo Select an image repository URL or path, overriding --mirror and BARN_REPO
--mirror Use the China official repository (https://repo.pigsty.cc/barn) for setup, up, reload, recreate, update, and image pull
-m, --mode Select host or shared network mode on macOS
-d, --dry-run Print the execution plan for setup or image prune without modifying state
--yes Confirm host, setup, image, or destruction plans; -y is available only where listed below
--force (init, destroy, recreate) Overwrite an existing inventory (init) or skip the interactive confirmation word (long-form only, as -f means --file)
--ask (purge) Display the VM list and deletion scope, then require y/yes before deleting
-n, --no-wait Return as soon as QEMU starts, skipping guest SSH readiness and recovery checks
--rollback (up, reload) Clean up uncommitted preparation artifacts for nodes that failed during the current run
--delete-persistent Delete persistent: true data disks during a whole-deployment destroy
--purge Delete all deployment disks, SSH keys, and state during a whole-deployment destroy, while keeping cached base images

Each command exposes only the flags that apply to its operation:

Command Local controls
init --output/-o (default ./barn.yml; - writes to stdout), --cidr/-c, --image/-I, --arch/-A, --force
setup [meta|dual|trio|full] --file/-f, --cidr/-c, --image/-I, --arch/-A, --repo/-r, --mirror, --mode/-m, --dry-run/-d, --yes/-y (defaults to meta)
validate --file/-f, --repo/-r, --image/-I, --arch/-A; accepts no node arguments
plan --file/-f, --repo/-r, --image/-I, --arch/-A
up, reload --file/-f, --repo/-r, --image/-I, --arch/-A, --mirror, --no-wait/-n, --rollback
recreate --file/-f, --repo/-r, --image/-I, --arch/-A, --mirror, --yes/--force, --no-wait/-n
resize --file/-f, --image/-I, --arch/-A (still applies only CPU/memory changes)
start, restart --no-wait/-n
stop, status Node selectors and global presentation flags only
destroy --yes/--force, --delete-persistent, --purge (the last two require whole-deployment scope)
purge Deletes immediately; --ask enables confirmation; accepts no node arguments or --yes, -y, --force
doctor, version Global presentation flags only
provision Required --script/-s; --sudo; --parallel/-p positive integer (default 1); --timeout/-t positive duration (default 1h)
ssh-config Optional node selectors; --install/-i or --remove (mutually exclusive); --name (default barn). --remove takes no nodes and works without deployment state
logs --source/-s serial|qemu|events (default serial); --follow/-f (events is deployment-wide and takes no node)
image info, image pull Optional image selector, --arch/-A (legacy -a also works), --repo/-r; pull also accepts --mirror
image import --sha256/-s; named imports (--name local-*) also require --boot/-b bios|uefi and --source-user/-u, with optional --arch/-A to record guest architecture
image prune --dry-run/-d or --yes/-y (mutually exclusive); --repo/-r
image sync Source URL or file path, --repo/-r, and optional --allow-downgrade
update --repo/-r, --mirror
image list, image reset --repo/-r
repo scan/build/verify One repository directory; relative paths, ~/, and directory symlinks are supported
network install --cidr/-c (default 10.10.10.0/24), --mode/-m (default host), --yes/-y; macOS-only --archive/-a, --interface-id/-i
network status Optional --cidr/-c
network uninstall, hosts install/uninstall --yes/-y
hosts install (1.2.0) --file/-f: publish inventory nodes and vm_hosts, including before VM creation

Notes on flag behavior:

  • setup --dry-run and setup --yes are mutually exclusive. --cidr adjusts generated templates and never rewrites an existing -f file. For validate against the China mirror, pass --repo https://repo.pigsty.cc/barn.
  • init and template-generating setup write -I and -A into all.vars.vm_image and all.vars.vm_arch; setup rejects these generation flags when using an existing inventory. up, plan, reload, recreate, resize, and validate apply overrides in memory for that invocation. Image overrides affect selected nodes; architecture affects the whole deployment. See precedence and defaults.
  • Standalone image info/pull/import default to the host architecture without reading inventory vm_arch; select another with -A. image import -A requires --name. Since qcow2 metadata cannot identify the guest CPU, the declared architecture must match the actual image.
  • network and hosts install/uninstall display a plan and prompt for confirmation on a terminal (install defaults to yes; uninstall defaults to no). Without --yes, they only print the plan and exit 0 when stdin is not a terminal or the output is --json/--yaml; check applied in the structured report, or pass --yes to apply the plan. On macOS, first-time network installation is handled by barn setup. network uninstall refuses to remove the host network while VMs are running, and stopping or destroying a deployment leaves the host network installed. On Linux, network uninstall keeps the global networkd unit state when other host links exist or exclusive Barn use cannot be established; the plan reports preserve_networkd, and the applied report explains the decision in checks.networkd.
  • Safety and repository flags (--ask, --mirror, --force, --rollback, --remove, --allow-downgrade, --sudo, --delete-persistent, --purge) are long-form only. On Linux commands, -f selects --file (except barn logs -f, where -f means --follow), -n means --no-wait (whereas barn mac logs -n sets the line count), and -d means --dry-run.
  • barn purge deletes immediately by default: all nodes, persistent disks, SSH keys, deployment state, and the default SSH config fragment are removed; cached images and the host network remain. Only --ask displays the node list and deletion scope before prompting: y/yes confirms; n/no, an empty line, or EOF cancels with exit 130. It does not accept --yes, -y, or --force. With no deployment, default barn purge and whole-deployment barn destroy succeed cleanly; purge also removes retained persistent disks with intact Barn ownership metadata. Unverifiable retained directories still prevent deletion. If deployment state is missing, destroy --force --delete-persistent and destroy --force --purge direct you to barn purge.

Specification flags and SPEC display

Barn 1.2.0 adds --cpu/-C, --mem/-M, --scale/-S, and --disk/-D, sharing input rules with --image/-I and --arch/-A. Specification options are available on:

Command Added support
init, template-generating setup C/M/S/D, saved in the new inventory
validate, plan, up, reload, recreate C/M/S/D, overriding the current desired specification
resize C/M/S/D, applying CPU/memory changes and root growth; retain existing I/A checks
setup reusing an inventory Reject specification overrides and ignore specification environment variables
start/stop/restart/status/destroy/purge No specification inputs; retain applied state

See specification overrides for parameters, units, environment variables, and persistence. up/reload do not automatically apply resource changes to existing nodes; use resize or explicit recreate.

Node tables use SPEC for effective CPU and memory, such as 1c2g, 2c4g, and 8c16g. c means vCPU and g means GiB; 1536 MiB appears exactly as 1.5g, and 512 MiB as 0.5g. Capacities not divisible by MiB retain their exact byte count with b; unknown specifications show -.

status and completion results show applied resources; plan/validate/setup show desired resources. Plans show changes as 2c4g → 4c8g, with root capacity in a separate ROOT column. The init summary, live lifecycle progress, and ordinary terminal completion results also display SPEC. When scale differs from 1, output explains the base, multiplier, result, and sources; --verbose also explains calculations with scale 1. JSON retains numeric CPU and byte capacities, with bases, scale, effective results, and sources under resources; scripts need not parse the abbreviation.

In-place root disk growth

barn resize -D 128GiB meta
BARN_DISK=128GiB barn resize meta

-D is an absolute target. Repeating a completed target neither changes the disk nor restarts the VM; shrinking fails. Growth requires downtime. Running nodes return to running; stopped nodes temporarily boot for checks and expansion, then return to stopped. Supported roots use 512-byte logical sectors and a last plain ext4/XFS partition in a GPT or primary MBR layout. LVM, encrypted devices, later partitions, and missing tools fail before image modification. Existing root data, VM UUIDs, and SSH keys are retained; data disks are not resized.

Barn persists recovery stages and checks image, guest disk, partition, and filesystem capacities before completion. On failure, status shows a root-resize warning. Fix the reported problem and repeat the same resize -D target. A grown image is never shrunk to undo incomplete guest expansion. An inventory retaining the old size reports drift on the next run; update vm_disk to keep the new size permanently. See root growth acceptance for implementation details and measurements.

Lifecycle concurrency in 1.2.0

Barn 1.2.0 defaults Linux VM lifecycle concurrency to max(1, logical CPUs - 2), using the logical CPU count available to the Barn process. Each phase uses at most its selected node count. Released Barn 1.1.0 uses at most four workers and has no lifecycle --parallel flag.

In 1.2.0, up, start, stop, restart, reload, recreate, destroy, and purge accept --parallel/-p with zero or a positive integer. Omitting the flag uses the CPU-based default; explicitly passing --parallel 0 runs all selected nodes concurrently. A positive value sets the concurrency limit, and --parallel 1 runs node work serially. The override applies to disk preparation, startup, and shutdown where those phases occur; state commits and artifact removal retain their existing ordering. Values above 16 are accepted, and values above the node count create no extra workers. The option belongs to the command invocation and is preserved in suggested retry commands, including an explicit zero.

barn up -p 0
barn up --parallel 8
barn start -p 1
barn restart meta node-1 -p 2

A startup worker remains occupied while its guest boots and passes SSH/readiness checks. With --no-wait, it becomes available after QEMU starts and its process identity is verified. Disk preparation completes before the startup phase begins. provision --parallel keeps its separate default of 1; native macOS guest limits are documented under Mac commands.

Implementation and regression coverage: concurrency.go, concurrency_test.go, and lifecycle_parallel_test.go.

Native acceptance on 2026-11-10 used six Debian 13 guests in a temporary BARN_HOME on macOS/arm64 with 18 logical CPUs. The automatic default was 16 and reached six concurrent startups; an explicit value of 1 kept startup and shutdown serial; an explicit value of 32 reached six concurrent restarts. All guests passed readiness checks, and the test deployment was destroyed afterward.

On 2026-11-11, a second isolated run on the same host verified explicit zero with 18 Debian 13 guests: all 18 started concurrently and passed readiness checks in 39.6 seconds. Selecting three nodes for stop/start produced a concurrency peak of three in each phase while preserving the other 15 VM processes. The test deployment was destroyed afterward, and all six pre-existing VM processes remained unchanged. Regression tests also cover positive limits above 16, negative values rejected before state changes, and retries retaining explicit zero.

Structured failures

Always check the process exit code first, then parse the command’s output.

In human-readable text mode, operations lasting one second or longer on a terminal redraw a single live table (NAME ADDRESS STATE MESSAGE, ITEM STATE MESSAGE, or IMAGE STATE MESSAGE) on stderr above a completion line ending with [DONE], [FAILED], or [CANCELLED]. When both stdout and stderr are a terminal without --verbose, the completed table freezes in place so stdout prints only follow-up hints (connect:, retry:, next:). Ordinary failures print error: <message> on stderr, followed by the last stderr lines of any failed external program and an actionable next: command when available. SSH child exit failures pass the remote process output through directly without adding a wrapper error line.

In --json or --yaml mode, commands that define their own report schema (doctor, network, provision, lifecycle commands, and ssh/exec) return that report schema directly on stdout—for example, exec returns success, exit_code, stdout, and stderr. When a command fails before producing a command-specific report, Barn writes a single generic failure object to stdout:

  • error: One of the stable error classes in the Exit codes table (recreate_required and nodes_removed are reported as reason values under error: "conflict", and resource collisions use error: "resource").
  • message: Human-readable summary.
  • Optional context fields: reason, next, operation_id, and command (name, argv, exit_status, signal, timed_out, stderr).

Single-result commands emit one JSON or YAML document. logs --follow uses NDJSON or a YAML document stream; structured interactive SSH sends session output to stderr and its final report to stdout. The design rationale is in CLI contracts.

Lifecycle results

When invoked without node arguments, lifecycle commands target all managed nodes in the deployment, whereas ssh, exec, and per-node logs default to the prepared control node (or the first prepared node if the control node is not yet prepared). Node selectors must be resolved node names such as meta—IP addresses, group names, and vm_alias entries are not valid command-line selectors.

  • restart restarts applied nodes using their committed state without re-reading barn.yml.
  • reload validates the effective desired specification and startup dependencies before stopping selected nodes, then runs up for that selection. It can add nodes; changes that replace an existing VM still require recreate.
  • resize applies effective CPU/memory changes, including vm_scale, and supports root growth. Supported increases may be hot-plugged; decreases, older VMs, and unsupported backends use a stop/start fallback while preserving the VM identity and disks. It refuses image, data-disk, network, share, create, remove, and deployment-wide changes.
  • recreate applies changed node definitions by replacing root disks and non-persistent data disks while reattaching compatible persistent data disks. Explicit plan, up, reload, recreate, and resize selections retain unselected peers’ applied definitions, including peers edited or omitted in YAML. Uncreated, unselected nodes do not require image resolution. Conflicting addresses, aliases, control-node roles, or deployment-wide settings still fail preflight; omitting selectors applies the whole inventory.
  • When configuration drift requires a destructive action, up and reload exit with a conflict error pointing to barn plan and barn recreate <node> or barn destroy <node>. On a terminal, recreate and destroy list the nodes they replace or remove and require typing the command name unless --yes (or --force) is passed; without a terminal they exit 2 unless one of those flags is present. Without node arguments, both apply to the whole deployment.

A whole-deployment recreate refuses an inventory that omits an existing node before any VM stops (conflict, reason nodes_removed, exit 4). Remove that node explicitly with barn destroy <node>, or add it back to the inventory. Barn checks the scope again under the destruction lock; if the applied state changed during preflight, recreate exits with reason deployment_changed, and you can review the plan and retry.

Destroying the control node makes the first remaining node the control node. Update the inventory’s control role (admin_ip) to that node, then run barn up to install its control SSH access; --no-wait defers that step. Until you do, an inventory that still names the deleted node as control is role drift and up refuses it; to bring the old node back, add it as a regular node. Barn refuses to destroy the control node, before deleting anything, when the successor has an unfinished prepare record that cannot be completed, and the error names the successor.

Planning

barn plan is read-only and exits 0 even when its recommended action is resize, recreate, or blocked-removal; automation scripts should inspect action, the create, resize, recreate, start, and missing arrays, and the optional blocked explanation string.

For catalog images, plan resolves configuration and catalog metadata locally without requiring QEMU or host networking; for local-* imported images, it also verifies the cached image via qemu-img. Plans never download images and display exact image versions, total CPU/memory/disk resources, drift reasons, and per-disk actions (including the default 128 GiB /data disk).

Starting and SSH configuration

barn up verifies host capabilities and IP availability, creates new nodes, starts stopped nodes, verifies SSH readiness on running nodes, and refreshes Barn’s host SSH client fragment (~/.ssh/barn_config) and guest /etc/hosts + control-node SSH entries. recreate performs the same full refresh; start and restart also refresh SSH aliases; per-node destroy removes the destroyed node’s SSH entry; and whole-deployment destroy removes the fragment.

If a VM lifecycle action succeeds but updating ~/.ssh/config fails (or if ~/.ssh/config is a symlink or hard link, which Barn never edits), Barn writes ~/.ssh/barn_config, prints the Include line to add manually, records the notice in warnings[], and exits 0 (barn ssh continues to work directly).

On the control node, Barn-managed SSH entries accept replacement host keys without writing to known_hosts, keeping recreated lab nodes reachable while preserving any custom SSH entries you added yourself.

Readiness

A Linux guest is considered ready once management SSH succeeds. Non-fatal post-boot issues are reported in nodes[].warnings, and completed recovery actions (such as formatting a damaged data disk) are recorded in nodes[].repairs; the command still exits 0. Re-running barn up retries any unfinished guest setup steps on running VMs without restarting them.

Passing --no-wait returns as soon as the QEMU process is running, deferring SSH readiness, disk recovery, and guest /etc/hosts updates to the next barn up.

Data disks describes which disk problems Barn repairs (reported in repairs) and which it only reports (in warnings). Existing guests receive the current recovery helper on their next barn up readiness check.

Node failures

When one or more nodes fail during a batch operation, Barn preserves any nodes that succeeded, exits 5, and summarizes each failure as N of M node(s) failed: <node> (<stage>: <error>); ... across stages such as prepare, start, readiness, bootstrap, guest-setup, stop, and status. Structured output populates failures[] with node, stage, error, and optional reason (plus rolled_back when --rollback cleaned up uncommitted node preparation files). For readiness or bootstrap failures, check barn logs <node> and see A node did not become ready.

Status

barn status reports each node’s name, IP (ADDRESS), state (running means the QEMU process is alive; it does not probe guest SSH), exact image, vCPUs, and memory, flagging TCG emulation when active (and on Apple Silicon when Mac VMs exist, appends mac: <names>; barn mac ls). Pass --verbose to include forwarded SSH ports, architecture, accelerator, and QEMU PID. If a node is degraded—including when an online resize was interrupted after hotplugging (stage: "resize", reason: "resize_incomplete")—status still lists its healthy peers, populates failures[], and exits 5.

Recovery and interrupted operations

  • Fault isolation: During up or start, a missing host share or failed new-node preparation affects only that node; independent stopped nodes still start, and the command exits 5 with a retry command that preserves your -f, --repo, and flag arguments.
  • Setup event log: Host setup and its follow-up lifecycle run share one operation_id. If initial setup fails before a deployment exists, you can still inspect the bounded phase log via barn logs --source events --json (which records phase transitions without sensitive arguments or credentials; setup --dry-run writes no log).
  • Retained persistent disks: Persistent disks left behind by a previously removed node do not block destroying the remaining nodes; use destroy --delete-persistent or purge to remove them.
  • macOS setup ordering: On macOS, setup finishes Homebrew discovery/installation or the pinned socket_vmnet archive download before prompting for administrator privileges, so downloads neither expire an earlier sudo token nor prompt for a password when a download fails.
  • PID and journal reconciliation: If a recorded QEMU PID has been reused by an unrelated process, Barn marks the node stopped; if a stop was interrupted while the VM process remained alive, Barn marks it running. If an online resize was interrupted after modifying the live guest, status reports resize_incomplete; retry barn resize <node> or run barn restart <node>. Other interrupted transitions print the recovery command (destroy resolves interrupted state directly), and a failed initial up automatically rolls back uncommitted journal artifacts on the next run.
  • Cancellation: When a command is cancelled, Barn sends SIGTERM to the external program it is running, which sudo forwards to the privileged command, and waits at most 10 seconds for that process and its inherited output pipes. Descendants that ignore SIGTERM need external supervision.

Logs and environment

barn logs reads the guest serial console (--source serial) by default. Pass --source qemu for QEMU stderr diagnostics or --source events for the deployment-wide bounded event log. Adding --follow (-f) streams raw bytes in text mode, NDJSON lines in --json mode, or YAML documents in --yaml mode. QEMU argv is omitted from normal log output unless --verbose is set.

Environment variable Purpose
BARN_HOME Private state directory (default ~/.barn); accepts relative paths, ~/, and directory symlinks, but not / or the home root. Prefer an absolute path across working directories
BARN_IMAGE/BARN_ARCH/BARN_CPU/BARN_MEM/BARN_SCALE/BARN_DISK 1.2.0 VM specification overrides; CLI takes priority. Read only by commands accepting each field; see specification overrides
BARN_REPO Default image repository URL or path; overridden by --mirror and --repo
BARN_OUTPUT Default output format (text, json, or yaml); overridden by --json/--yaml
BARN_VERBOSE Boolean default for diagnostic output; overridden by -v/--verbose
BARN_VMNET_ARCHIVE Path to a local pinned socket_vmnet archive for macOS setup; accepts relative paths, ~/, and symlinks (SHA-256 verification still applies)
NO_COLOR Any non-empty value disables ANSI color output

Explicit --json, --yaml, and -v/--verbose override BARN_OUTPUT and BARN_VERBOSE, and --help works even when those variables hold invalid values. Any other command exits 2 (usage) while an invalid value is not overridden on the command line.

SSH passthrough and completion

barn ssh [node] [-- command...]
barn exec [node] -- command...
Tip

Remote commands require --. Pass barn exec meta -- hostname rather than barn exec meta hostname, and use sh -c explicitly for a shell command string.

  • barn ssh without a command opens an interactive login shell; with -- command..., it joins the arguments after -- with spaces and passes them to the guest shell.
  • barn exec requires -- command... and passes the program and each argument separately, preserving spaces, quotes, empty arguments (after the first non-empty command token), and shell metacharacters literally. Wrap pipelines, redirections, variable expansion, or compound commands in sh -c '...'.
  • Positional arguments before -- may contain at most one known node name (defaulting to the control node when omitted). Unknown node names or extra selectors before -- fail immediately with exit code 2 (usage), listing known nodes and suggesting barn status.
  • Barn flags (--json, -v, --help) must appear before --; everything after -- (including a second --, -V, or -o...) is passed to the remote command and cannot alter local OpenSSH client options.
barn ssh meta
barn ssh meta -- 'df -h /data; id'
barn exec meta -- hostname
barn exec meta -- printf '<%s>\n' 'a b' ''
barn exec meta -- sh -c 'id; df -h /data'
barn exec meta --json -- uname -a

Generate shell completion with barn completion bash|zsh|fish|powershell (for Bash, reload after upgrades via source <(barn completion bash)). Completion suggests commands, aliases, templates, image aliases, enumerated flags, YAML files for -f, and node names from the current inventory or applied state, and stops suggesting Barn flags after --.

Exit codes

Code error Meaning
0 Success (including ready guests with non-fatal warnings[] or repairs[])
1 runtime Operation executed and failed (external tool, download, or guest failure)
2 usage Invalid CLI syntax, missing inventory file, or invalid configuration/image selector
3 capability Host lacks a required dependency, active Barn network, or privilege
4 conflict Operation conflicts with current deployment state or another active barn process
5 partial Node-level batch failure; inspect failures[] (successful peer nodes remain intact)
6 resource Host IP address, port, subnet, or disk path is already in use
7 integrity Digest, catalog signature or signed revision, instance identity, or file ownership verification failed
130 cancelled Interrupted by SIGINT/SIGTERM or interactive confirmation declined

An image catalog whose signature is missing or does not verify fails with reason: "catalog_signature", and a catalog that reuses an accepted or embedded revision number with different bytes fails with reason: "catalog_equivocation". Permission and I/O errors while reading a cached catalog signature are runtime failures (exit 1).

When another barn process holds the deployment lock, mutating commands wait up to 10 minutes while reporting the lock holder, then exit 4 with reason: "deployment_busy" on timeout. Read-only and access commands (status, ssh, exec, ssh-config, hosts) do not wait on the lock; status displays the last committed state and notes the active operation in note.

ssh and exec return the remote command’s exit code verbatim (including 255, which can indicate either an OpenSSH transport failure or a remote command exiting 255), keeping text output, JSON exit_code, and process exit status identical.

2.4 - Mac Runtime Reference

barn mac commands and flags, machine rules, JSON results, failure reasons, networks, and files.
barn [--json|--yaml] [-v|--verbose] mac <command> [flags] [name...]

Running macOS guests requires an Apple Silicon Mac on macOS 27 or later, invoked as the logged-in desktop user. Commands that talk to the native runner reject root with mac_root and unsupported hosts with mac_host_unsupported. Top-level --help hides mac on Linux and Intel Macs, while local-state queries such as barn mac image ls work without invoking the runner. Running barn mac with no subcommand executes barn mac ls.

This reference covers the Barn 1.2.0 command interface and Barn.app, the native macOS app bundle that runs the VMs (runner protocol 2). The Apple Silicon release and Homebrew installation include the app; see macOS Guests.

For complete per-command syntax, options, and examples, see the macOS Command Reference. This page collects machine rules, networking, result formats, and state files.

Commands

Command Purpose
ls List every VM with state, IP, SSH status, macOS build, resources, and shares (aliases: list, status, st)
up [name] Create the VM if needed, start it, and wait for SSH and passwordless sudo
start [name...] Start existing VMs
stop [name...] Request a clean macOS shutdown, powering off after two minutes if still running
restart [name] Stop and start a VM, applying any pending hardware, share, or subnet changes
open [name] Open the desktop window, starting the VM first if needed
ssh [name] [-- command...] Open an interactive shell, or pass a command string after -- to the guest shell
exec [name] -- command... Execute a remote program while preserving argument boundaries
configure name Update CPUs, memory, shares, or subnet while stopped; clipboard can change while running
recreate name Replace a VM with a fresh macOS clone while keeping its hardware and network settings
destroy name... Delete one or more VMs
password [name] Print or copy the guest login password
ssh-config Print, install, or remove ~/.ssh/barn-mac_config entries
logs [name] Read recent lines from the VM’s runner.log
setup Download and prepare the default macOS base image without creating a VM
image ls List cached .ipsw restore files and prepared base images alongside the VMs using them (alias: list)
image update Download and prepare Apple’s latest macOS 27 build as the default base image
image prune List unused images, or delete them when --yes is passed
doctor Check host eligibility, Barn.app, base images, and every VM slot

SSH and exec

Tip

Remote commands require --, for example barn mac exec dev -- sw_vers. Interactive barn mac ssh dev needs no separator.

Before --, pass Barn flags and at most one existing VM name (omitting the name selects the sole VM or mac1). ssh without a command opens an interactive shell; exec requires a non-empty command after --. Unknown VM names or invalid arguments fail with exit code 2 (usage) before attempting a connection and point to barn mac ls.

ssh joins arguments after -- into a command string for the guest shell, whereas exec passes the program and arguments separately (preserving spaces, quotes, and subsequent empty arguments). Use sh -c '...' for pipelines, redirection, or compound commands. Put Barn flags (--json, -v, --help) before --; everything after -- belongs to the guest command.

For compatibility, exec also accepts a single command string as shell shorthand. In new scripts, pass separate arguments or use an explicit sh -c so the intended shell interpretation is clear.

barn mac ssh dev
barn mac ssh dev -- 'sw_vers; id'
barn mac exec dev -- sw_vers
barn mac exec dev -- printf '<%s>\n' 'a b' ''
barn mac exec dev -- sh -c 'sw_vers; id'
barn --json mac exec dev -- sh -c 'echo out; exit 3'

Flags by command

Command Flags
up --cpu, --memory, --disk, --user, --share, --clipboard, --subnet, --ipsw, -y/--yes, -n/--no-wait, --open
start --all, -n/--no-wait, --open, --recovery
stop --all, --force
restart -n/--no-wait, --open
configure --cpu, --memory, --share, --unshare, --clipboard, --subnet
recreate --update, --yes/--force, -n/--no-wait
destroy --yes/--force
password -c/--copy
ssh-config -i/--install, --remove
logs -n/--lines positive integer (default 100); returns the requested line count
setup --ipsw, --disk, -y/--yes
image update --ipsw, -y/--yes
image prune --installers, -y/--yes

When a VM name is optional, omitting it selects the only existing VM or mac1; if multiple VMs exist and none is named mac1, Barn asks you to specify a name. start and stop accept multiple names or --all. On a terminal, destroy and recreate print what will be deleted and ask you to type the command name; --yes (or --force) skips the question and is required without a terminal. Neither command has a -y shorthand, and mac stop --force means immediate power-off.

--no-wait returns as soon as the VM process is running; for a newly created VM, run barn mac up NAME without --no-wait to finish SSH key installation and sudo setup. Ordinary start requires a VM that has completed its first boot—if an uninitialized VM is already running, use barn mac open NAME to view its first boot, or barn mac up NAME --open if it is stopped. Passing --recovery boots directly into the macOS Recovery desktop without waiting for normal SSH readiness.

Flag values

Flag Value
--cpu vCPU count: at least 2 and at most the Mac’s logical CPU count (default 4)
--memory Memory size in binary units (16G, 16GiB, 16GB) or exact bytes; must be a whole MiB between 4 GiB and host RAM (default 8 GiB)
--disk Base image disk capacity, minimum 32 GiB (defaults to the prepared base image’s capacity, or 100 GiB for the first base image; requesting another size installs a matching base image if needed)
--user Guest administrator username (defaults to your host macOS username, or barn if the host name is not a valid account identifier)
--share [name=]path[:ro|:rw], repeatable; name defaults to the last path component and ~/ expands to your home directory
--clipboard on or off (also accepts true/false, yes/no; default on)
--subnet Canonical private /24 such as 10.10.30.0/24, or auto for the first free subnet

If you run up on an existing VM with creation flags that differ from its recorded settings, up exits with mac_configuration_conflict and prints the exact configure or recreate command in next::

  • Use barn mac configure while the VM is stopped to change --cpu, --memory, --share/--unshare, --subnet, or --clipboard (clipboard changes can also be saved while the VM is running and take effect on the next start; use the menu bar item to toggle clipboard sync for the active session). Share names may contain spaces and Unicode, must be valid UTF-8 of 1–255 bytes, and cannot be ., .., or contain / or ASCII control characters; reusing a share name in configure --share updates its path and mode, while --unshare removes that share.
  • The guest administrator account (--user) and disk capacity (--disk) are fixed for the lifetime of a VM and preserved across recreate. Create a new VM if you need a different account or disk size.
  • To upgrade a VM to a newer macOS build, run barn mac image update followed by barn mac recreate NAME --update. Because recreate --update preserves the VM’s disk capacity, if the new macOS build does not yet have a base image at a custom disk size (for example, 200G), prepare it first with barn mac setup --disk 200G.

Machines

  • Names: 1–32 characters starting with a lowercase letter and containing lowercase letters, digits, or internal hyphens (for example mac1, dev, build-2).
  • Two-VM host limit: Apple’s Virtualization framework allows at most two macOS VMs to run simultaneously on a single Mac across all apps (including an active .ipsw base-image installation). Barn never stops a running VM to make room for another. ls.running counts VMs in the current BARN_HOME, while Apple’s framework enforces the host-wide limit.
  • Administrator account: Configured with passwordless sudo, Ed25519 SSH key authentication, automatic desktop login, and Remote Login enabled. SSH password authentication is disabled, and the random local password is saved in the VM slot’s password file (0600).
  • Hostnames: The guest ComputerName matches <name> and LocalHostName is barn-<name> (barn-<name>.local).
  • Shared folders: Mounted inside the guest via a single VirtioFS device at /Volumes/My Shared Files/<name>. Each host source must resolve to an existing directory; symlinks are supported. Names accept spaces and Unicode.
  • Clipboard sync: Syncs plain text up to 1 MiB over the VM’s SSH connection whenever the desktop window gains or loses focus, skipping pasteboard items marked concealed.
  • Shutdown: barn mac stop requests a clean guest shutdown and waits up to two minutes before powering the VM off ("forced": true); --force powers off immediately.

Networks

Each VM’s runner process creates an isolated user-space network at startup and tears it down on exit—without root privileges or a background daemon—using Apple’s shared-mode vmnet API:

Item Value
Subnet First free /24 from 10.10.20.0/24 through 10.10.59.0/24 that avoids host routes and other VMs (or an explicit --subnet)
Gateway .1 on the Mac host
Guest IP .10, assigned via a vmnet DHCP reservation for the VM’s MAC address
Topology Host-to-guest routing and outbound NAT per VM subnet, with no LAN bridging or inbound port forwards

If a newly added host route (such as a VPN) overlaps the VM’s saved subnet, start refuses to boot with mac_subnet_in_use; run barn mac configure NAME --subnet auto to move the VM to a free subnet. Because Barn pins SSH host keys to barn-mac-<instance_id> rather than the IP address, changing subnets keeps SSH trust intact.

If macOS Local Network privacy blocks a third-party terminal or IDE from reaching the guest subnet (often surfacing as No route to host), enable that app under System Settings → Privacy & Security → Local Network. Barn routes its own Go SSH transport through /usr/bin/nc and invokes /usr/bin/ssh for user sessions so Apple system binaries handle the local-network socket directly.

JSON output

All barn mac commands except interactive ssh support --json and --yaml on stdout while writing progress to stderr.

ls

{
  "schema_version": 2,
  "root": "/Users/alice/.barn/mac",
  "prepared": true,
  "base": {"version": "27.0", "build": "26A428", "base_id": "26A428-e16af589f4b705ca397f5b21"},
  "machines": [
    {
      "name": "mac1",
      "kind": "macos",
      "state": "running",
      "ready": true,
      "ssh": "ready",
      "address": "10.10.20.10",
      "address_stable": true,
      "ssh_host": "10.10.20.10",
      "ssh_port": 22,
      "user": "alice",
      "image": {"version": "27.0", "build": "26A428", "base_id": "26A428-e16af589f4b705ca397f5b21"},
      "cpus": 4,
      "memory_bytes": 8589934592,
      "disk": {"capacity_bytes": 107374182400, "allocated_bytes": 1202647040},
      "network": {"subnet": "10.10.20.0/24", "gateway": "10.10.20.1", "address": "10.10.20.10"},
      "shares": [{"name": "src", "host": "/Users/alice/src", "guest": "/Volumes/My Shared Files/src", "readonly": false}],
      "clipboard": true,
      "pid": 2545,
      "instance_id": "b1482ffc-2da7-45d9-ace5-c5f3193a9172",
      "warnings": []
    }
  ],
  "running": 1,
  "limit": 2
}
Field Values
state prepared (created, never completed first boot), starting, running, stopping, stopped, unknown
ready true when this check verified guest SSH connectivity and passwordless sudo
ssh ready, pending (first boot in progress), unavailable, offline, unchecked
observed Present when the running guest reports a macOS build different from its base image
window_visible Present while the VM’s desktop window is open
error, warnings Last recorded failure on the slot and any SSH issues detected during this check

Lifecycle results

Single-machine operations (up, restart, open, recreate, configure) return a single machine result object, while batch operations (start, stop, destroy) return {"machines": [...]} even when called with one name. If a multi-VM batch partially fails, the response includes both the succeeded entries and the failures and exits 5.

{"name": "mac1", "action": "created", "state": "running", "ready": true,
 "address": "10.10.20.10", "user": "alice", "version": "27.0", "build": "26A428"}
Field Values
action created, started, running (already running), restarted, recreated, opened, configured, stopped, powered_off, already_stopped, destroyed, absent
forced true when stop timed out and powered off the VM
window true when the desktop window was opened
warnings Non-fatal notices, such as a clipboard change that takes effect on next start

Other structured responses:

  • exec --json: Returns the same schema as Linux barn exec --json (node, exit_code, stdout, stderr).
  • password --json: Returns the password, or {"name": "mac1", "copied": true} when combined with --copy.
  • ssh-config --json: Returns fragment when printing, or installation status when combined with --install/--remove.
  • logs --json: Returns name and logs.
  • doctor --json: Returns ok and checks[] (name, ok, detail, and optional next), exiting 3 if any check fails.

Failures

Exit codes follow the Barn CLI exit code table, and ssh/exec pass through the remote command’s exit status. Structured error objects include stable reason codes and a next recovery command:

Reason Exit Meaning and next step
mac_host_unsupported 3 Host is not Apple Silicon or is running macOS older than 27
mac_runner_missing 3 Barn.app / native runner is not installed alongside the barn CLI
mac_runner_protocol 3 Installed Barn.app uses a different runner protocol version; reinstall matching CLI and app builds
mac_root 2 Invoked as root; run as the logged-in desktop user without sudo
mac_download_consent 2 Non-interactive macOS download requires --yes (or supply a local file with --ipsw)
mac_machine_absent 4 Named VM does not exist; create it with barn mac up NAME
mac_not_initialized 4 VM has not finished its first boot; complete initialization with barn mac up NAME
mac_not_running 4 ssh or exec called on a stopped VM; run barn mac start NAME
mac_running 4 Requested configuration change requires the VM to be stopped; run barn mac stop NAME
mac_configuration_conflict 4 up flags differ from the existing VM; run the suggested configure or recreate command
ssh_config_linked 4 ~/.ssh/config is a symlink or hard link; add the printed Include line manually
mac_vm_limit 6 Two macOS VMs are already running on this Mac; stop the reported VM first
mac_subnet_in_use 6 A host route overlaps the VM’s subnet; run barn mac configure NAME --subnet auto
disk_full 6 Insufficient free disk space to download or install the macOS base image
mac_machine_damaged 7 An initialized VM slot is missing its disk or identity files (the slot directory is preserved for inspection)
mac_readiness_interrupted 130 barn mac stop interrupted a first-boot SSH readiness wait

Files

$BARN_HOME/mac

  • config.jsonInstallation identity, default base image, and ssh_config_off flag
  • images/
    • ipsw/
      • <build>.ipswCached Apple restore image (.partial while downloading)
      • <build>.ipsw.jsonRestore image metadata and digest
    • base/
      • <id>/Read-only, unbooted macOS base image bundle
  • slots/
    • <name>/
      • state.jsonMachine record (schema 2)
      • disk.asifCopy-on-write APFS disk layer cloned from the base image
      • machine-id.binApple Virtualization machine identifier
      • auxiliary-storage.binNVRAM / boot auxiliary storage
      • id_ed25519Per-VM SSH private key
      • id_ed25519.pubPer-VM SSH public key
      • known_hostsPinned guest host key under barn-mac-<instance>
      • passwordGenerated guest login password (mode 0600)
      • runner.logPer-VM runtime log read by barn mac logs
  • runtime/Global state lock and per-machine operation locks

Additional host and guest paths:

  • Control sockets: /tmp/barn-mac-<uid>-<hash>/.
  • OpenSSH fragment: barn mac ssh-config manages ~/.ssh/barn-mac_config and a # barn-mac:include block in ~/.ssh/config (independent of the Linux # barn:include block). Running barn mac ssh-config --remove sets ssh_config_off in config.json so subsequent lifecycle commands do not re-add the Include block until you run barn mac ssh-config --install.
  • Desktop preferences: Barn stores window positions in ~/Library/Preferences/com.pgsty.barn.plist (0.9.0 used ~/Library/Preferences/io.pgsty.barn.mac-runner.plist).
  • Runner discovery: The barn CLI locates the runner relative to its resolved binary path across app bundle (Barn.app/Contents/MacOS/barn-mac-runner), pkg installer, and Homebrew libexec layouts, or via BARN_MAC_RUNNER with an absolute path, relative path, or ~/ path. Before executing VM operations, the CLI verifies runner protocol 2; barn mac doctor also verifies the runner’s local code signature.
  • Guest configuration files: Inside the macOS guest, Barn writes ~/.ssh/authorized_keys, /private/etc/sudoers.d/80-barn, and /etc/ssh/sshd_config.d/000-barn.conf, configures ComputerName and LocalHostName, and disables idle sleep via pmset.

Image pruning keeps base images that interrupted recreates still reference until recovery cleanup finishes. A malformed recovery journal stops barn mac image ls, the space estimate in barn mac doctor, and pruning until it is repaired, and every image stays in place. If one machine’s state cannot be read, barn mac ls fails for all machines, while commands that name the other machines keep working.

2.5 - Images

Signed catalogs, built-in aliases, repository selection, local cache verification, imports, and pruning.

Barn resolves Linux guest images from a static catalog and immutable qcow2 artifacts. Official and HTTP repositories require Ed25519/minisign catalog signatures, while explicitly selected local directory and HTTPS repositories may be unsigned. You can activate newer image catalogs with barn update without replacing the barn binary; the binary defines the trusted signing public keys and qcow2 safety rules.

Warning

el7 (CentOS Linux 7.9) is a deprecated compatibility image. Every other version in the embedded catalog is supported.

Aliases and pull order

Barn 1.2.0 embeds image catalog 2026110502, including Debian 13 image 20261103.2651.1. It covers 9 distribution families with one stable version each, for 17 image artifacts: el7 is amd64-only, and every other family provides both amd64 and arm64. When no image is specified, Barn selects u24:stable (Ubuntu 24.04 LTS) on the host’s native architecture.

The table below lists the stable channel targets in embedded catalog 2026110502. Run barn update followed by barn image list to inspect the latest catalog published on your selected repository, and see Platforms and Limits for host support details.

Family Embedded stable Accepted aliases
d12 20261026.2643.1 debian12, bookworm
d13 20261103.2651.1 debian13, debian, trixie
el7 7.9.20221112.0 c7, centos7, centos79
el8 8.10.20240528.3 rocky8
el9 9.8.20260525.2 rocky9, rocky
el10 10.2.20260525.0 rocky10
u22 20261020.0.1 ubuntu22, ubuntu2204, jammy
u24 20261017.0.1 ubuntu, ubuntu24, ubuntu2404, noble
u26 20261016.0.1 ubuntu26, ubuntu2604, resolute

Distribution notes:

  • u22, u24, and u26 are Ubuntu 22.04, 24.04, and 26.04 LTS; d12 and d13 are Debian 12 and 13. Barn’s image recipe builds them from the distributions’ cloud images: the image ships a dba user (UID/GID 88), XFS userspace tools are present, and en_US.UTF-8 is available while C.UTF-8 stays the default locale.
  • el7 is CentOS Linux 7.9 (amd64 BIOS), and el8, el9, and el10 are Rocky Linux (amd64 and arm64 UEFI). el8 and el9 go through the same recipe (dba, UID/GID 88); el10 and el7 are the distributions’ unmodified cloud images.
  • cloud-init configures accounts and networking on first boot for every image.
  • Running barn update updates what :stable or a numeric prefix resolves to for new nodes without altering running VMs or pinned @version selectors. If an existing inventory uses a channel or prefix whose target changed in the updated catalog, barn plan and barn up report the image drift and require barn recreate before replacing any node.
Alias Distribution Architectures Boot Status
el7 CentOS Linux 7.9 / 2211 amd64 BIOS deprecated
el8 Rocky Linux 8.10 amd64, arm64 UEFI supported
el9 Rocky Linux 9.8 amd64, arm64 UEFI supported
el10 Rocky Linux 10.2 amd64, arm64 UEFI supported
d12, d13 Debian 12 / 13 amd64, arm64 UEFI supported
u22, u24, u26 Ubuntu 22.04 / 24.04 / 26.04 LTS amd64, arm64 UEFI supported
barn image list
barn image info d13
barn image info d13:stable
barn image info el9@9.8
barn image pull d13@20261103.2651.1
barn image pull d13 --arch arm64
barn update

barn image list prints every version and architecture in the active catalog along with registered local-* aliases. barn image info defaults to the host architecture (like image pull) and can inspect uncached catalog metadata without QEMU; if a local cache file is present, info also runs qemu-img verification on it. Neither list nor info downloads image files.

Catalog status values indicate support tier (supported, testing, deprecated, or unknown); non-supported images still boot normally and emit a status warning.

When pulling an image, Barn executes four steps:

  1. Load the active catalog — Reads the selected repository’s active local catalog once for the command (either the binary’s embedded catalog or the revision last activated for that repository by barn update or barn image sync).
  2. Resolve the selector and architecture — Resolves image[:channel] or image@version-prefix (defaulting to u24:stable on the official catalog) and the target architecture. Standalone image pull defaults to the host; lifecycle commands use vm_arch, also defaulting to the host when unset. --arch/-A overrides either choice; see configuration precedence.
  3. Verify the local cache — Reuses an existing cached file immediately if its byte size, SHA-256 digest, and qemu-img structural checks all pass.
  4. Download with resume and digest checks — Downloads the artifact from the selected repository with automatic retries and HTTP range resumption, verifying the final size, SHA-256, and qcow2 header before moving it into the cache. The two official mirrors (https://repo.pigsty.io/barn and https://repo.pigsty.cc/barn) can fall back to each other during artifact downloads, while custom --repo sources download exclusively from the specified root. Upstream URLs recorded in the catalog serve as provenance metadata only.

Repository selection precedence is --repo → --mirror (https://repo.pigsty.cc/barn) → BARN_REPO → compiled default (https://repo.pigsty.io/barn). Both official endpoints require valid minisign catalog signatures.

Because each repository root maintains its own active catalog state in $BARN_HOME/images/manifests/, pass the same --repo (or BARN_REPO) when working with a custom repository—even after its images are cached locally—and run barn update --repo <root> before using custom aliases from a newly selected repository. Once the catalog and image files are cached and verified, all ordinary commands work offline.

Barn automatically restores read-only permissions (0444) on verified cache files. If an unreferenced cached file fails verification, Barn renames it to .corrupt-<timestamp> before re-downloading; if a VM still uses that base image, Barn leaves the file in place and reports an integrity error.

Runtime policy

When guest and host architectures match, Barn uses hardware virtualization (Apple HVF on macOS, KVM on Linux) with one built-in exception: the stock Rocky Linux 8 arm64 kernel uses a 64 KiB page size that Apple HVF does not support, so Barn automatically runs el8 on Apple Silicon under same-architecture QEMU TCG and flags tcg in status output. Cross-architecture guests (after resolving vm_arch and any command override) also run under TCG; for amd64 guests on arm64 hosts, Barn pins TCG to a single translation thread to preserve x86 memory ordering.

The ARM64 TCG runtime uses cortex-a72 to avoid firmware hangs with the max CPU model in some QEMU/AAVMF combinations. ARM64 guests on Linux x86-64 have passed an actual Barn boot and SSH check. Barn’s catalog and runtime currently accept only amd64 and arm64; other architectures offered by a distribution or QEMU still require Barn integration before use with barn up.

el7 is supported only on native linux/amd64. On Linux hosts, barn setup installs the native QEMU package set; before booting a foreign vm_arch with up or recreate, install the target architecture’s qemu-system-* emulator and UEFI firmware (barn plan can resolve catalog images without them, though local-* imports still require qemu-img).

barn image pull d13 --mirror
barn image pull d13 --repo https://mirror.example/barn
BARN_REPO=/absolute/local/repository barn up

Trust and verification

Release binaries embed both production Ed25519 public verification keys (private signing keys live outside the source repository). Catalog activation rejects unknown signing keys, malformed JSON, conflicting content at the same revision number (equivocation), and revisions lower than the repository’s highest accepted revision unless --allow-downgrade is passed to barn image sync. A missing or failed signature and an equivocating revision exit 7 (integrity; see exit codes). A new trusted signature over identical catalog bytes is accepted, which also repairs a damaged cached signature.

Every cached or imported image must be a standalone qcow2 file matching its declared byte size and SHA-256 digest, with no backing file, external data file, encryption, or unknown incompatible qcow2 feature bits. Barn marks verified base images read-only and creates copy-on-write overlay disks for each VM node.

barn update
barn image sync --repo https://repo.example/barn \
  https://repo.example/barn/catalog.json
barn image sync --repo /srv/barn --allow-downgrade /srv/barn/catalog.json
barn image reset --repo /srv/barn
  • barn update fetches, verifies, and activates the current catalog from --repo or --mirror.
  • barn image sync activates a catalog from an explicit URL or file path (for unsigned custom repositories, the source path must be <repo>/catalog.json) and supports --allow-downgrade.
  • barn image reset reverts the active catalog for --repo (or default repo) back to the binary’s embedded catalog while keeping the recorded highest-revision watermark intact.

Static repository format

A static Barn image repository uses the following directory layout:

/srv/barn

  • repo.yamlAuthoritative human-edited policy and version declarations (schema 1)
  • catalog.jsonGenerated runtime image catalog with digests and sizes (schema 3)
  • catalog.json.minisigMinisign signature (required for official and HTTP repositories)
  • images/
    • <image>-<version>-<arch>.qcow2Immutable qcow2 image artifacts

repo.yaml (schema: 1) declares defaults, aliases, channels, versions, architectures, boot modes, support status, source_user (for example, rocky upstream or dba after official normalization), and optional upstream provenance URLs—without manual checksums or byte sizes. barn repo build inspects the qcow2 files and generates catalog.json (schema: 3) with each variant’s filename, SHA-256 digest, file size, and virtual disk capacity.

repo.yaml
schema: 1
revision: 1
defaults: { image: u24, channel: stable, arch: native, boot: uefi }
images:
  u24:
    aliases: [ubuntu24, noble, ubuntu]
    channels: { stable: "1" }
    versions:
      "1":
        status: unknown
        variants:
          amd64: {}
          arm64: {}

When a variant omits file, Barn expects images/<image>-<version>-<arch>.qcow2 (for example, images/u24-1-amd64.qcow2 and images/u24-1-arm64.qcow2).

Repository management rules (barn repo scan|build|verify /absolute/repo):

  • Repository roots accept relative paths, ~/, and directory symlinks. The root and images/ may be shared writable directories. repo.yaml may link to a non-empty regular file up to 4 MiB. Artifact entries remain regular files; catalog.json output symlinks are not overwritten.
  • Boot mode inherits along variant → family → defaults.boot; status inherits along variant → version → unknown. Every family must define the default channel pointing to an existing version, and family/alias names must be unique and cannot start with local-. Avoid reassigning built-in aliases (ubuntu, debian, rocky, etc.) to different families, because inventory parsing normalizes built-in aliases via the embedded catalog first.
  • Channels and dot-separated numeric prefixes (el9@9.8, el9@9) resolve to an exact version key when present or the highest matching numeric version, yielding an immutable artifact identity (image, exact version, arch):
    d13:stable + native
      -> d13@20261103.2651.1 + arm64
      -> images/d13-20261103.2651.1-arm64.qcow2
  • barn repo scan is read-only and does not require qemu-img; it exits 0 while reporting any missing, unsafe, or untracked files in its output arrays (automation must check that missing and unsafe are empty).
  • barn repo build and barn repo verify require qemu-img. build validates repo.yaml, inspects every qcow2 artifact, and atomically writes catalog.json (failing if any declared artifact is missing or unsafe, and omitting untracked files). verify checks that the existing catalog.json matches a fresh build byte-for-byte. Increment revision whenever catalog contents change, upload immutable .qcow2 files before updating catalog.json and catalog.json.minisig, and publish the catalog and signature together.

Local layout and imports

Barn stores cached catalogs, downloaded images, and local imports under $BARN_HOME/images (default ~/.barn/images):

$BARN_HOME/images

  • manifests/Active catalog.json and highest accepted revision per repository
  • local-images.jsonRegistry of named local-* image aliases
  • local/
    • <sha256>.qcow2Imported local qcow2 files indexed by content digest
  • d13/
    • d13-20261103.2651.1-arm64.qcow2Verified read-only catalog artifacts by family
  • u24/
    • u24-20261017.0.1-arm64.qcow2

Import a local qcow2 file into the cache or register it under a local-* alias:

barn image import --sha256 <digest> /path/to/base.qcow2
barn image import --name local-mybase --boot uefi \
  --source-user ubuntu --sha256 <digest> /path/to/base.qcow2

Import rules:

  • image import accepts relative paths, ~/, and symlinks to regular files, and requires qemu-img. Reads are bounded by the actual local file size or declared catalog size; downloads stop on overflow, while legacy downloads without a declared artifact size are bounded at 8 GiB. Optional --sha256 verifies an expected digest.
  • image import copies and validates the qcow2 container as-is without modifying guest credentials, installing cloud-init, or booting the image.
  • Source filenames may contain spaces or Unicode and need no particular extension; Barn validates the qcow2 contents. New imports use local/<sha256>.qcow2, so different images can share a source filename and identical bytes reuse a cache entry. Existing aliases keep their original cache paths when reimported. Updates that exceed the 1 MiB registry metadata limit fail before changing existing aliases.
  • Named imports (--name local-<alias>) require --boot bios|uefi and --source-user <user> and default to the host architecture. --arch/-A can record an explicit amd64 or arm64 architecture, including aliases. The qcow2 metadata cannot identify the guest CPU, and import does not convert architectures. Match inventory vm_arch or the lifecycle command’s -A when using the image. Aliases are immutable and can be referenced via vm_image: local-mybase; unnamed imports only pre-populate the cache and cannot accept -A.

Pruning

barn image prune --dry-run
barn image prune --yes

barn image prune (or barn image prune --dry-run) lists candidate files that can be reclaimed; passing --yes deletes them.

prune protects all artifacts referenced by the selected repository’s active catalog, all base image digests used by currently applied nodes, and all registered local-* aliases (preserving cached images across destroy and purge). Only unreferenced images from older catalogs or other repositories and recognized stale staging files become candidates; quarantined .corrupt-* files are left in place for manual inspection, and unexpected/unsafe files cause an error. Always preview with --dry-run (passing --repo when checking a custom repository), as prune --yes deletes candidates sequentially without rolling back earlier deletions if a later file fails inspection.

To export the binary’s embedded schema-3 catalog byte-for-byte when bootstrapping a mirror, run go run ./tools/catalogexport /absolute/new/catalog.json. Any public catalog published at the embedded revision number must match those exact bytes.

2.6 - Image Pipeline

Validate or offline-normalize an explicit qcow2 candidate without downloading, uploading, or signing it.

Use packaging/image-pipeline/build.sh in the Barn source checkout to validate or offline-normalize a downloaded qcow2 candidate against an expected SHA-256 digest. The builder runs locally without network access, signing keys, or changes to Barn runtime state, and produces a testing candidate bundle ready for smoke testing and release review.

Host requirements:

  • Tools: Python 3 and qemu-img for all modes; offline mode also requires working libguestfs tools (virt-customize and virt-cat). You can override tool binaries with --qemu-img, --virt-customize, and --virt-cat or the QEMU_IMG, VIRT_CUSTOMIZE, and VIRT_CAT environment variables.
  • Out-of-tree storage: Keep source images, RPM/DEB package caches, and output directories outside the Git working tree (the repository tracks recipes and lockfiles only).
  • Architecture: --arch records the target metadata architecture; run offline customization on a host where libguestfs can execute the guest’s architecture.

Modes and profiles

  • validate: Copies and re-hashes the source image, verifies a single-file qcow2 structure with qemu-img check, and writes an evidence bundle without modifying guest files or credentials.
  • offline: Runs virt-customize --no-network and virt-cat on the staged copy to normalize guest access: it verifies that UID/GID 88 is free of unrelated accounts, configures the locked dba/admin identity, disables SSH password and root login, strips SSH host keys, shell history, machine-id, and cloud-init caches, restores SELinux labels, and verifies a deterministic guest marker.

build.sh defaults to --profile base. The available profiles are:

Profile Normalization steps Package inputs
base Account, sudo, SSH, and cloud-init normalization only None (package flags rejected)
el8 Base normalization plus Python 3, SSH drop-in include, legacy network cleanup, chronyd, and SELinux relabeling Locked RPM dependency set
el9 Base normalization plus legacy network cleanup and SELinux relabeling None (package flags rejected)
d12, d13 Base normalization plus offline XFS userspace tools and en_US.UTF-8 / C.UTF-8 locale setup Locked DEB dependency set

validate mode accepts only --profile base. In offline mode, el8, d12, and d13 require both --package-lock and --package-cache matching the profile and architecture. For official catalog targets, use build-official.py to drive these inputs automatically.

Official candidate matrix

packaging/image-pipeline/build-official.py builds the eight offline-normalized official targets—Debian 12/13 (d12, d13) and Rocky Linux 8/9 (el8, el9) on amd64 and arm64—using the upstream URLs, package locks, release versions, SHA-256 digests, and SOURCE_DATE_EPOCH pinned in official-v1.json (the catalog’s Ubuntu artifacts come from the same recipe run directly by pipeline.py; Ubuntu cloud images already ship xfsprogs, so they need no offline package lock).

./packaging/image-pipeline/build-official.py --list

./packaging/image-pipeline/build-official.py \
  --source-cache /absolute/source-cache \
  --package-cache /absolute/package-cache \
  --output /absolute/existing-output-root \
  --target d13/arm64 --fetch

Usage notes:

  • Create --source-cache, --package-cache, and --output beforehand and pass canonical absolute paths. With --fetch, the wrapper downloads any missing pinned HTTPS inputs and verifies their SHA-256 digests before running virt-customize --no-network; without --fetch, all locked inputs must already be present in the cache directories.
  • Omit --target to build all eight targets, or repeat --target to build a subset. --list prints the exact releases pinned in your checkout.

Once all eight target bundles have been built, assemble them into a candidate static repository by passing the parent directory (or repeating --assemble-from across multiple parent directories) that contains the bundle directories:

./packaging/image-pipeline/build-official.py \
  --assemble-from /absolute/existing-output-root \
  --output /absolute/new-candidate-repository

--assemble-from requires exactly one bundle for each of the eight targets and writes to a new --output path that does not yet exist, invoking barn repo build and barn repo verify (using barn on PATH or --barn /absolute/path/to/barn). The assembled repository maps its channels to candidate (for example d13:candidate) with status testing so you can run live VM acceptance checks before promoting images to stable and supported.

Validate a single downloaded image

SOURCE_DATE_EPOCH=1787486400

./packaging/image-pipeline/build.sh \
  --mode validate \
  --source /absolute/source.qcow2 \
  --expected-sha256 <digest> \
  --output /absolute/new/evidence-directory \
  --name u24 --release 20260801.0.0 --arch amd64 \
  --source-user ubuntu --boot uefi \
  --source-uri https://immutable.example/source.qcow2 \
  --artifact-url 'https://images.example/u24/{sha256}.qcow2' \
  --license NOASSERTION \
  --source-date-epoch "$SOURCE_DATE_EPOCH" \
  --manifest-version 2026082903

Path and size constraints:

  • --source must be a canonical absolute path to a regular file (not a symlink) up to 16 GiB, and --output must be a new path inside an existing parent directory.
  • Barn validates runtime image artifacts against their declared catalog sizes (or actual file size on local import; see Local image import). The contributor pipeline retains its own recipe bounds.
  • The builder acquires an adjacent lockfile, stages work in a 0700 temporary directory, and atomically renames the completed bundle into --output (cleaning up only its staging directory on failure).

Each completed output bundle contains:

File Purpose
<name>-<release>-<arch>-<digest-prefix>.qcow2 Validated read-only candidate qcow2 image
build-recipe.json Builder invocation, tool versions, and input digests
manifest-candidate.json Unsigned candidate metadata (status: "testing")
provenance.intoto.json SLSA build provenance statement
sbom.spdx.json SPDX software bill of materials covering declared pipeline inputs and injected packages
validation.json Before/after qcow2 checks, normalization marker results, and pending release gates
checksums.txt SHA-256 checksums of all files in the bundle
package-lock.json Included when the profile installs locked RPM/DEB packages

With fixed inputs and tool versions, validate mode is byte-for-byte reproducible; for offline mode, build the candidate twice and compare the bundles when preparing release evidence. Promoting a candidate to a public supported release requires running live VM smoke tests across target host/guest combinations, updating repo.yaml with the new catalog revision and supported status, running barn repo build and barn repo verify, signing catalog.json.minisig, and verifying the published repository endpoints.

3 - Project

Supported platforms, design decisions, contribution, and release engineering for Barn.

Barn (Bootstrap And Run Nodes) is an Apache-2.0 virtual machine manager for local database labs, Linux development, and macOS testing. The current stable release is 1.2.0, published on 2026-11-12, with binaries for macOS and Linux on arm64 and amd64. See downloads and installation.

  • Platforms and Limits — Host requirements, guest compatibility matrix, and storage rules in Barn 1.2.0
  • Design — Architecture of the single-inventory Linux lab and independent barn mac VMs
  • Security and Trust — Host privileges, SSH credentials, shared folders, and image verification boundaries
  • Contributing — Source layout, local build and test gates, packaging snapshots, and catalog maintenance
  • Mac Release — Native payload pairing, Developer ID signing, notarization, and installation acceptance
  • Barn 1.2.0 Release — Resource overrides, root growth, concurrency, host names, and upgrade steps
  • 1.2.0 Release Validation — Final signed artifacts, Linux/KVM and native Mac acceptance, public downloads, and Homebrew upgrade
  • Validation History — Dated release verification records and native hypervisor test matrices

Follow the release notes for version updates and the design essays for architectural trade-offs. Bug reports and pull requests are welcome on GitHub; please report security vulnerabilities privately through the security policy.

3.1 - Platforms and Limits

Barn 1.2.0 host requirements, Linux guest compatibility, macOS requirements, and storage limits.

Barn is built for local development and testing. Use this page to check host requirements and guest capabilities before setting up a lab.

Version

This documentation covers Barn 1.2.0 (see the Installation Guide and 1.2.0 Release Notes). Run barn version to check your installed binary; the Linux image catalog updates independently via barn update.

  • Stable channel: Barn 1.2.0 was published on 2026-11-12 as a regular GitHub release, so releases/latest, the download installer without BARN_VERSION, and the Homebrew formula select it; DEB and RPM packages are downloaded from the release. Set BARN_VERSION=1.2.0 to pin it.
  • Native macOS component: The Darwin arm64 archive and Apple Silicon Homebrew install include the signed, notarized Barn.app alongside the matching CLI and helper. Go and Xcode are not required; see macOS Guests.
  • Changes in 1.2.0: Resource and environment overrides, recoverable root-disk growth, lifecycle --parallel, and inventory-based host names. Linux defaults change to 1 vCPU / 2 GiB; set vm_cpu: 2 and vm_mem: 4GiB explicitly to preserve 1.1.0 defaults. Existing applied VMs retain their resources until a resource-changing operation is requested.
  • Historical verification: Dated test records are archived in Validation History and Barn 1.1.0 Local Acceptance.

See the version baseline for option availability and the 1.2.0 validation record for release evidence.

Linux guests

Host Architecture Native acceleration Minimum QEMU
macOS arm64 (Apple Silicon), amd64 (Intel) HVF 8.2.1
Linux amd64, arm64 KVM 6.2

Barn builds release binaries for all four host targets, with primary live-hypervisor test coverage on darwin/arm64 and linux/amd64. On Linux hosts, Barn requires accessible /dev/kvm and either NetworkManager or systemd-networkd. Run barn doctor to inspect host readiness or barn setup --dry-run to preview dependency and network setup.

The catalog embedded in Barn 1.2.0, revision 2026110502, provides 9 distribution families and 17 image artifacts with u24:stable as the default: Ubuntu 22.04/24.04/26.04 LTS, Debian 12/13, Rocky Linux 8/9/10, and CentOS 7 (see Linux Images for exact versions):

  • Each user manages one Linux lab in a private RFC1918 /24; the node count is limited only by the subnet’s usable addresses and host resources.
  • Foreign-architecture guests run under QEMU TCG software emulation. Rocky Linux 8 arm64 also uses TCG on Apple Silicon because its 64 KiB page-size kernel is incompatible with Apple HVF.
  • CentOS 7 (el7) is a deprecated compatibility image restricted to native linux/amd64.
  • vm_shares (QEMU Virtio-9p) supports Linux and macOS hosts when QEMU and the guest kernel support 9p. SSH/SCP/rsync remain alternatives when a guest kernel lacks 9p.

macOS guests

barn mac requires Apple Silicon, macOS 27 or later, an active desktop login session, and the Barn.app native component. Guests run macOS 27 on Apple’s Virtualization.framework without needing QEMU or the Linux host network (see the macOS Guests guide):

  • Disk space: Preparing the first macOS VM requires roughly 65 GiB of free disk space for the cached .ipsw restore image, the unbooted base image, and the VM’s APFS clone. Downloads support HTTP range resumption, or you can pass a local file via --ipsw.
  • Concurrency limit: Apple’s framework allows at most two running macOS VMs at a time per Mac (counting VMs across all apps and any active .ipsw base installation); you can keep additional VMs in the stopped state.
  • Isolation and sharing: Each macOS VM gets its own private /24 NAT subnet, SSH keypair, login password, and APFS copy-on-write disk, plus VirtioFS directory shares (--share) and plain-text clipboard sync.
  • Framework boundaries: USB passthrough, VM snapshots, and suspend/resume are not supported, and Apple Account sign-in inside a virtual machine may be restricted by macOS.
  • Damaged Mac state: A malformed recovery journal stops barn mac image ls, the space estimate in barn mac doctor, and image pruning until it is repaired. One unreadable machine state stops barn mac ls, while the other machines stay usable by name.

Use barn mac doctor and barn mac ls to inspect macOS VMs; top-level barn status, destroy, and purge apply only to the Linux lab.

Data and lifecycle

Keep important work backed up on the host or in version control:

  • Linux lab: barn recreate replaces root disks and non-persistent data disks while retaining persistent: true disks, and a whole-deployment recreate refuses an inventory that omits an existing node; barn destroy removes root and non-persistent disks. During recovery, barn up formats a data disk that carries no signature and resets an existing ext4 or XFS filesystem only when the checker reports recognized corruption. A partition table, a signature without a UUID, unsupported filesystem features, or an unknown checker failure keeps the data and reports a warning (see Storage and Access).
  • macOS VMs: barn mac recreate replaces the VM’s disk with a fresh clone from the base image, and barn mac destroy deletes the VM’s disk, keys, and state while leaving shared host directories untouched.

For diagnostics, see Troubleshooting; for privilege and trust boundaries, see Security and Trust; and for build and release workflows, see Contributing.

3.2 - Design

How Barn models Linux labs and macOS guests, with predictable state and explicit lifecycle changes.

Barn provides two distinct virtualization workflows through one CLI: multi-node Linux labs and standalone macOS guests.

Dimension Linux labs (barn …) macOS guests (barn mac …)
Hypervisor QEMU with HVF or KVM (TCG for foreign architectures and el8 on Apple Silicon) Apple Virtualization.framework
Desired state One Pigsty-compatible YAML inventory (barn.yml) Named VMs configured via barn mac flags
State directory $BARN_HOME (default ~/.barn) $BARN_HOME/mac (default ~/.barn/mac)
Networking User-mode management NIC + shared fixed-IP /24 lab subnet Dedicated /24 NAT subnet and DHCP .10 reservation per VM
Primary workloads Database clusters, Ansible playbooks, multi-node Linux testing macOS builds, GUI testing, isolated desktop environments

The sections below summarize the Linux lab architecture; for the deeper rationale behind each decision, see the Design Essays. The native Mac lifecycle essay explains network ownership, locks, credentials, and recovery; usage is in macOS Guests and Mac Commands.

One Linux lab per user

Each Unix user manages one Linux lab deployment stored in BARN_HOME (default ~/.barn) and driven by a single Pigsty-compatible YAML inventory (see Why Barn Has No Projects). Because deployment state lives in BARN_HOME rather than the working directory, lifecycle and access commands work from any directory.

Node-level convergence

Barn computes a specification hash per node from the documented vm_* and Pigsty identity fields and records it alongside the node’s committed state and QEMU process identity:

  • barn up creates newly added nodes, starts stopped nodes, retries unfinished guest setup on running nodes without restarting their QEMU processes, and refreshes managed /etc/hosts and SSH entries.
  • Changing an existing node’s specification or removing a node from barn.yml never destroys a VM behind your back—CPU and memory changes use barn resize, other modifications require barn recreate, and removals require barn destroy. A whole-deployment recreate refuses an inventory that omits an existing node (see Declarative Does Not Mean Destructive).

Runtime selection

--arch/-A overrides inventory vm_arch for an invocation. With neither set, the guest follows the host; -A native overrides the inventory and restores that default. Image flag -I does not change architecture, and switching an existing VM requires recreation; see configuration rules.

vm_arch applies across the entire deployment (native uses the host CPU architecture; explicit amd64 or arm64 selects that architecture from the catalog). Barn uses hardware acceleration (HVF on macOS, KVM on Linux) whenever guest and host architectures match, switching to QEMU TCG only for foreign architectures or catalog-declared kernel incompatibilities (el8 arm64 on Apple Silicon). If native HVF/KVM fails for any other reason, Barn fails closed rather than silently degrading to software emulation.

Each node’s committed state records the effective architecture and accelerator (barn status displays both). Before executing a destructive recreate, Barn pre-validates the QEMU binary, version, network backend, base image bytes, boot mode, and firmware so a missing dependency never destroys a working VM.

Two NICs, one fixed subnet

Every Linux VM attaches two virtual NICs (see Why Every Barn Node Has Two NICs):

  • Management NIC: QEMU user-mode networking providing DHCP, DNS, outbound internet access, and host-loopback SSH port forwarding without host firewall changes.
  • Fixed-IP lab NIC: Carries the static RFC1918 /24 address from barn.yml for host-to-node, node-to-node, and Ansible traffic.

To provide the fixed-IP subnet without running QEMU as root:

  • macOS hosts run a pinned socket_vmnet daemon.
  • Linux hosts use NetworkManager (nmcli managing barn0) when active, or systemd-networkd with the distribution qemu-bridge-helper (verifying beforehand that enabling an inactive systemd-networkd will not claim physical host interfaces). On Debian, setup grants qemu-bridge-helper access to a group containing the caller and smoke-tests an unprivileged QEMU attachment before committing the network transaction.

Storage and configuration lifetimes

Desired configuration (barn.yml), applied deployment state ($BARN_HOME), and cached base images ($BARN_HOME/images) have independent lifecycles:

  • Updating a catalog channel (barn update) does not rewrite existing VM root disks.
  • Verified base images are stored read-only and shared across nodes via per-node qcow2 copy-on-write root overlays.
  • Ordinary destroy and recreate retain persistent: true data disks. During guest recovery, up formats only a data disk without any signature or an ext4/XFS filesystem whose checker reports recognized corruption; anything it cannot classify keeps its data and reports a warning (see Data Disks and Storage and Access).
  • barn image prune protects images referenced by the active catalog, applied nodes, and local-* aliases (see Images).

Safety boundary

QEMU processes and image downloads run as your normal user account; root privileges are used only during setup, network install/uninstall, and hosts install/uninstall. Before signaling a process or deleting a node file, Barn verifies file ownership, path containment, QMP UUID/name identity, process start-time/cmdline tuples, and the journal allowlist—stopping immediately if any check is ambiguous (see A PID Is Not a Virtual Machine).

3.3 - Security and Trust

Host privileges, guest credentials, shared folders, and the separate trust boundaries in Barn 1.2.0.

Barn is designed for trusted local development workstations. All Linux lab nodes share a private subnet, and the control node holds the SSH key needed to administer every peer node in the lab. Barn’s ownership and identity checks prevent accidental damage to unrelated host files or processes, rather than isolating mutually untrusted local users.

Host privileges

Always run barn as your normal desktop or shell user. QEMU processes, Barn.app macOS VMs, image downloads, VM disks, and state files all run under and belong to your user account. Administrator (sudo) privileges are used only for three explicit host-level tasks:

Task Privileged scope
barn setup (Linux dependency install) Runs the distribution package manager (apt/dnf) to install QEMU, firmware, and OpenSSH (macOS Homebrew runs as your normal user)
barn network install / uninstall Configures or removes the host-wide fixed-IP network backend (barn0 bridge on Linux, socket_vmnet launchd service on macOS)
barn hosts install / uninstall Updates only Barn’s marker-delimited block in /etc/hosts through the dedicated barn-hosts-helper binary

Before prompting for sudo, setup, network, and hosts display the exact plan they will execute. On a terminal, up, start, restart, and reload can offer to prepare a host that lacks a dependency or an intact Barn network; they run the same setup plan with the same single confirmation and never assume --yes. Without a terminal they only report what is missing, so run barn setup --yes for unattended preparation.

Keep barn and /opt/barn/libexec/barn-hosts-helper installed from the same release build: the CLI verifies the helper’s embedded SHA-256 digest before invoking it, and the helper checks /etc/hosts ownership, permissions, link count, and pre/post content digests on every write.

By contrast, barn mac creates per-VM user-space vmnet networks on demand without any root daemon or privileged network setup (see Design and Uninstall).

Guest access and SSH trust

In Linux labs, the default guest account is dba with a locked password, passwordless sudo, and an Ed25519 deployment SSH keypair generated in $BARN_HOME. Barn also installs that private key on the control node so Ansible playbooks and administrative scripts can reach every node in the lab—meaning anyone with shell access to the control node has administrative access to the entire Linux lab.

Barn applies distinct OpenSSH host-key policies across its three connection paths:

Connection path Host-key policy
Host → Linux guest (via barn or ~/.ssh/barn_config) StrictHostKeyChecking accept-new against a private known_hosts file keyed by VM UUID alias (pins on first connection; rejects later mismatches; explicit recreate generates a new VM UUID)
Linux control node → lab peers StrictHostKeyChecking no with UserKnownHostsFile /dev/null for Barn-managed peer aliases so recreated lab nodes remain reachable without manual known_hosts edits
Host → macOS guest (via barn mac or ~/.ssh/barn-mac_config) Strict host-key verification against the per-VM known_hosts file keyed by barn-mac-<instance_id>, with SSH agent forwarding disabled

Each macOS VM generates its own Ed25519 SSH keypair and random local administrator password (stored in a 0600 password file inside the VM slot directory and readable via barn mac password). Once guest initialization completes, both Linux and macOS guests enforce SSH public-key authentication, disable SSH password authentication and root SSH login, and grant passwordless sudo to the configured admin user. Strip private keys, passwords, and sensitive state before sharing logs or inventories in bug reports.

On first Mac contact, trust comes from the runner-owned private network and runner identity checks before and after the SSH connection; the observed host key is then pinned to the instance UUID. The GUI password shares the SSH key’s file-permission boundary: that key already grants guest sudo, so storing only the password in Keychain would add signing and lock-state failures without protecting additional guest authority. See Native Mac lifecycle.

Shared folders and disposable disks

  • Host directory shares: Writable shares (readonly: false in Linux vm_shares or :rw in barn mac --share) grant guest processes direct write access to the shared host directory, and changes made inside the shared directory persist after the VM is destroyed. Share dedicated project folders and prefer read-only shares whenever guests do not need to modify host files.
  • Persistent and disposable data disks: Ordinary Linux destroy and recreate retain persistent: true data disks. During recovery, up formats a data disk without any signature and resets an existing ext4 or XFS filesystem, persistent or not, when the checker reports recognized corruption; other probe or checker failures keep the data and report a warning. Keep important data backed up on the host (see Storage and Access).

Downloads and image trust

Barn separates trust verification across four independent mechanisms:

Mechanism What it verifies
Release checksums.txt Lists the SHA-256 digests of every release asset—CLI archives, .deb/.rpm packages, SPDX SBOMs, the Homebrew formula, the installer, and release metadata; the installer verifies the selected archive against it
macOS release signatures The Apple Silicon release CLI, helper, and Barn.app use Developer ID signatures and Apple notarization; the app carries a stapled ticket. Local source builds default to ad-hoc signing
Linux catalog signature (catalog.json.minisig) Verifies with Barn’s embedded Ed25519 public keys that catalog.json was signed by a trusted key and satisfies per-repository anti-rollback revision rules
Image artifact verification Verifies before use that every downloaded or imported .qcow2 matches its catalog byte size, SHA-256 digest, virtual capacity, and standalone qcow2 format rules

Release assets are built from the exact tag, by the tag workflow when its Mac signing secrets are configured and otherwise by the documented local release path. Source CI must pass on the release commit first. Both paths require package, archive, SBOM, and checksum verification before creating a draft GitHub Release; local publication follows the Mac release handbook.

In 1.2.0, release.json records signed:false, attested:false for the general release envelope. The Git tag and general checksums/release metadata have no separate cryptographic signature or attestation. Catalog Minisign verification and Apple Silicon code signing/notarization retain the distinct scopes listed above; the final acceptance record verifies them separately.

The two official image repositories (https://repo.pigsty.io/barn and https://repo.pigsty.cc/barn) always require valid minisign catalog signatures, even when passed explicitly via --repo. Custom HTTPS or local directory repositories may use unsigned catalogs when explicitly selected with --repo or BARN_REPO (see Linux Images).

Report a vulnerability

Report security vulnerabilities privately via GitHub Security Advisories or by emailing [email protected] with barn --json version, your host OS and architecture, and a minimal reproduction with credentials removed. Security fixes land in the latest 1.x patch release; only the latest 1.x release is supported, and 0.x releases receive no further fixes. See SECURITY.md for the full policy, and use GitHub Issues for ordinary bug reports.

3.4 - Contributing

Work on Barn’s source, documentation, packages, and image catalog.

Barn is an Apache-2.0 open-source project. Contributions to the CLI, native macOS component, documentation, and image pipeline are welcome.

Choose a repository

What you want to change Where to work
CLI behavior, VM lifecycle, native macOS component (Barn.app), installer, and packages pgsty/barn
Documentation site, guides, command reference, and translations pgsty/barn.pgsty.com
Official Linux image definitions or offline normalization rules packaging/image-repository/ and packaging/image-pipeline/ in pgsty/barn

When filing a bug report, include barn version, your host OS and architecture, the exact command you ran, and the error output with private keys and passwords removed. Report security vulnerabilities privately via the security policy. For major behavioral changes, open an issue first to align on the design, and see CONTRIBUTING.md for coding and pull-request conventions.

Find the implementation

Tag v1.2.0 is the current stable release; its changes since 1.1.0 are listed in the 1.2.0 release notes. Find each subsystem here:

Subsystem Source paths
CLI commands, flags, JSON/YAML output, and error rendering cmd/barn/, internal/failure/
Inventory parsing, defaults, and resolved node hashes internal/config/, internal/spec/
Linux convergence, journals, disks, and SSH readiness internal/private/, internal/vm/, internal/cloudinit/, internal/state/
Host networking, process identity, and privileged /etc/hosts updates internal/network/, internal/process/, internal/hostconfig/, cmd/barn-hosts-helper/
Image catalog trust, qcow2 verification, and repository build tools internal/image/, tools/catalogexport/, tools/catalogsign/
Native macOS VM lifecycle and desktop app internal/macvm/, native/mac-runner/
Packaging, CI workflows, and integration suites packaging/, .github/workflows/, tests/

Read each package’s tests alongside the implementation—they codify the exact boundary between automatic recovery and fail-closed safety checks.

Build and test

Install the pinned toolchain listed in Build from Source and run the full local gate:

make check

make check runs Go module and shell checks, unit and race-detector tests, go vet, staticcheck, govulncheck, four-target cross-builds (darwin/arm64, darwin/amd64, linux/amd64, linux/arm64), installer and image-pipeline tests, and license verification. When changing hypervisor, networking, or Barn.app behavior, also run live VM lifecycle tests on the target host (macOS HVF / Virtualization.framework or Linux KVM) and record the tested host environment in your pull request.

To verify packaging changes locally with the tools pinned in packaging/toolchain.env:

make release-check
make release-snapshot SNAPSHOT_DIST=.goreleaser-review

SNAPSHOT_DIST must be a new directory path inside the repository root; release-snapshot builds and verifies archives, .deb/.rpm packages, SBOMs, and checksums locally without uploading artifacts.

Edit the documentation

Maintain new design decisions, research conclusions, and acceptance records in this site’s content/blog/design/ and content/blog/release/, with their date, implementation status, source evidence, and acceptance conditions. The design notes collect the durable explanations; task guides and reference contracts stay in content/docs/. The source repository no longer maintains a docs/ directory.

Every documentation page is maintained as an English/Chinese pair (page.md and page.zh.md). Keep commands, defaults, explicit {#id} heading anchors (required by OINK v1.5.1 Duo View compare: true), tables, and warnings in 1:1 parity across both languages, lead with the user’s task, and keep Linux lab and barn mac workflows clearly separated.

Validate the website repository with:

make check

make check verifies the Go module graph, builds the site with Hugo Extended against the pinned OINK v1.5.1 module (clearing any local theme replacements and disabling Go workspaces), and runs bin/check_internal_links.py public to verify all internal links, anchors, and assets. When testing against a local sibling OINK checkout, use make check-local or make build-local. Check CLI examples against barn <command> --help and test inventory examples with barn validate and barn plan using a temporary BARN_HOME.

Release outputs and gates

Stable versions from 1.0.0 on are published as the latest GitHub release; versions below 1.0.0 and versions with a prerelease suffix are GitHub pre-releases and need an explicit BARN_VERSION in the installer. Each release ships darwin/linux .tar.gz archives (containing bin/barn, bin/barn-hosts-helper, and licenses/), Linux .deb and .rpm packages (installing /usr/bin/barn and /opt/barn/libexec/barn-hosts-helper), checksums.txt, release.json, and SPDX SBOMs.

Since 0.9.2, the Darwin arm64 archive also contains bin/Barn.app. Its CLI, helper, and app use Developer ID signatures and Apple notarization. Homebrew installs the app under libexec; ordinary installation needs no Go or Xcode.

The automatic release workflow requires all six Apple signing/notarization secrets and a successful native build. With none configured, it skips automatic release creation; partial configuration fails. The Mac release handbook covers the local signed-release process, payload identity, CI secrets, and installation acceptance.

Before publishing a release:

  1. Run make catalog-check: it verifies both official mirrors’ signatures in fresh temporary data roots, requires the exact embedded catalog bytes, and checks all stable artifact URLs and sizes. If the catalog changed, synchronize the embedded snapshot, revision, date, and repo.yaml first. Run catalog tests and barn update with a fresh temporary BARN_HOME; go run ./tools/catalogcheck -verify-bytes also downloads and hashes every stable artifact without retaining it. Formal native builds and both release paths enforce the catalog gate.
  2. Run the source CI and packaging workflows on the target commit and verify version strings, paired barn-hosts-helper digests, licenses, SBOMs, and checksums.txt.
  3. Run live Linux VM acceptance tests (and live macOS VM acceptance when shipping Barn.app).
  4. Publish the GitHub Release and update the Homebrew tap (pgsty/infra/barn), then publish the documentation site and verify the public download and package installation paths.

Maintain the image catalog

Use the Image Pipeline tools (packaging/image-pipeline/build.sh and build-official.py) to validate and offline-normalize candidate qcow2 images before running live hypervisor smoke tests, updating repo.yaml, and signing catalog.json.

To export the binary’s embedded schema-3 catalog to a new path:

go run ./tools/catalogexport /absolute/new/catalog.json

catalogexport writes atomically to a non-existent target path; it accepts relative paths, ~/, and directory symlinks and creates missing parent directories. Use make catalog-sign and make catalog-verify with the offline Minisign keypair (keeping production private keys outside Git and CI). See Linux Images for the repository and client verification rules.

3.5 - Shipping the Mac Component

Build, sign, notarize, and verify Barn’s native macOS release payload.

Maintainers ship the native macOS component inside the ordinary barn_VERSION_darwin_arm64.tar.gz archive. Since 0.9.2, the standard installer and pgsty/infra/barn Homebrew formula install the CLI, its matching hosts helper, and Barn.app together. This page is the maintained release handbook, checked against the v1.2.0 release scripts on 2026-11-12. For a development build, use Build from Source.

Keep one release identity

The native payload and the GoReleaser archives must carry the same version and source commit. packaging/mac-release.py exports MACOS.md, MACOS.json, bin/barn, bin/barn-hosts-helper, and Barn.app; its manifest records file modes, digests, and signing state. VM disks, credentials, restore images, and installed services are outside this payload.

The app’s bundle identifier is com.pgsty.barn; its internal executable is barn-mac-runner. The CLI checks the versioned runner protocol and also recognizes the earlier Barn Mac.app bundle name. Replacing the app preserves VM state under $BARN_HOME/mac.

Signing order matters because the CLI embeds the helper’s digest:

  1. Build and sign barn-hosts-helper.
  2. Embed that signed helper’s SHA-256 when building barn, then sign the CLI.
  3. Sign Barn.app with hardened runtime and a secure timestamp.
  4. Submit the CLI, helper, and app together to Apple for notarization. After acceptance, staple the app, validate its ticket, and check Gatekeeper.
  5. Export and re-extract the payload, then recheck signatures and the stapled ticket. During release assembly, replace the Darwin arm64 archive’s CLI/helper with these exact signed bytes and regenerate its SPDX document and checksums.

The payload builder and archive verifiers enforce this pairing. Signing the helper again after building the CLI changes the digest and breaks it.

Build a formal release locally

Use an Apple Silicon Mac with macOS 27+, Xcode 27+, the locked toolchain, a Developer ID Application identity, and a stored notarytool profile. Select the identity with security find-identity -v -p codesigning; create the profile with xcrun notarytool store-credentials. Keep certificate private keys and passwords outside the repository.

Check out a clean source tree tagged v$RELEASE_VERSION, setting RELEASE_VERSION to the intended release version. Choose a new artifact directory outside the source repository:

export BARN_CODESIGN_IDENTITY='Developer ID Application: your organization (TEAMID)'
export BARN_NOTARY_PROFILE=barn-release
export BARN_MAC_REQUIRE_NOTARIZATION=1
VERSION="${RELEASE_VERSION:?Set RELEASE_VERSION to the checked-out release tag version}"
COMMIT="$(git rev-parse HEAD)"
export SOURCE_DATE_EPOCH="$(git show -s --format=%ct HEAD)"
export BARN_BUILD_DATE="$(date -u -r "$SOURCE_DATE_EPOCH" +%Y-%m-%dT%H:%M:%SZ)"
RELEASE_DIR="$(mktemp -d "${TMPDIR:-/tmp}/barn-release.XXXXXX")"

python3 packaging/mac-release.py build \
  "$VERSION" "$COMMIT" "$SOURCE_DATE_EPOCH" "$RELEASE_DIR/native.tar.gz"
BARN_MAC_PAYLOAD="$RELEASE_DIR/native.tar.gz" \
  make release-local VERSION="$VERSION" OUTPUT="$RELEASE_DIR/assets"

The formal native build and release-local enforce the make catalog-check gate before building. Synchronize and test any changed official catalog first, following the release gates. A snapshot or uncommitted payload cannot serve as a formal local release. release-local verifies archives, packages, SBOMs, checksums, and CLI/helper pairing, and writes the assembled assets to the specified directory.

For packaging development, BARN_MAC_PAYLOAD=/absolute/native.tar.gz make release-snapshot exercises native payload composition. Match the payload to the snapshot version selected by .goreleaser.yaml—for example, 1.2.1-next after v1.2.0—and use uncommitted for a dirty checkout or the full commit for clean source. Snapshots accept ad-hoc signatures; formal release verification requires a notarized Developer ID payload.

Configure automated signing

The tag workflow uses all six repository secrets below. With none configured, it skips automatic release creation so maintainers can use the local path. A partial set fails immediately; with all six present, a native build, signing, or notarization failure blocks release assembly.

Secret Value
MAC_CERTIFICATE_BASE64 Base64-encoded Developer ID Application .p12, including the private key
MAC_CERTIFICATE_PASSWORD Password protecting the .p12
MAC_CODESIGN_IDENTITY Imported identity name or certificate fingerprint
MAC_NOTARY_KEY_BASE64 Base64-encoded App Store Connect API .p8 key
MAC_NOTARY_KEY_ID API key identifier
MAC_NOTARY_ISSUER API issuer identifier

The Mac workflow imports signing material into an ephemeral runner keychain and removes it at the end of the job. Pull requests and snapshots use ad-hoc builds. The release workflow creates a draft after verifying the assembled payload.

Verify installation and publish

Run make check, make mac-native-test, and a full snapshot with the native payload. Exercise first installation, repeated installation, and upgrade in an isolated directory. On a supported physical Mac, verify startup, SSH, the desktop window, and normal shutdown using the packaged app. App replacement must preserve instance IDs, machine IDs, SSH keys, and host pins.

The existing tests/mac-live-acceptance.py requires an absolute --barn path, a dedicated disposable --home, a separate new or empty --output evidence directory, and explicit --allow-test-root authorization for that test root. Inspect the sequence with --plan first. --gui adds desktop/clipboard checks; --allow-download adds Apple download when no base is prepared. The harness preserves failed state and evidence. Isolate the process’s home directory as well as BARN_HOME when testing SSH integration, because managed ~/.ssh/config entries live outside the VM state root.

Release assembly generates barn.rb from packaging/homebrew/barn.rb.tmpl. The formula installs barn into bin, and the helper and app into libexec. Keep its install/test logic aligned with the tap. Versions with a suffix need an explicit version line because Homebrew may infer 64 from the architecture suffix; plain versions omit that line. The 0.9.1 public archive predates app delivery and supports Linux guests only.

After checking the local output, push the source and tag, upload the files to a draft GitHub Release, and verify the uploaded bytes before publishing. Mark versions below 1.0.0 and versions with a prerelease suffix as pre-releases; stable releases become latest. Update all four Homebrew platform URLs and checksums after the assets are public, then verify the public archives, installer, and Homebrew upgrade against those released bytes. Record signing, notarization, VM acceptance, and public installation results separately in the validation history.

3.6 - Validation History

Original Barn release validation, native VM tests, image checks, and design history.
Note

This page collects dated source, packaging, VM, and public-installation evidence through 1.2.0. Each entry retains its tested revision and scope. For current host support and release behavior, see Platforms and Limits and the release notes.

Barn 1.2.0 final acceptance — 2026-11-12

Item Recorded identity
Tag and commit v1.2.0 → 90f615804b823b4eb491101e642073dcd37a9700
Build time 2026-11-11T23:38:07Z; SOURCE_DATE_EPOCH=1794440287
GitHub Release ID 442314866; published 2026-11-12T00:05:32Z as stable/latest
Apple notarization Accepted submission 165b486b-5055-4eca-a595-54bed6bdd6c1
Signed catalog Revision 2026110502; SHA-256 d41e46d20bc57cb1b071e3919a27a8f7f703df2addca879970a528e7ab004065
  • CI and packaging: Source/Mac CI and complete packaging CI passed the release commit. The tag workflow passed its signing-configuration check and skipped remote assembly; the documented local signing path produced the formal release. make release-local, signatures, notarization, staple, and Gatekeeper passed.
  • Final artifacts and catalog: All 20 draft and all 20 anonymous public downloads matched the verified local sizes and SHA-256 digests. Both official catalogs verified, activated in fresh roots, and matched the embedded bytes; each mirror’s 17 stable artifacts passed reachability/size checks. Selected runtime image downloads also verified their payload digests.
  • Final runtime: The downloaded Linux archive passed 18 KVM operations covering ext4/XFS root growth, idempotence, shrink rejection, guest-tool failure, CLI interruption, identity/data retention, concurrency, and teardown. The downloaded native Mac bundle completed all 15 lifecycle phases plus desktop, two-way clipboard, close-window background execution, SSH, and normal shutdown. One Apple HTTPS timeout was preserved and retried successfully after identity/base checks; no product bytes changed.
  • Public delivery: Public latest and pinned installers passed on darwin/arm64 and linux/amd64. Isolated installer upgrade from 1.1.0 and repeated installation passed. The Homebrew update passed a real 1.1.0 → 1.2.0 upgrade, brew test, style, and strict online audit. The bilingual reference now documents 57 commands and 174 local option entries per language against 1.2.0.

Physical runtime coverage was darwin/arm64 and linux/amd64; darwin/amd64 and native linux/arm64 received cross-build and artifact checks. The historical 1.1.0 homepage recording is explicitly labelled. See the complete release review for candidate upgrade evidence, retained retries, and scope limits.

Barn 1.2.0 preparation — 2026-11-12

The 1.2.0 readiness review preserves the uncommitted candidate’s source, catalog, full snapshot, Linux/KVM and native Mac checks, followed by the completed commit, CI, signing, and publication gates.

Barn 1.1.0 final acceptance — 2026-11-09

Item Recorded identity
Tag and commit v1.1.0 → b2cc8da8983e710255d504bf26bf5d7b3f182790
Build time 2026-11-09T01:28:55Z; SOURCE_DATE_EPOCH=1794187735
GitHub Release ID 438656123; published 2026-11-09T01:46:13Z as stable/latest
Signed catalog Revision 2026110502; SHA-256 d41e46d20bc57cb1b071e3919a27a8f7f703df2addca879970a528e7ab004065
  • Source and packaging: The release commit passed source/Mac CI and complete packaging CI before tagging. Local make check, make mac-native-test, and make release-snapshot also passed. The tag workflow passed its signing-configuration check; the complete release used the documented local signing path because repository signing secrets were absent.
  • Downloaded assets: All 20 draft attachments were downloaded again and matched the local sizes and SHA-256 digests. The 19-entry checksum manifest, archive/package parity, four binary architectures, file permissions, helper pairing, and SPDX documents passed verification. Apple accepted notarization submission cabe1b9c-fc54-4f07-b8bf-d834f0226013; the downloaded app passed signature, staple, and Gatekeeper checks. The relocated Homebrew bin/libexec layout found the matching native component.
  • Catalog: Both official catalogs passed signature verification, activation in fresh data roots, and byte comparison against the embedded catalog; all 17 stable artifacts passed HTTP availability and size checks on both mirrors. The final downloaded binary also activated the official catalog in a fresh BARN_HOME.
  • Linux runtime: The downloaded linux/amd64 binary created a Debian 13 guest using -I d13 -A x86_64 without rewriting its inventory. CPU/memory changes 2/2 GiB → 3/3 GiB → 2/2 GiB, stop/start, remote arguments, and exit 17 passed. VM UUID, SSH public key, machine ID, and the persistent-disk sentinel survived resizing and restart. A second Debian 13 guest booted as arm64 through -A aarch64, using QEMU TCG with cortex-a72; SSH reported aarch64. Both test guests were removed after verification.
  • Mac runtime: A new isolated macOS 27 guest passed creation, SSH, remote exit 17, stop/start, identity and data retention, and destruction. Existing user SSH files were unchanged and the user’s running Mac VM was left in place. The signed public installer and repeated installation also passed in a separate directory.
  • Public delivery: Anonymous downloads of all 20 public attachments matched the verified draft. Both the default installer and explicit BARN_VERSION=1.1.0 installed the published release. The Homebrew update sets all four archive URLs and digests. The initial Ubuntu 26.04 recording verified public installation and a first Linux VM on a prepared host network; the homepage recording now shows a real Apple Silicon macOS run (brew install pgsty/infra/barn, ./configure -c full, a four-node barn up, and barn status on macOS 27.0.1 with QEMU 11.1.3). A subsequent 16-stage in-place upgrade and multi-distro acceptance run on Apple Silicon is recorded in Barn 1.1.0 Local Acceptance.

Physical hosts covered darwin/arm64 and linux/amd64, with an additional linux/arm64 guest under TCG. darwin/amd64 and native linux/arm64 received cross-build and artifact checks. GUI and clipboard evidence retains its earlier dated scope. Initial harness retries corrected unsupported timeout flags, a test write requiring guest sudo, and a TCG assertion that did not accept the thread=multi option; their logs were retained alongside the successful runs.

Barn 1.0.0 final acceptance — 2026-11-05

The final acceptance record binds the downloaded artifacts and public installation checks to this release identity:

Item Recorded identity
Tag and commit v1.0.0 → 69ef7f1d6ffc7c6fbffffe178b2fa48e799dc02d
Build time 2026-11-04T17:54:45Z; SOURCE_DATE_EPOCH=1793814885
GitHub Release ID 434268111; published 2026-11-04T23:18:10Z as stable/latest
Signed catalog Revision 2026110502; SHA-256 d41e46d20bc57cb1b071e3919a27a8f7f703df2addca879970a528e7ab004065
  • Source and downloaded assets: The final commit passed source/Mac CI and packaging CI. The release used local Developer ID signing because automated signing secrets were absent. All 20 uploaded attachments were downloaded again, matched their original sizes and SHA-256 digests, and passed the 19-entry checksum manifest plus archive/package, CLI/helper, architecture, permission, SBOM, and identity checks.
  • Catalog bytes: Both official catalogs verified and matched the embedded bytes. Streaming all 17 stable artifacts from each mirror produced 34 successful size/digest checks. A fresh BARN_HOME activated the catalog with barn update.
  • Packaged Mac acceptance: The downloaded CLI/helper/app passed Developer ID, notarization, staple, and Gatekeeper checks, including after installation and relocation into the Homebrew layout. An isolated 0.9.3 → 1.0.0 installer upgrade retained the old release and existing VM identities, SSH keys, and data sentinels. A new VM passed SSH, write, stop/start, identity/data retention, and destruction checks.
  • Linux acceptance: On linux/amd64, the downloaded binaries created two Debian 13 guests in a fresh state root. Fixed IPs, accounts, XFS data disks, stop/start/restart, argument boundaries, and remote exit 17 passed. Missing-node recreation failed before stopping VMs; control-node deletion elected a successor; destruction followed by up rotated machine identity while preserving a persistent-disk sentinel.
  • Public channels: After publication, both the default installer and explicit BARN_VERSION=1.0.0 installed the public release. Real Homebrew upgraded 0.9.2 → 1.0.0 and passed brew test, online audit, and tap tests. The tap update and site update were published, and the public bilingual release and installation pages were checked.

Physical runtime coverage was darwin/arm64 and linux/amd64. darwin/amd64 and linux/arm64 received cross-build and artifact checks. GUI, clipboard, and fault-injection experiments from the preceding candidate run retain that earlier identity below.

Hardening and candidate rehearsals — 2026-11-04–05

The hardening checkpoint at 377061c passed source checks and a 0.9.4-next packaging snapshot without a native Mac payload. Subsequent physical-host tests began with 0.9.4-next; fixes received focused retests, followed by a signed and notarized 1.0.0-rc.1 build using catalog 2026102802. The candidate run covered disk-preservation checks, lock release after cancelled sudo work, shared-networkd uninstall, the 15-stage Mac lifecycle, GUI/clipboard, and interrupted-recreate recovery. Final release assembly later used catalog 2026110502 and the identity above.

The record retains failed attempts as well as successful reruns. A short SSH probe needed bounded retry; a resource-constrained build and a shared module-cache failure needed environment corrections; one acceptance assertion incorrectly required a stdout field when successful empty output could omit it. The dm-error experiment was rejected by the whole-disk guard before reaching the filesystem checker. These observations identify which product path actually ran and which failure belonged to the harness or environment.

Barn 0.9.1 local candidate acceptance — 2026-10-22

The 0.9.1 record reports make check, native component tests, four-platform packaging, and an ad-hoc native payload. The CLI candidate was 0.9.1-dev.1; the complete snapshot was 0.9.1-next. An isolated upgrade from a real 0.9.0 archive used a local download fixture and verified repeated installation, retained releases, helper pairing, and app discovery. Homebrew checks covered the bin/libexec layout; native tests and mac doctor did not start a VM. The public 0.9.1 assets contained CLI/helper archives and Linux packages, without Barn.app; the original record’s public-release checklist describes the subsequent process, not completed runtime evidence.

Pre-0.9.0 checkpoint — 2026-09-29

At the 2026-09-29 checkpoint, Barn 0.9.0 was the first release candidate prepared under the barn name prior to publication. For current installation commands, use the Quick Start.

Documentation baseline

Component Identity at the 2026-09-29 checkpoint Scope notes
Application and docs Barn 0.9.0 release candidate Built from the renamed source tree prior to public tag release
CLI and configuration barn, barn.yml, BARN_* Clean break from earlier internal names
State and host resources ~/.barn, Barn networking, and barn-hosts-helper Uses fresh ~/.barn state without migrating pre-rename development directories
Image repository /barn on the official endpoints Requires valid minisign catalog signatures

macOS guests

At the 2026-09-29 checkpoint, barn mac ran macOS 27 guests on Apple Silicon via Barn Mac.app (io.pgsty.barn.mac-runner; subsequently renamed to Barn.app / com.pgsty.barn in Barn 0.9.1) with state under $BARN_HOME/mac (see the current macOS Guests guide and Mac Commands reference).

Following the rename on 2026-09-29, local CLI and barn-hosts-helper unit suites, native bridge/image/exit-prompt/menu tests, the runner bundle build, ad-hoc code-signature verification, and probe checks all passed, building on the full 16-phase live macOS VM acceptance run recorded earlier that day.

The rename record explicitly limits the renamed build’s evidence to those checks: its VM lifecycle was not replayed. The 2026-10-03 archive correction retained the original dates, versions, commits, and test scope rather than attributing earlier VM runs to the renamed build.

Remaining pre-0.9.0 release checks

At the 2026-09-29 checkpoint, the remaining pre-publication gates for 0.9.0 were:

  • Full source, archive, DEB/RPM, installer, and four-platform checks on the final tagged commit.
  • Fresh host setup and Linux/macOS VM lifecycle and cleanup verification under the renamed binary and paths.
  • macOS Developer ID signing, notarization, and release publication.
  • Verification of the public GitHub repository, Homebrew tap, official /barn image endpoints, and barn.pgsty.com.

Earlier Mac implementation records

  • 2026-09-26–27: The initial implementation log records a full IPSW download and CLI restore, base-image immutability, dual-guest lifecycle tests, and installer rejection of a modified package. The final 12-stage run took 140.305 s starting with already initialized guests. It used the retired fixed-slot/shared-network implementation and an ad-hoc development signature.
  • 2026-09-28: The complexity review retained identity, locking, and recovery safeguards while simplifying the implementation. Regression checks verified that readiness did not rewrite SSH material. An initial shutdown timeout coincided with high guest load; the full rerun passed without forced power-off or timeout changes. The record treats the load as an observation, not a proven cause.

Barn Mac native record: 2026-09-29

On 2026-09-29, the pre-rename development tree was validated on an Apple Silicon Mac running macOS 27.0 (26A428) using an ad-hoc signed development build and APFS clones of a base image prepared on 2026-09-26 from Apple’s pinned 27.0 restore image:

  • Source and bundle checks: Full make check, native Swift component tests, and the Mac bundle build with extracted-archive checksum, signature, and probe checks passed.
  • Automated 16-phase live acceptance (244.9 s): Verified VM creation from the base image, named VM creation with a VirtioFS shared folder, independent machine identities, sshd hardening, disk and per-VM subnet isolation, DNS, outbound HTTPS, exit-code propagation, exec argument boundaries, bidirectional shared-folder writes, clean stop/start, configure, enforcement of the two-VM running limit, recreate identity rotation, desktop window display, two-way plain-text clipboard sync, idempotent up, and destroy.
  • Manual timing and recovery checks: First boot from a prepared base image reached SSH readiness in 22 s; clean shutdown completed in 6.5 s; subsequent starts reached SSH readiness in 6–12 s; forced power-off completed in 0.7 s. Also verified booting into macOS Recovery (--recovery), changing --subnet while preserving the pinned SSH host key, and connecting via ssh mac1 through installed ~/.ssh/barn-mac_config entries.

Items deferred to subsequent release milestones at that checkpoint:

  • Developer ID signing, Apple notarization, and public distribution of the app bundle.
  • End-to-end IPSW download + base preparation via first-time barn mac up or barn mac image update.
  • Desktop menu Restart… and Shut Down… actions, additional Apple Silicon models, and host reboot persistence.

The same named-machine record includes a later 07ebfd4 candidate rerun: all 16 phases passed in 298.9 s after an initial five-second SSH probe failed and the guest answered on a later observation. Both runs reused an existing 26A428 base; fresh download and restore remained outside that checkpoint’s coverage.

Pre-rename Linux validation summary

Host or artifact Path Last verified Result
Two Linux amd64 hosts (m0, m3) KVM, 7 guest distributions per host 2026-09-21 (0.8.0) Clean initialization, first SSH, in-place candidate upgrade, repeated up, stop/start, disk identity, and Ansible inventory reads passed
Two macOS arm64 hosts (m1, m5) HVF, 7 guest distributions per host 2026-09-21 (0.8.0) Full 7-distro lifecycle checks plus 7-node ansible all -m ping passed
Catalog 2026092001 9 families, 37 artifacts (September Debian/Ubuntu refresh) 2026-09-21 Embedded, local, LAN, and public catalog bytes matched (23e8dbf6...); 10 new image objects verified at both public endpoints and booted across the 4 hosts
Ubuntu 26.04 amd64 KVM, QEMU 10.2.1, Ubuntu 24.04 guest 2026-09-16 (0.7.0 recovery) Corrupt data-disk reset, failed probes, busy mounts, retained-disk recreation, share recovery, and idempotent up passed
macOS arm64 HVF, Ubuntu 24.04.4 guests 2026-09-05 (0.6.0 lifecycle) Create, scale-out, control-to-peer SSH, stop/start, reload, recreate, degraded status, and scale-in passed
macOS 26.6.2 arm64 HVF, QEMU 11.1, socket_vmnet 2026-09-01 (v0.2.0) Selected node create/SSH/stop/start, incremental cached create, full status, and full destroy passed
macOS 26.6.2 arm64 HVF, QEMU 11.1, socket_vmnet 2026-08-27 Single-node and additive four-node lifecycle passed
Ubuntu 26.04 amd64 (mx) KVM, QEMU 10.2.1, NetworkManager 2026-09-01 (v0.2.0) Audited a live four-node deployment and connected to its control guest
Ubuntu 26.04 amd64 (mx) KVM, QEMU 10.2.1, NetworkManager 2026-08-27 Host setup, single-node, additive four-node, and network uninstall passed
macOS arm64 HVF host, TCG rule for Rocky Linux 8.10 arm64 2026-08-28 Boot, stop/start, and SSH readiness in 44.2 s passed
Published Catalog 2026090501 9 families, 27 qcow2 artifacts 2026-09-05 Artifact verification, embedded/public byte equality, and signed barn update against both official endpoints passed
Published Catalog 2026082903 9 families, 27 signed qcow2 artifacts 2026-08-29 Full 19 GiB SHA-256 sweep and clean-client d13:stable pull passed

In the 0.8.0 four-host acceptance run, each host booted Rocky Linux 9.8 and 10.2, Debian 12.15 and 13.7, and Ubuntu 22.04.5, 24.04.5, and 26.04.1 (28 guest VMs total, all passing and cleaned up afterward). Both official endpoints served catalog 2026092001 with SHA-256 23e8dbf6c19bd192d56c6d71eb30901f17945b3487e427a43abe108463780306, verified via isolated barn update runs and HTTP HEAD content-length checks on the 10 new image objects.

Open test items at the earlier checkpoint

  • Rocky/EL9 hosts with NetworkManager + firewalld, and a fresh systemd-networkd host replay.
  • Physical host reboot persistence across active deployments.
  • Live hypervisor runs on darwin/amd64 and linux/arm64 hosts beyond cross-build and package verification.
  • End-to-end el7 VM lifecycle replay on native linux/amd64.
  • Full Pigsty configure → barn up → install.yml playbook execution.
  • Fresh-host setup and VM creation driven end-to-end from the public download installer, Homebrew formula, or published DEB/RPM packages.

Barn verification history

Barn 0.8.0 — 2026-09-21

  • Release CI and assets: Tag v0.8.0 (320a32afa8f6fca02592215aa0d5607ca4e852b2) passed source CI, the packaging snapshot workflow, and the release tag workflow, publishing 20 release assets (19 checksummed payloads). All 20 assets were downloaded anonymously and verified against the release checksums.txt manifest, and isolated user-prefix installs on darwin/arm64 and linux/amd64 verified 0.8.0 (320a32a).
  • Homebrew tap: Commit 9c34019 updated pgsty/infra/barn to 0.8.0 across all four targets and passed local brew fetch, audit, and tap CI.
  • Four-host 7-distro acceptance: Clean initialization was run at baseline commit 6d7870e26cb2f4082a00c188f746783fc027687e across two Linux amd64 hosts (m0, m3, installed from the .deb package) and two macOS arm64 hosts (m1, m5, installed from the verified archive), booting the 7-distro pro inventory from a local LAN repository. Runtime candidate 1c054a027420b5410c6f6feb344e27e001ae1af1 (adding the macOS Homebrew authentication-order fix) was then installed in place on all four hosts and verified across idempotent up (preserving running QEMU processes, VM UUIDs, and disk inodes), two rounds of guest SSH, stop/start, /data disk mounts, Debian locale/XFS checks, control-to-peer SSH, and Ansible inventory reads (ansible all -m ping on both Macs). See the 0.8.0 Release Notes for per-host timings.

Barn 0.7.0 release — 2026-09-16

  • Release CI and assets: Commit 9c6d4896d93733d1cb60a7e5d8591e9a06659c9d (v0.7.0) passed source CI, the packaging snapshot workflow, and the release tag workflow. All 20 public release assets passed anonymous download and SHA-256 verification, public installer checks passed on darwin/arm64 and linux/amd64, and pgsty/infra/barn passed local brew upgrade and brew test.
  • Ubuntu amd64/KVM storage & bootstrap recovery: Tested corrupt ext4/XFS data-disk resets (3.2 s recovery while keeping the QEMU process alive), persistent disk retention across recreate, transient probe failures and busy mounts (preserving existing disk data and UUIDs), read-only 9p share fallback, and resuming a 0.6.0 VM whose initial egress probe had failed (3.3 s recovery, surfacing the expected control-ssh warning where 0.6.0 had already removed the staged control key; see the 0.7.0 Release Notes). Catalog 2026090501 remained unchanged.

Barn 0.6.0 release — 2026-09-05

  • Release CI and assets: Following lifecycle, UX, release-metadata, and CI fixture reviews, commit 057774e3a13477782a2ae07bd71127d03c0f1ae7 (v0.6.0) passed Go 1.27.1 source CI, packaging checks (13d9d70), and the release tag workflow, producing 20 verified release assets. An isolated darwin/arm64 installation verified upgrading from 0.5.0 to 0.6.0 via the public installer, plus barn init and barn plan.
  • macOS arm64/HVF lifecycle acceptance: Verified initial boot, scale-out without restarting the control node, control-to-peer SSH, stop/start, reload, selective recreate, scale-in, guest /etc/hosts refresh, preflight rejection of invalid reload or conflicting partial recreate before stopping existing VMs, degraded status reporting, and SSH exit code 255 passthrough.
  • Catalog 2026090501: Switched the default image to u24:stable and incorporated the Debian/Rocky updates from 2026090302 across all 27 verified qcow2 artifacts on both official endpoints.

Barn 0.5.0 release — 2026-09-03

Commit fc85b65ff6a24b0933b56ae1179be9ada2ba91b1 (v0.5.0) passed Go 1.27.1 source and GoReleaser packaging gates and published the 20-asset pre-release. 0.5.0 introduced confirmation-free whole-deployment barn purge, set https://repo.pigsty.io/barn as the global default repository with --mirror for https://repo.pigsty.cc/barn, removed implicit upstream URL fallback during artifact downloads, and added packaging/image-pipeline/build-official.py for the eight digest-pinned Debian/Rocky targets.

Barn 0.4.0 release — 2026-09-02

Passed make check and all release archive, DEB/RPM, SBOM, checksum, installer, and Homebrew formula gates. barn up gained automatic interactive host setup when run on an unprepared terminal host, vm_disks[].fs defaulted to auto, readiness errors began surfacing the last guest error line, and CLI output formatting was unified across commands.

Barn 0.3.0 release — 2026-09-02

Passed make check and all release packaging gates. Made catalog refreshes explicit via barn update and barn image sync, and added per-node failure stages and log inspection hints to guest readiness error messages.

Barn 0.2.0 release — 2026-09-01

Commit 59d1b62aebb3d044a317e4006cc8a0bf56f4feaf (v0.2.0) passed source CI, packaging workflows, and local four-platform archive, DEB/RPM, and SPDX verification. Live darwin/arm64 HVF acceptance verified selective u24-1 create/SSH/stop/start, incremental cached el9-1 creation, whole-deployment status, and destroy with SSH config cleanup. On Ubuntu 26.04 amd64/KVM, the Linux binary audited a running four-node deployment and connected to its control guest.

Schema-3 Catalog closure — 2026-08-29

Catalog 2026082903 (9 families, 27 artifacts, SHA-256 571b1ff9c7d4d42355df3392ea62a339471c2d01d868669a7625fac8b93f245d, signed by key 4686B39A40F9B562) passed byte-for-byte equality checks between the embedded catalog and published endpoints, a full SHA-256 sweep across all 27 qcow2 artifacts (19 GiB), and a clean-client d13:stable (409.3 MiB) download and qemu-img check on darwin/arm64.

0.1.0 candidates — 2026-08-28 and 2026-08-29

Validated two targeted fixes prior to 0.2.0:

  • Targeted QMP recovery: When QMP sockets were externally removed from two of four running nodes on darwin/arm64, Barn restarted only the two affected nodes (13.7 s to readiness) while keeping the two healthy nodes’ boot IDs unchanged.
  • QEMU-free catalog inspection: Decoupled barn image list and make check from requiring qemu-img on PATH when no local cached qcow2 files need inspection.

EL7/EL8 compatibility — 2026-08-28

Commit 7c666c7 restored el7 and el8 support with regression tests covering pre-recreate runtime preflight ordering and signed-catalog baseline migration. Catalog 2026082801 (9 families, 17 image artifacts, plus 2 socket_vmnet archives) passed full SHA-256 and signature checks, and an isolated darwin/arm64 run booted Rocky Linux 8.10 arm64 under the built-in TCG rule to SSH readiness in 44.2 s (verifying NetworkManager, fixed IP, dba UID/GID 88, and guest markers).

Native replay — 2026-08-27

Verified fixed-IP networking, SSH readiness, default CPU/memory/root/data disks, cloud-init, stop/start, cross-directory CLI invocations, control-node uptime preservation during scale-out, control-to-peer SSH, ignoring upper-layer Pigsty variables, and explicit-only node destruction across darwin/arm64 and linux/amd64 (mx). On Linux, also verified passwordless sudo automation, Debian qemu-bridge-helper group permissions, unprivileged bridge attachment, refusal to uninstall an active bridge with four attached tap interfaces, and clean network teardown after destroy.

3.7 - Barn 0.9.3 Changes

Barn 0.9.3 changes covering resource resize, recovery, paths, sharing, node selection, images, and limits.

This page records the user-visible changes shipped in Barn 0.9.3 on 2026-10-31. The workflow changes began in source commit 27f17bb and were completed by the commits leading to the release. Check barn version when auditing another build.

See the Barn 0.9.3 release notes for the upgrade summary and the complete bilingual release note.

Resource changes and recovery

Edit vm_cpu or vm_mem and run barn resize [node...]. QEMU hot-plugs supported increases when the running VM has reserved capacity; decreases, older VMs, and unsupported backends use a stop/start fallback. The VM identity, disks, and SSH keys remain unchanged. resize refuses image, disk, network, share, creation, removal, and deployment-wide changes; use recreate, up, destroy, or a full selected operation as indicated by its next: field.

Linux and macOS lifecycle, persistent-disk, and replacement operations journal their transition state. If a process is interrupted or receives SIGINT or SIGTERM, the next lifecycle command can recover the operation; cancelled commands report the structured cancelled result. On macOS, barn mac up --no-wait returns when the VM process starts and may leave first-boot SSH and sudo provisioning running. Run barn mac up NAME again to wait for readiness.

Paths

barn init and setup work through directory symlinks. init -o ./lab/config/barn.yml also creates missing parent directories. Existing files still require --force, and writes never follow or replace an output-file symlink.

User-selected paths now accept normal development layouts:

Input Resolution
BARN_HOME Relative paths, ~/, and directory symlinks; may also be the working directory
Inventory and provision --script Symlinks to regular files; each file retains its 4 MiB limit
Linux vm_shares.host Relative to the inventory’s real file, or an absolute/~/ path; directory symlinks work
Local image repository and image import Relative paths, ~/, and symlink inputs
catalogexport output Relative paths and ~/, directory aliases, and automatic parent creation; target must be absent
BARN_MAC_RUNNER and local socket_vmnet archive Relative paths and ~/; archive inputs may be symlinks and still undergo digest verification

CLI-relative paths, including a relative BARN_HOME, are interpreted from the current working directory. Use an absolute BARN_HOME to reach the same deployment from different directories. Barn still rejects / and the user’s home directory as data roots and checks the ownership and structure of its managed state. An owned ~/.ssh directory may be reached through a directory symlink; a linked ~/.ssh/config is still left for manual Include integration.

Shared folders and mount paths

Linux guests use QEMU Virtio-9p, now supported on macOS hosts as well as Linux hosts. QEMU and the guest kernel must support 9p. Native macOS guests continue to use VirtioFS through barn mac --share.

  • Linux share sources use the invoking user’s actual filesystem permissions. A directory may belong to another user, overlap another source, or be writable by multiple guests. Coordinate concurrent writes as you would between host processes.
  • Source paths support spaces, Unicode, and commas. Linux guest mount paths are normalized and escaped for shell commands and fstab, including spaces, Unicode, and shell punctuation.
  • Mac share names support spaces and Unicode: valid UTF-8, 1–255 bytes, excluding ., .., /, and ASCII control characters. Share sources may be directory symlinks.
  • The eight-share limit is removed. Backend device and host resource limits still apply. Linux sources must not overlap BARN_HOME, and mount targets must not overlap each other, system directories, data-disk mounts, or the guest SSH directory.

See Storage and Access for Linux shares and Mac Commands for native macOS shares. Barn never changes host ownership to make a share writable.

Apply only the selected node changes

Explicit selections in plan, up, reload, and recreate preserve unselected peers’ applied definitions, even if those peers were edited or omitted in the current inventory. Uncreated, unselected nodes do not require image resolution. This allows a multi-node edit to be applied in batches:

barn plan node-1 -f barn.yml
barn recreate node-1 -f barn.yml
barn plan node-2 -f barn.yml

The entire inventory must still parse correctly. Conflicts with applied addresses, aliases, control-node roles, or shared deployment settings are checked before stopping or deleting nodes. Omitting selectors evaluates the whole inventory; removing a YAML entry never deletes its VM. See Daily Operations.

Generated template image selection

barn init [template] --image <reference> writes an explicit vm_image under all.vars for every generated node. barn setup [template] --image <reference> uses the same behavior while preparing a generated template. References may be aliases, channels, or version selectors; the flag does not download an image. Existing inventories selected with -f or discovered automatically are never rewritten by this option.

Images and development limits

Area New behavior
Linux node count No separate 20-node ceiling; nodes need distinct usable addresses in the deployment’s /24 and sufficient host resources
External inventory hosts vm_skip: true entries may use hostnames or IPv6 keys; managed VMs still use IPv4, and at least one managed node is required
Linux capacity strings Case-insensitive units; K/M/G/T and KiB/MiB/GiB/TiB are binary, while KB/MB/GB/TB remain decimal
Provisioning --parallel accepts positive integers and uses at most the selected node count; --timeout accepts any positive duration, without the old four-worker or 24-hour ceilings
Image size No 8 GiB artifact ceiling for local imports or catalog artifacts with declared sizes; downloads without a declared size retain the legacy 8 GiB bound
Image imports Arbitrary source filenames; new cache entries use local/<sha256>.qcow2, so same-name files with different contents coexist and identical contents reuse the cache
Local aliases No 256-alias ceiling; updates exceeding the 1 MiB registry metadata limit fail before writing. Reimporting an existing alias preserves its old cache path
Local repositories Relative or linked roots and shared writable directories work; repo.yaml may be a symlink to a regular file
Mac logs Any positive --lines count; requested lines are no longer silently clipped at 1 MiB or 10,000 lines
Fallback ports Try preferred + n × 10000 through port 65535, rather than stopping after five candidates

Provisioning still defaults to one worker and a one-hour deadline, with 1 MiB of captured stdout and stderr per node. These user-facing limits are separate from the internal VM startup scheduler. Image digests, declared sizes, qcow2 structure, retained-disk compatibility, and process identity are still verified. The contributor image pipeline retains its own recipe bounds.

3.8 - Barn 1.1.0 Local Acceptance

Upgrade an Apple Silicon host in place, rebuild Linux and macOS labs, and record reviewable acceptance runs.

This run starts with the installed Barn 1.0.0, installs the public 1.1.0 Homebrew formula, checks upgrade and everyday workflows, then rebuilds the test environment. The script was written before execution. This run has ended: core checks passed, one recovery gap was found, and clipboard/privileged-host checks remain incomplete. The owner authorized deletion of existing local Barn VMs.

Environment and evidence

  • Host: macOS 27.0.1, Apple Silicon, 18 logical CPUs, 128 GiB RAM; QEMU 11.1.3.
  • Initial state: eight running Linux VMs and one running macOS VM named mac1; the normal executable is /opt/homebrew/bin/barn.
  • Upgrade target: public tag v1.1.0, commit b2cc8da8983e710255d504bf26bf5d7b3f182790.
  • Recordings, raw output, assertions, and executable scripts live in a separate acceptance directory outside Git. Each stage produces an asciicast v2 recording with original timing and a viewing copy with shortened waits.
  • Linux checks use the actual user’s ~/.barn. Detailed native Mac checks reuse tests/mac-live-acceptance.py with a dedicated test root and an APFS clone of the prepared base; final checks cover the normal user path.
  • Terminal recordings capture output without keyboard input, passwords, or private SSH keys. Desktop checks use native application controls; the screenshot is in the acceptance chat. Clipboard status is recorded below.

Execution order

Stage Operations Pass condition
U01 Upgrade baseline Record the old version, node identities, public SSH fingerprints, and data sentinels; stop Linux and Mac VMs Reviewable baseline; every target stops cleanly
U02 Local installation brew upgrade pgsty/infra/barn; check version, helper, signatures, notarization, and brew test Normal PATH resolves to public 1.1.0; CLI, helper, and App match
U03 Upgrade recovery Start existing VMs and compare identities, data, and SSH; then remove old VMs Upgrade preserves identities and data; explicit disposal succeeds
L01 First run Run barn up from an empty directory; check generated inventory, confirmation, progress, and SSH Default native Ubuntu 24.04 guest becomes ready
L02 Inventory and catalog Four templates; valid/invalid YAML; -I/-A and aliases; both signed official repositories; metadata and cache Valid input resolves consistently; failures have correct classification and exit codes; overrides leave inventory unchanged
L03 Two nodes and storage Ubuntu/Debian pair; persistent ext4, temporary data, XFS, space/Unicode paths, read-only and writable 9p shares Filesystem types, permissions, and bidirectional reads/writes match the inventory
L04 Network and access Fixed IPs, control-node SSH to its peer, DNS/HTTPS, SSH config, exec argument boundaries and remote exit codes Correct addresses, targets, output streams, and exits
L05 Lifecycle Repeated up, selected stop/start/restart, CPU/memory increase/decrease, reload, selected recreation Idempotent calls retain PIDs; resources change in the guest; peers and persistent data remain intact
L06 Failures and deletion Omitted nodes, missing share paths, interrupted guest process recovery, provision success/failure/timeout, purge cancellation No implicit deletion; actionable failures; retries recover; cancellation preserves state
L07 Retention Recreate, selected and whole destroy, create again, purge Root/temporary data resets; persistent data follows each command’s retention rules
L08 Image matrix Eight supported arm64 image families and Debian amd64 under TCG SSH, architecture, OS identity, and data disk work; el8 arm64 uses expected TCG
M01 Multiple Macs and identity Automated two-VM checks, independent identities and disks, immutable shared base Identities differ; cloning and lifecycle operations preserve the base
M02 Mac networks and shares Separate subnets, host access, guest isolation, DNS/HTTPS, VirtioFS, remote arguments Network and sharing behavior matches the reference
M03 Mac lifecycle Stop/start, resource configuration, third-running-VM limit, recreate identity rotation, selected destruction Identity retention/rotation matches the operation; the peer remains intact
M04 Desktop and clipboard Open desktop, verify login, copy text both ways, close the window, check SSH Usable desktop; exact text transfer; window closure keeps the VM running
F01 Final state Remove dedicated test VMs; check normal installation, state, diagnostics, and SSH config; index recordings No leftover test processes; every result has output or visual evidence

Assertions and retries

Every command records arguments, exit status, duration, and raw output. Failed assertions preserve the environment. Script corrections create separate retry records; product defects are recorded independently. Failed footage remains available. Only viewing copies shorten waits.

The existing host network is checked and reused first. Operations requiring host sudo inspect actual authorization and the execution plan; unavailable checks are marked blocked. Apple OS image updates, physical Intel Macs, and Linux KVM are separate environment coverage.

Results and playback

This run ended at 11:08 on 2026-11-09 (Asia/Shanghai). The normal PATH resolves to public Barn 1.1.0. Upgrade recovery passed for the original eight Linux VMs and one Mac VM before their authorized deletion. All test VMs, the dedicated Mac data root, and the additional 120 GiB base were removed; the original default macOS base remains.

Stage results

Area Result and evidence
Homebrew installation and upgrade Passed; matching CLI/native components, signatures, notarization, and brew test; existing VM identities and persistent-path data survived
CLI, inventory, and catalogs Passed; first run, four templates, architecture aliases, invalid inputs, both signed official sources, local catalog build/verification/import/pruning
Linux pair and lifecycle Passed; fixed IPs, control-node SSH, DNS/HTTPS, arguments/exits, ext4/XFS, read-only/writable 9p, resource changes, recreation, persistent disk reattachment
Failures and recovery Missing shares, provision failure/timeout, cancelled purge, and process interruption after disk sync passed; immediate power-loss readiness issue below
Native Mac automated suite All 15 phases passed; separate identities/disks/networks, unchanged base, resource settings, running-VM limit, recreation, selected destruction
Normal Mac data path Passed; complete installation of a new 120 GiB base from the existing IPSW, read-only/writable VirtioFS, SSH Include, original default base retention, extra-cache pruning
Interactive terminals Linux and Mac passed; real guest TTY allocation and interactive shell exit 17 propagated; all three Linux log sources parsed as JSON
Desktop Logged-in Finder desktop visually confirmed; closing the window kept the same VM process and SSH available; bidirectional clipboard remains unverified
Source checks make check and make mac-native-test passed on the release commit
Final state Zero Linux or Mac VMs and no remaining test VM processes; barn doctor, barn mac doctor, and network status healthy

Deleting the control node promotes a remaining node. After updating admin_ip in the inventory as instructed, creating the former node again reattached its retained disk and preserved the peer’s PID and data. With the old inventory requesting a control-role reversal, up correctly refused implicit recreation.

Image matrix

All nine configurations passed image acquisition, boot, SSH, OS/architecture checks, data writes, stop/start, and cleanup. -I/-A left the inventory file unchanged. EL7/arm64 returned usage / 2 because the catalog has no matching artifact. EL7/amd64 on this host returned capability / 3 without creating a deployment.

Image Guest architecture Accelerator Result
u22@20261020.0.1 arm64 hvf Passed
u24@20261017.0.1 arm64 hvf Passed
u26@20261016.0.1 arm64 hvf Passed
d12@20261026.2643.1 arm64 hvf Passed
d13@20261103.2651.1 arm64 hvf Passed
el8@8.10.20240528.3 arm64 tcg,thread=multi Passed
el9@9.8.20260525.2 arm64 hvf Passed
el10@10.2.20260525.0 arm64 hvf Passed
d13@20261103.2651.1 amd64 tcg,thread=single Passed

Readiness marker after forced termination

Immediately after successful recreation of an Ubuntu 24.04 arm64/HVF node, its verified QEMU process was forcibly terminated. The next up booted the guest, but both /var/lib/barn/ready.json and error.json were absent. SSH and persistent data worked and cloud-init had finished; up timed out after 180 seconds with exit 5. This observed recovery gap is preserved in 03-linux-lifecycle-r3 and 03-power-loss-investigation.

The following recovery restored readiness while preserving the running VM PID, its peer, and persistent data:

barn exec meta -- sudo -n /usr/local/libexec/barn-finalize
barn up meta -f barn.yml

In a second trial, the guest ran sudo sync before the process was terminated, again after checking its command line and QMP UUID. up recovered automatically. The immediate-interruption behavior was observed once under the stated conditions; persistence timing and other images need further investigation. Relevant 1.1.0 code is the readiness probe and guest-setup retry entry.

Checks not completed

  • Bidirectional clipboard: native UI automation did not reliably inject the host system clipboard, and the browser tool has an isolated clipboard. Manual host input had not arrived when this run ended, so this remains unverified.
  • Privileged host changes: sudo -n required a password, including access to root-owned network planning state. Existing network reuse, privilege-error classification, and read-only hosts plans were checked; network reinstall/uninstall and /etc/hosts writes were not executed.
  • This host is Apple Silicon. Physical Intel Macs, Linux/KVM, and Apple OS image updates remain separate environment coverage.

Local evidence

Evidence lives at ~/barn-acceptance/1.1.0-20261109-101029/. Open player/index.html for offline playback of 23 asciicasts: approximately 58.9 minutes of original timing or 12.1 minutes in the viewing copies. Viewing copies change timestamps only; output bytes and SHA-256 are checked per recording. Terminal recordings do not contain desktop frames; the desktop screenshot is in the acceptance chat.

evidence/plan-before-execution.md and .zh.md preserve the original plan. Stage directories contain script snapshots, commands, exit codes, and raw output; findings.json records the recovery gap and incomplete checks. Initial footage and retries remain for script corrections involving Homebrew prompts, SSH aliases, create/recreate plan categories, control-role promotion, TCG parameters, and error classification. Full recordings stay local; this site contains the acceptance account.