# Running Ubuntu on Apple's Container

> Posted: 2026-06-12 · Updated: 2026-06-12 · Tags: macos, containers, ubuntu, systemd, docker
>
> Getting a real Ubuntu box running under Apple's container means putting systemd back as PID 1. Here's the Dockerfile I used and what the build/run flags actually do.


After [getting Apple's `container` running]({{< ref "2026-06-12_MacOS-Container-Tool" >}}), the thing I actually wanted was a normal Ubuntu box to poke around in. That took one more step than I expected: systemd.

A container runs whatever its `CMD` is as PID 1. The stock `ubuntu:26.04` image doesn't ship an init, so you boot into a bare shell and not much else. If you want services, units, and a machine that behaves like a real Ubuntu install, systemd has to be the thing running as init. So you build your own image.

Here's the Dockerfile. Most of it is the well-worn systemd-in-a-container cleanup: install systemd, then delete the units that make no sense inside a container so it doesn't sit there trying to start hardware that isn't there.

```dockerfile
FROM ubuntu:26.04

ENV container container

RUN apt update -y && apt upgrade -y && apt install -y systemd init
RUN (cd /lib/systemd/system/sysinit.target.wants/; for i in *; do [ $i == \
systemd-tmpfiles-setup.service ] || rm -f $i; done); \
rm -f /lib/systemd/system/multi-user.target.wants/*;\
rm -f /etc/systemd/system/*.wants/*;\
rm -f /lib/systemd/system/local-fs.target.wants/*; \
rm -f /lib/systemd/system/sockets.target.wants/*udev*; \
rm -f /lib/systemd/system/sockets.target.wants/*initctl*; \
rm -f /lib/systemd/system/basic.target.wants/*;\
rm -f /lib/systemd/system/anaconda.target.wants/*;

VOLUME [ "/sys/fs/cgroup" ]
CMD ["/usr/sbin/init"]
```

Build it, create a machine from it, and run:

```console
❯ container build -t local/devel -f devel.dockerfile
[+] Building 356.1s (7/7) FINISHED
 => [resolver] fetching image docker.io/library/ubuntu:26.04
 => [internal] load build definition from Dockerfile
 => [linux/arm64 1/3] RUN apt update -y && apt upgrade -y && apt install -y systemd init
 => [linux/arm64 2/3] RUN (cd /lib/systemd/system/sysinit.target.wants/; for i in *; do ... )
 => exporting to oci image format
local/devel:latest

❯ container machine create --name devel --set-default local/devel
devel

❯ container machine run
chris.houdeshell@devel:/Users/chris.houdeshell$
```

That last line is the Ubuntu shell. You're in.

What the flags are doing, quickly:

- `container build -t local/devel -f devel.dockerfile`: `-t` tags the image as `local/devel`; `-f` points at the Dockerfile by name, since I didn't name it `Dockerfile`.
- `container machine create --name devel --set-default local/devel`: builds a VM named `devel` from that image. `--set-default` makes it the machine everything else targets, so I don't have to spell out the name on every command.
- `container machine run`: boots the default machine and drops me into its shell.

One thing worth knowing: that build ran just shy of six minutes. The tool is young and the build path isn't quick yet, so don't assume it hung.


---

Source: https://houdeshell.dev/post/2026-06-12_ubuntu-on-apple-container/
