Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
10 changes: 10 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,16 @@ Start the system service with:
container system start
```

### Run your first container

```bash
container run --rm alpine echo hello
```

This pulls the `alpine` image, runs it in a lightweight Linux VM, prints `hello`, and
removes the container when it exits. See the [tutorial](./docs/tutorials/start-here.md)
for a fuller walkthrough that builds and publishes an image of your own.

### Upgrade or downgrade

For both upgrading and downgrading, you can manually download and install the signed installer package by following the steps from [initial install](#initial-install) or use the `update-container.sh` script (installed to `/usr/local/bin`).
Expand Down
20 changes: 4 additions & 16 deletions docs/command-reference.md
Original file line number Diff line number Diff line change
Expand Up @@ -906,22 +906,10 @@ container volume create --opt journal=journal --opt size=10g myvolume

**Anonymous Volumes**

Anonymous volumes are auto-created when using `-v /path` or `--mount type=volume,dst=/path` without specifying a source. They use UUID-based naming (`anon-{36-char-uuid}`):

```bash
# Creates anonymous volume
container run -v /data alpine

# Reuse anonymous volume by ID
VOL=$(container volume list -q | grep anon)
container run -v $VOL:/data alpine

# Manual cleanup
container volume rm $VOL
```

> [!NOTE]
> Unlike Docker, anonymous volumes do NOT auto-cleanup with `--rm`. Manual deletion is required.
Using `-v /path` or `--mount type=volume,dst=/path` without a source auto-creates a
named volume for you, tagged with the `com.apple.container.resource.anonymous` label.
See [Mounts and volumes](./volumes.md#anonymous-volumes) for how to find and clean
these up.

### `container volume delete (rm)`

Expand Down
72 changes: 72 additions & 0 deletions docs/container-inspection.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,72 @@
# Inspecting containers and images

Get detailed, machine-readable information about your containers and images.

## Get container or image details

`container image list` and `container list` provide basic information for all of your images and containers. You can also use `list` and `inspect` commands to print detailed machine-readable output for resources.

Use the `inspect` command and send the result to the `jq` command to get pretty-printed JSON for the images or containers that you specify:

<pre>
% container image inspect web-test | jq
[
{
"configuration": {
"name": "web-test:latest",
...
},
"variants": [
{
"platform": {
"os": "linux",
"architecture": "arm64"
},
"config": {
"created": "2025-05-08T22:27:23Z",
"architecture": "arm64",
...
% container inspect my-web-server | jq
[
{
"configuration": {
"mounts": [],
"id": "my-web-server",
"resources": {
"cpus": 4,
"memoryInBytes": 1073741824,
},
...
},
"status": {
"state": "running",
"networks": [
{
"ipv4Address": "192.168.64.3/24",
"ipv4Gateway": "192.168.64.1",
"hostname": "my-web-server.test.",
"network": "default"
}
],
...
}
}
]
</pre>

Use the `list` command with the `--format` option to display information for all images or containers. In this example, the `--all` option shows stopped as well as running containers, and `jq` selects the IP address for each running container:

<pre>
% container ls --format json --all | jq '.[] | select ( .status.state == "running" ) | [ .configuration.id, .status.networks[0].ipv4Address ]'
[
"my-web-server",
"192.168.64.3/24"
]
[
"buildkit",
"192.168.64.2/24"
]
</pre>

See [Networking](./networking.md) for how to publish ports, reach the host from a
container, set a custom MAC address, and create isolated networks.
38 changes: 37 additions & 1 deletion docs/container-system-config.md
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,42 @@ For a guided walk-through on setting default values, see [Container system confi

Source of truth: [`Sources/ContainerPersistence/ContainerSystemConfig.swift`](../Sources/ContainerPersistence/ContainerSystemConfig.swift).

## Viewing your configuration

Use `container system property list` (alias `ls`) to print the merged configuration
the `container` service is actually using — combining your `config.toml` with
hardcoded defaults for anything you haven't set:

```console
% container system property list
[build]
cpus = 2
memory = "2048mb"
rosetta = true
image = "ghcr.io/apple/container-builder-shim/builder:0.12.0"

[container]
cpus = 4
memory = "1gb"

[dns]
domain = "test"

[kernel]
binaryPath = "opt/kata/share/kata-containers/vmlinux-6.18.5-177"
url = "https://github.com/kata-containers/kata-containers/releases/download/3.26.0/kata-static-3.26.0-arm64.tar.zst"

[network]

[registry]
domain = "docker.io"

