Skip to content

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

Return to the regular view of this page.

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 - 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.

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.

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.

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.

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.

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.

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.

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.

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.

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.