← Back to list

Apple Container Machine: Bring Linux into Your Mac

Addo Zhang · 2026-06-10 21:58 · 93 claps · 8.0 min read
#macos #containers #apple #docker #linux
Open on Medium ↗
Wiki topics: ☁️ · DevOps & Cloud 🔓 · Open Source

Apple Container Machine: Bring Linux into Your Mac

TL;DR

container machine is the often-overlooked other half of Apple Container: instead of running a single process, it runs a complete Linux development environment, automatically mounts your macOS $HOME, supports systemd, and feels much closer to WSL2 than Docker.

From Cargo Box to Study Room: What Is Container Machine?

This series has covered four articles so far:

After reading all four, your mental model of Apple Container is probably: each container gets its own VM, container run nginx:latest spins up an nginx process inside that VM, and the usage is similar to Docker.

But Apple Container has carried another subcommand since version 0.1.0: [container machine](https://github.com/apple/container/blob/main/docs/container-machine.md). It rarely gets mentioned, and the documentation is relatively brief.

What container machine does, in one sentence: run a complete Linux system, not just a single process.

Specifically, when you start a container machine:

  • The VM runs the image’s own init system (e.g., systemd) instead of Apple's minimalist [vminitd](https://github.com/apple/containerization)
  • Your macOS username and $HOME directory are automatically mounted inside, with zero configuration
  • Once inside, you are yourself — your username is unchanged, and /Users/<username> is your Mac home directory

This is a fundamental difference from regular containers. A regular container is an application model: give a process an isolated execution environment, run it, then exit. container machine is an environment model: bring in a complete Linux system. The Mac is the living room, Linux is the study room, and the door between them is always open.

In the broader Apple Container series, this article fills in the fourth piece of the puzzle: framework introduction → basic containers → evolution assessment → development environment. If you’re on a Mac and want to “use Linux toolchains directly without maintaining a VM,” container machine is built for exactly that use case.

Container vs Container Machine: Two Modes

Both use the same underlying engine — OCI images, isolated lightweight VMs, managed through the same CLI. The difference is only in how they boot:

  • container run → ignores the image's init, directly launches the entrypoint process, exits when done
  • container machine create → hands control to the image's own init system (e.g., systemd), bringing the full Linux environment to life

Same Ubuntu image, two usage modes, two completely different results.

| | container run | container machine | |---|---|---| | Role | Application model: run a single process/service | Environment model: full Linux development environment | | Init system | vminitd (Apple's own, minimalist) | Image's built-in (e.g., systemd) | | macOS $HOME | Not mounted | Auto-mounted, zero config | | User identity | root or image-defined user | Automatically matches host username | | Lifecycle | Exits when process ends | Persistent, requires explicit stop | | Typical use case | CI, running services, testing | Daily development environment |

Relationship to Docker

container run and docker run are spiritually the same thing: process/application model, stateless, entrypoint-driven. If you've only used this half and think Apple Container is just "a native Docker replacement," that's not wrong — but you've only used half of it.

container machine is actually closer to WSL2 (on Windows) or Lima (its predecessor on Mac): a persistent Linux environment with the host filesystem integrated, where you can run systemctl to manage services.

Relationship to VMware Fusion

On the surface, container machine looks most like VMware Fusion — both run full Linux, both have systemd, both are persistent. But there's one fundamental difference:

VMware Fusion is state-accumulation based. You install software inside, change configs, and the VM gradually becomes more and more “yours” — its state becomes unreproducible, and when something breaks, it’s a headache.

**container machine is declarative.** The environment is defined by a Dockerfile. If something goes wrong, container machine rm it and rebuild — the new one is identical to the original. Your files are in Mac's $HOME; not a single one is lost.

The core distinction isn’t about weight, it’s about the relationship between environment and data: VMware Fusion mixes environment and data together, both living inside the VM. container machine separates the two — the environment is declared, and data lives on the Mac. In this respect, it's closer in spirit to devcontainer: describe your development environment with a Dockerfile or config file, and the environment itself is disposable, rebuildable, and shareable with your team, while code and data always remain on the host.

Quick Start

This assumes you’ve already installed Apple Container and started the service. If not, download the latest signed installer (currently 1.0.0) from the GitHub releases page, double-click to install, then run:

container system start

Note: The project currently only officially supports macOS 26; issues found on macOS 15 are no longer being fixed. For the full installation walkthrough, refer to Apple Container Unboxing & Practice, but note that the version information in that article is now outdated.

Create and Enter Your First Machine

container machine create alpine:latest --name dev

Once created, enter an interactive shell:

container machine run -n dev

Once inside, run two commands to verify:

whoami   # outputs your macOS username, not root
pwd      # /Users/<your-username>, which is your Mac home directory

The output of these two lines is the most intuitive difference between container machine and a regular container — you're not in some foreign Linux environment; you brought your own identity and files with you.

You can also skip the shell and run a single command directly inside the machine:

container machine run -n dev uname -a
container machine run -n dev -- cat /proc/cpuinfo   # add -- when the command has flags

If the machine is stopped when container machine run is called, it will automatically start it first — no manual start required.

Set a Default Machine

Typing -n dev every time gets old. You can set a default:

container machine set-default dev

After this, container machine run will operate on dev without needing to specify the name.

Common Commands

Lifecycle Management

container machine ls              # list all machines
container machine inspect dev     # view detailed info (JSON)
container machine stop dev        # stop
container machine rm dev          # delete, also clears persistent storage

Adjusting Resources

container machine set -n dev cpus=4 memory=8G
container machine stop dev
container machine run -n dev -- nproc   # verify the change took effect

Resource changes take effect after the next start — remember to stop and run again after making changes. The default memory is half the host machine’s memory.

The mount mode is also adjustable. The $HOME mount supports three modes:

  • rw (default): read-write, real-time sync between Mac and Linux
  • ro: read-only, Linux cannot write to Mac files
  • none: no mount, the machine is completely isolated from the host filesystem
container machine set -n dev home-mount=ro

The m Shorthand

machine has an alias m, usable for all subcommands:

container m ls
container m run
container m stop dev
container m set -n dev cpus=4 memory=8G

This saves a lot of typing, especially for the high-frequency container m run, which feels almost as natural as ssh-ing into a VM.

Linux Services Built-In: systemd in Practice

This is where container machine differs most from regular containers. You can't run systemd in a regular container — vminitd only handles launching the entrypoint, with no concept of service management. But container machine uses the image's own init system, so as long as the image has /sbin/init, systemd can run in full.

The alpine image doesn’t include systemd. To experience this capability, you need to build an image that supports it. The official docs provide a Dockerfile for Ubuntu 24.04:

FROM ubuntu:24.04

ENV container container

RUN apt-get update && \
    apt-get install -y \
    dbus systemd openssh-server net-tools iproute2 iputils-ping curl wget vim-tiny man sudo && \
    apt-get clean && \
    rm -rf /var/lib/apt/lists/* && \
    yes | unminimize

RUN >/etc/machine-id
RUN >/var/lib/dbus/machine-id

RUN systemctl set-default multi-user.target
RUN systemctl mask \
      dev-hugepages.mount \
      sys-fs-fuse-connections.mount \
      systemd-update-utmp.service \
      systemd-tmpfiles-setup.service \
      console-getty.service
RUN systemctl disable \
      networkd-dispatcher.service

RUN sed -i -e 's/^AcceptEnv LANG LC_\*$/#AcceptEnv LANG LC_*/' /etc/ssh/sshd_config

A few key points:

  • >/etc/machine-id and >/var/lib/dbus/machine-id: clears the machine ID so the system auto-generates a unique ID on first boot, avoiding multiple machines sharing the same identifier
  • systemctl mask: disables several units that cause the boot process to hang in virtualized environments
  • systemctl set-default multi-user.target: skip the GUI, go straight to the command line

Build and create:

container build --dns 8.8.8.8 -t local/ubuntu-machine:latest .
container machine create local/ubuntu-machine:latest --name ubuntu
container machine run -n ubuntu

By default, containers during the build process have no available DNS, which causes apt-get update to fail because domain names can't be resolved. Adding --dns 8.8.8.8 fixes this.

Once inside, you have a complete Ubuntu environment where you can use systemd to manage services:

sudo systemctl start ssh
sudo systemctl status ssh
sudo systemctl start postgresql   # start directly after installing

No different from a real Linux machine.

Custom Images and User Initialization

Image Requirements

Any Linux image that has /sbin/init at the root can serve as the base for a container machine. It doesn't have to be systemd — other init systems (like OpenRC) work too.

User Initialization

On first boot, container machine automatically runs a built-in script that creates a user inside Linux with exactly the same username, UID, and GID as your host machine. That's why whoami immediately returns your own name when you enter.

If the built-in initialization logic isn’t enough — say you want to add sudo privileges, write in an SSH public key, or configure dotfiles when the user is created — you can place a custom script in the image at:

/etc/machine/create-user.sh

This script is executed once as root on first boot and has access to the following environment variables:

| Variable | Meaning | | — -| — -| | CONTAINER_USER | Username | | CONTAINER_UID | User ID | | CONTAINER_GID | Group ID | | CONTAINER_HOME | Home directory path | | CONTAINER_MACHINE_ID | Machine identifier |

A practical example — creating the user and configuring sudo:

#!/bin/sh
useradd -m -u "$CONTAINER_UID" -g "$CONTAINER_GID" "$CONTAINER_USER"
echo "$CONTAINER_USER ALL=(ALL) NOPASSWD:ALL" >> /etc/sudoers

The script only runs once; it won’t re-execute on subsequent reboots. If you want to reset user initialization, just container machine rm it and rebuild — this is another benefit of the "declarative environment" approach: the state is clear, with no historical baggage.

Summary

container machine and regular containers share the same engine, but they solve completely different problems.

The answer from regular containers is: give a process a clean execution environment. The answer from container machine is: bring Linux in. Your files on Mac don't need to move, you don't need to switch identities, and when the environment breaks just delete and rebuild — your code is still there.

This design was specifically called out in the 1.0.0 release notes — Apple clearly considers it a standout capability of the project. If you have a use case on Mac that requires “a real Linux environment,” whether that’s running systemd services, testing multi-distro compatibility, or simply wanting a development sandbox that doesn't pollute your Mac, container machine is worth trying.

After all, compared to maintaining a VMware virtual machine, container machine run is a lot more natural.

References


메타데이터
post_id
a79fa6efb9eb
slug
apple-container-machine-bring-linux-into-your-mac-a79fa6efb9eb
url
https://medium.com/@addozhang/apple-container-machine-bring-linux-into-your-mac-a79fa6efb9eb
canonical_url
https://medium.com/@addozhang/apple-container-machine-bring-linux-into-your-mac-a79fa6efb9eb
author_url
https://medium.com/@addozhang
status
ok
fetched_at
2026-07-14 08:13:23