# macpine gives macOS developers Alpine VMs with a single alpine command

> macpine is a Go tool that creates and manages lightweight Alpine Linux virtual machines on macOS through QEMU, with port forwarding, bridged networking and cross architecture emulation. Its real value is running nested container workloads on a Mac, and its real dependency is a QEMU version you may have to pin.

**beringresearch/macpine** — Lightweight Linux VMs on MacOS

- Repository: https://github.com/beringresearch/macpine
- Website: https://beringresearch.github.io/macpine/
- Stars: 1,042 · Forks: 30
- Language: Go
- License: Apache-2.0
- Published: 2026-10-19 · Updated: 2026-10-19 · Language: en
- Canonical page: https://hysenlabs.com/projects/beringresearch-macpine

## Tiny VMs exist here to host container runtimes

The motivation section explains the design in three steps, and the order matters. The goal is to let macOS users easily spin up and manage lightweight Alpine Linux environments. The second step is to use those tiny VMs to take advantage of containerisation technologies, with Incus, LXD and Docker named explicitly. The third is to build and test software on x86_64 and aarch64 systems.

That middle step is the reason the project exists. A container runtime on a Mac has a kernel constraint that a Linux virtual machine does not, and the nested container tooling in the list wants a real Linux kernel underneath it. A small Alpine guest is the cheapest place to put one, and a full desktop Linux image would be mostly unused disk.

The result is positioned as lightweight rather than general purpose. The features listed are port forwarding, automatic file sharing, bridged networking and aarch64 and x86_64 emulation, and the guest is Alpine, which is a small distribution by design. The audience is therefore a developer on a Mac who needs repeatable, disposable Linux environments, not someone looking for a workstation virtualiser with a graphical console and snapshots of a desktop OS.

## One binary, a cobra command tree, and a local bin directory

The command surface is a cobra tree, and the binary it installs is named alpine rather than macpine, which is worth internalising before you read any error message. The available commands are completion, delete, edit, exec, help, import, info, launch, list, pause, publish, rename, restart, resume, ssh, start, stop and tag.

The lifecycle is straightforward. alpine launch creates and starts an instance, alpine list shows what exists, and alpine ssh attaches an interactive shell over ssh. alpine exec runs a single command on an instance over ssh, which is what you want in a script. alpine pause and alpine resume freeze and thaw a running instance, restart stops and starts it, and alpine edit opens the instance configuration for changes. There is also an alpine info command for details, and alpine completion to generate shell autocompletions.

The repository layout supports that split. There is a main.go at the root, a cmd/ directory for the command tree, a qemu/ directory for the virtualisation layer, a host/ directory for the macOS side, utils/ for shared helpers and scripts/ for the build and release helpers. The go.mod declares Go 1.25.3 and a short dependency list: cobra for the CLI, filippo.io/age for encryption, golang.org/x/crypto, golang.org/x/term, and gopkg.in/yaml.v3 for configuration. The Makefile builds into a local bin/ directory, installs to /usr/local/bin by default, and has a xcompile target that cross builds darwin binaries for amd64 and arm64 with gox, matching the .goreleaser.yaml in the root.

## Port forwarding syntax carries the instance to the host

Networking is where the command line is densest, and the syntax is worth learning once because it appears in every launch command. The -s flag exposes the instance SSH port on a host port, and the -p flag forwards further host ports into the instance.

The forms multiply quickly. A bare list forwards the same port number on both sides, a colon form maps a different host port to an instance port, and a trailing u marks a port as UDP. The README's own examples walk through this: an instance launched with -s 2022 -p 8888,5432 forwards those two ports one to one, a launch with -s 8022 -p 8081:8082,8083 remaps one of them, and a launch with -s 9023 -p 9091u,9092:9093u mixes UDP forwarding with a remapped UDP port. Note that the SSH example in that last line uses 9023 while the prose describes 9022, so the examples are illustrations rather than a specification to copy without checking.

The mapping direction is worth keeping straight. Traffic to a host port is forwarded to the corresponding instance port, so the host side is the one you open locally and the instance side is the one the service inside the guest listens on.

## Bridged networking needs root, unless socket_vmnet is running

The three networking modes have very different operational costs. The default vmnet networking gives the instance a real, LAN reachable IP address, and it requires sudo for alpine start, stop, ssh and exec, because QEMU calls macOS's Vmnet.framework directly and that needs root.

The documented way around that is socket_vmnet, which does the one privileged step once in the background and lets macpine talk to it over a Unix socket instead. macpine detects a running socket_vmnet daemon automatically, and if it is not found macpine transparently falls back to QEMU's native vmnet-shared networking, which needs sudo as before. So the failure mode is quiet rather than loud: a missing daemon does not produce an error, it produces a slower, more privileged path.

Automatic file sharing is also part of the feature set, which is convenient but worth understanding as a share rather than a copy, since the documentation lists it as a distinct capability from the disk and port plumbing.

For a team that runs this on shared workstations, the difference between requiring sudo for every command and installing one background service is the difference between a tool people use and a tool people route around.

## Publishing an instance is how you move it between machines

Instances can be packaged for backup or sharing as .tar.gz files, and the two commands involved are alpine publish and alpine import. Publishing a named instance creates a file with that name, so alpine publish cheerful-result produces cheerful-result.tar.gz, and alpine import takes that archive back in. The listing command shows the state of the estate, and the example output shows what it reports:

```bash
alpine list

NAME                 STATUS      SSH    PORTS   ARCH      PID     TAGS
cheerful-result      Running     2022           aarch64   26568
glittering-swing     Running     3022           x86_64    57206   emulation,intel
```

Two things in that output matter. The ARCH column tells you whether a guest is running natively or through emulation, and the TAGS column records that fact explicitly, with emulation and intel on the x86_64 instance. The SSH column carries the host port the instance was launched with, which is how you connect to it later without remembering the launch flags.

This is also where the cross architecture motivation pays off. A developer can launch an aarch64 instance, or run x86_64 code on an Apple Silicon Mac through emulation, and then move that environment to another machine as a single archive. The security implication is that a published archive contains a disk image, and the presence of filippo.io/age in the dependencies suggests encryption is available for the publish path.

## The QEMU version is the operational risk

macpine depends on QEMU 7.22.0 or newer, and the Homebrew install pulls it in automatically, while a manual install needs brew install qemu. The known issue is worth reading before the first launch, because it looks like a macpine bug and is not.

QEMU 11.1.1 has a regression that can hang aarch64 instances on boot when vmnet networking is used. The documented workaround is to pin QEMU to 10.0.3 until the problem is fixed upstream, and there is a Troubleshooting page in the documentation. This is the kind of constraint that decides whether the tool fits your environment: if the QEMU in your environment is managed centrally and cannot be pinned, the failure appears as an instance that never boots, with no error from macpine.

The documentation set also covers the areas people hit later. There are pages for advanced port forwarding and securing instance SSH, editing instance configurations, running Incus within instances, running LXD within instances, hardening instances, and auto-starting instances at login, plus general troubleshooting. The autostart path has a visible implementation: the Makefile has an agent target that takes utils/alpineDaemonLaunchAgent.plist, substitutes the path to the alpine binary and writes the result to ~/Library/LaunchAgents/alpineDaemonLaunchAgent.plist, which is a LaunchAgent rather than a daemon the project owns.

## First instance, from Homebrew to a shell

The recommended install is a single command that installs the alpine binary and the qemu dependency:

```bash
brew install macpine # installs `alpine` command and `qemu` dependency automatically
```

Create and start an instance with the defaults:

```bash
alpine launch
```

Then attach a shell, replacing instance-name with the name the launch gave it:

```bash
alpine ssh instance-name
```

To size an instance explicitly, the launch flags are -d for disk, -c for cpus, -m for memory and -a for the architecture:

```bash
alpine launch -d 10G -c 4 -m 2048   # create an instance with a 10GB disk, 4 cpus, and 2GB of RAM
```

If you want to see every option and its default before committing, alpine launch -h prints them.

Other installation routes exist for people who cannot use Homebrew. MacPorts is supported with sudo port install macpine, and building from source needs a working go compiler and qemu, after which make compiles into a local bin/ directory and make install puts the binaries in /usr/local/bin, with PREFIX pointing it elsewhere if needed. A prebuilt binary can also be downloaded and moved into your PATH, with the architecture detected by the uname check in the download snippet in the README.

## Conclusion

macpine suits a macOS developer who needs a real Linux kernel for nested container tooling, or who wants to test a build against both aarch64 and x86_64 without a second machine. It does not suit someone who only needs a container, since a container runtime on the Mac is simpler, and it does not suit a setup where the QEMU version in use cannot be pinned, because the known 11.1.1 regression can hang aarch64 instances on boot with vmnet networking. Verify first that QEMU is at least 7.22.0, decide whether bridged networking will rely on socket_vmnet rather than sudo, and confirm that the alpine command in your PATH is the one you want before you build a workflow around it.

## FAQ

### What is macpine and what does it do?

macpine creates and manages lightweight Alpine Linux virtual machines on macOS through QEMU, with port forwarding, automatic file sharing, bridged networking and aarch64 and x86_64 emulation.

### How do I install macpine on macOS?

The recommended route is brew install macpine, which installs the alpine command and the qemu dependency automatically. MacPorts is also supported with sudo port install macpine, and there is a source build with make and make install.

### Does macpine need sudo to run instances?

Only for bridged vmnet networking, which requires root for alpine start, stop, ssh and exec because QEMU calls macOS's Vmnet.framework directly. Installing and running socket_vmnet removes that requirement, and macpine falls back to the native vmnet-shared networking when no daemon is detected.

### Which QEMU version does macpine need?

QEMU 7.22.0 or newer. The README documents a known regression in QEMU 11.1.1 that can hang aarch64 instances on boot with vmnet networking, and suggests pinning QEMU to 10.0.3 until it is fixed upstream.

### How do I move a macpine instance to another machine?

Package it with alpine publish, which creates a .tar.gz file named after the instance, and bring it back with alpine import. The alpine list command shows each instance's name, status, SSH port, architecture and tags, including the emulation tag on cross architecture guests.

## Sources

- [beringresearch/macpine on GitHub](https://github.com/beringresearch/macpine)
- [License: Apache-2.0](https://github.com/beringresearch/macpine/blob/main/LICENSE)
- [Project website](https://beringresearch.github.io/macpine/)
- [README](https://github.com/beringresearch/macpine/blob/main/README.md)
- [Releases](https://github.com/beringresearch/macpine/releases)

---

Hysen Labs editorial analysis, written from the project's own repository and release notes. Cite the canonical page: https://hysenlabs.com/projects/beringresearch-macpine