[vminit]
image = "ghcr.io/apple/containerization/vminit:0.34.0"
```

Pass `--format json` for machine-readable output.

## Top-level schema

```toml
Expand Down Expand Up @@ -48,7 +84,7 @@ Defaults applied when `container run` / `container create` is invoked without `-

| Key | Type | Default | Description |
|----------|-----------|---------|----------------------------------------------------------------------------|
| `domain` | `String?` | unset | Local DNS domain appended to container hostnames (e.g. `"test"` makes `my-web-server` resolvable as `my-web-server.test`). When unset, no domain is appended. |
| `domain` | `String?` | unset | Local DNS domain appended to container hostnames (e.g. `"test"` makes `my-web-server` resolvable as `my-web-server.test`). When unset, no domain is appended. See [Networking: Set up DNS-based container names](./networking.md#set-up-dns-based-container-names) for the full walkthrough. |

## `[kernel]`

Expand Down
88 changes: 88 additions & 0 deletions docs/host-integration.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,88 @@
# Host integration

Bridge your container and your Mac: forward your SSH agent in, or reach a host
service from inside a container.

## Mount your host SSH authentication socket in your container

Use the `--ssh` option to mount the macOS SSH authentication socket into your container, so that you can clone private git repositories and perform other tasks requiring passwordless SSH authentication.

When you use `--ssh`, it performs the equivalent of the options `--volume "${SSH_AUTH_SOCK}:/var/host-services/ssh-auth.sock" --env SSH_AUTH_SOCK=/var/host-services/ssh-auth.sock"`. The added benefit of `--ssh` is that when you stop your container, log out, log back in, and restart your container, the system automatically updates the target path for the socket mount to the new value of `SSH_AUTH_SOCK`, so that socket forwarding continues to function.

```console
% container run -it --rm --ssh alpine:latest sh
/ # env
SHLVL=1
HOME=/root
TERM=xterm
PATH=/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin
SSH_AUTH_SOCK=/var/host-services/ssh-auth.sock
PWD=/
/ # apk add openssh-client
(1/6) Installing openssh-keygen (10.0_p1-r7)
(2/6) Installing ncurses-terminfo-base (6.5_p20250503-r0)
(3/6) Installing libncursesw (6.5_p20250503-r0)
(4/6) Installing libedit (20250104.3.1-r1)
(5/6) Installing openssh-client-common (10.0_p1-r7)
(6/6) Installing openssh-client-default (10.0_p1-r7)
Executing busybox-1.37.0-r18.trigger
OK: 12 MiB in 22 packages
/ # ssh-add -l
...auth key output...
/ # apk add git
(1/12) Installing brotli-libs (1.1.0-r2)
(2/12) Installing c-ares (1.34.5-r0)
(3/12) Installing libunistring (1.3-r0)
(4/12) Installing libidn2 (2.3.7-r0)
(5/12) Installing nghttp2-libs (1.65.0-r0)
(6/12) Installing libpsl (0.21.5-r3)
(7/12) Installing zstd-libs (1.5.7-r0)
(8/12) Installing libcurl (8.14.1-r1)
(9/12) Installing libexpat (2.7.1-r0)
(10/12) Installing pcre2 (10.43-r1)
(11/12) Installing git (2.49.1-r0)
(12/12) Installing git-init-template (2.49.1-r0)
Executing busybox-1.37.0-r18.trigger
OK: 24 MiB in 34 packages
/ # git clone git@github.com:some-org/some-private-repo.git
Cloning into 'some-private-repo'...
...
```

## Access a host service from a container

> [!IMPORTANT]
> Due to macOS security constraints around packet filter rules, this feature has limited functionality:
> - Creating a localhost domain disables Private Relay.
> - The local domain packet filter rule is removed on a restart.

Create a DNS domain with `--localhost <ipv4-address>` to make a domain used by a container to access a host service. Any IPv4 address can be used as `<ipv4-address>`, which will be assigned to the domain name in container.

Choose an IP address that is least likely to conflict with any networks or reserved IP addresses in your environment. Reasonably safe address ranges include:

- The documentation ranges 192.0.2.0/24, 198.51.100.0/24, and 203.0.113.0/24.
- The 172.16.0.0/12 private range.

To connect a host HTTP server from a container, run:

```bash
mkdir -p /tmp/test; cd /tmp/test; echo "hello" > index.html
python3 -m http.server 8000 --bind 127.0.0.1
```

Create a domain for host connection:

```bash
sudo container system dns create host.container.internal --localhost 203.0.113.113
```

Test access to the host HTTP server from a container:

```console
% container run -it --rm alpine/curl curl http://host.container.internal:8000
hello
```

This uses the same underlying DNS mechanism described in [Networking: Set up DNS-based
container names](./networking.md#set-up-dns-based-container-names), just with
`--localhost` pointing the domain at a host address instead of a container.
Loading