chore: import upstream snapshot with attribution
container project - merge build / Invoke build (push) Successful in 1s

This commit is contained in:
wehub-resource-sync
2026-07-13 12:06:18 +08:00
commit e84fb4a79e
474 changed files with 68321 additions and 0 deletions
File diff suppressed because one or more lines are too long

After

Width:  |  Height:  |  Size: 85 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 474 KiB

+140
View File
@@ -0,0 +1,140 @@
# How to file effective bug reports
This guide helps you collect the essential information needed to file effective bug reports. Providing complete and accurate information helps maintainers reproduce and fix issues faster.
💡 **Example of a good bug report**: [Issue #1094](https://github.com/apple/container/issues/1094) demonstrates many of the best practices outlined in this guide.
## Steps to reproduce
Clear reproduction steps are essential for maintainers to understand and fix the issue.
### What to include
1. **Starting state**: What was your setup before the issue?
- Fresh installation or existing project?
- Any specific configuration files?
- Previous commands that led to this state?
- Has your machine recently been restarted?
2. **Exact commands**: Copy-paste the exact commands you ran
- Include all flags and arguments
- Use code blocks for clarity
3. **Reproducibility**: Does it happen every time or intermittently?
- Always reproducible
- Happens sometimes (describe conditions)
- Only happened once
### Example
```
1. Create new container: `container create --name test-app ubuntu:latest`
2. Start the container: `container start test-app`
3. Container fails during bootstrap with error:
"failed to bootstrap container test-app"
4. Container exits with code 1
```
## Problem description
Provide a comprehensive description of your problem. Include what currently happens (the bug), what you expect should happen instead, and any relevant log output.
### What to include
#### Current behavior
- Exact error messages (copy-paste, don't paraphrase)
- Exit codes or status indicators
- Performance issues (slowness, hangs, crashes)
- Unexpected outputs or results
#### Expected behavior
- The correct output or result you anticipated
- Reference to documentation if available
- How it works in previous versions (if applicable)
- Logical expectations based on the command or action
#### Relevant logs
Include any log output that helps illustrate the problem:
- Error messages or stack traces
- Warning messages related to your issue
- Output from failed commands
- Use verbose/debug flags to capture detailed information (see [Log Information](#log-information) section below for how to gather logs)
## Environment information
### Operating system details
Run this command in Terminal to get your macOS version:
```bash
sw_vers
```
Example output:
```
ProductName: macOS
ProductVersion: 26.0
BuildVersion: 12A345
```
### Xcode version
Get your Xcode version with:
```bash
xcodebuild -version
```
Example output:
```
Xcode 15.0
Build version 15A240d
```
### Container CLI version
Check your Container CLI version:
```bash
container --version
```
Example output:
```
container CLI version 0.10.0-27-g9fd15f0 (build: debug, commit: 9fd15f0)
```
## Log information
### Finding relevant logs
When reporting issues, include logs that show:
- Error messages or stack traces
- Warning messages related to your issue
- Output from failed commands
### Getting container logs
For Container CLI issues, run commands with verbose output:
```bash
container --debug <command>
```
You can also use the `container logs` command to get logs from running containers. See the [container logs](command-reference.md#container-logs) documentation for full details.
```bash
container logs <container-id>
```
### System logs
For system-level container issues, use the built-in system logs command. See the [container system logs](command-reference.md#container-system-logs) documentation for full details.
```bash
container system logs
```
## Common information gaps
### Missing context
- What were you trying to accomplish?
- What changed recently in your setup?
- Does the issue occur in a fresh installation from main?
### Incomplete error information
- Full error messages (not just the last line)
- Stack traces where relevant
- Related warning messages
### Environment variations
- Does it work with a new instance of the container?
- Does it work with a fresh install of the Container package?
- Have your network settings changed?
- Have your Xcode or macOS versions changed?
File diff suppressed because it is too large Load Diff
+149
View File
@@ -0,0 +1,149 @@
# Container machine
Container machine provides a highly integrated Linux environment that works seamlessly on your Mac. Container machines are fast, lightweight and persistent. They are based on standard OCI images that can be built and shared. Host integrations such as automatic user and home directory sharing provide quick and easy access to your Linux environment no matter where you are in a terminal.
## Why container machines
Containers are typically modeled after an application. A container machine is modeled after a Linux environment. It runs the image's init system allowing you to register long running services or test your application under a process supervisor.
A container machine automatically maps your username and home directory into the Linux environment. Your repositories and dotfiles are available on both platforms. Use editors and tools directly on macOS simultaneously building and running your application inside of the Linux environment.
- **Edit on the Mac, build inside.** Your repo lives in `$HOME` on macOS and is mounted at `/Users/<username>` inside the container machine. Use your macOS editor or IDE; compile and run inside your container machine.
- **Use macOS-native tooling against Linux artifacts.** Profilers, screenshot tools, browsers, and GUI debuggers on your Mac all see the same files the container machine sees — there is no copy step between "I built it" and "I am inspecting it".
- **Real Linux services for testing.** Run a database or whatever your stack needs as a system service — `systemctl start postgresql` works on images with `systemd` installed.
- **One environment per target distro.** Create as many container machines as you have target distros — `alpine`, `ubuntu`, `debian`. Each has the same `$HOME` and the same dotfiles from your Mac. Quickly test your application in various distributions.
## Quickstart
```bash
container machine create alpine:latest --name dev
container machine run -n dev whoami # your host username, not root
container machine run -n dev pwd # /home/<you> — your Mac home dir, mounted in
container machine run -n dev # interactive shell; cd into your repos in $HOME
```
`container machine run` is how you get a shell or run a single command. If the container machine is stopped, `run` boots it first.
## Working in a container machine
### Open a shell, or run a single command
With no command, `container machine run` opens an interactive shell as a user that matches your host account:
```bash
container machine run -n dev
```
Pass a command to run it once and exit:
```bash
container machine run -n dev uname -a
container machine run -n dev -- cat /proc/cpuinfo
```
### Set a default
Pick a default container machine so you can drop the `-n` flag:
```bash
container machine set-default dev
container machine run # operates on dev
```
### List, inspect, stop, delete
```bash
container machine ls # list all container machines
container machine inspect dev # JSON detail for one
container machine stop dev # stop the container machine
container machine rm dev # delete, including its persistent storage
```
`container machine` has the alias `m`, so `m ls`, `m run`, etc. all work.
### Resize CPUs, memory, or change the home-mount
`container machine set` updates configuration on disk. Changes take effect after the next stop and start:
```bash
container machine set -n dev cpus=4 memory=8G
container machine stop dev
container machine run -n dev -- nproc
```
Memory defaults to half of host memory. The home-mount can be `rw` (default), `ro`, or `none`.
### Nested virtualization and custom kernels
A container machine supports nested virtualization. The requirements for this to work are:
1. Apple Silicon **M3 or later** with **macOS 15 or later** is required.
2. A Linux kernel with CONFIG_KVM=y enabled. The default kernel does not support this.
```bash
container machine create \
--virtualization \
--kernel /path/to/vmlinux-kvm \
--name kvm-dev \
alpine:latest
# Verify /dev/kvm is exposed:
container machine run -n kvm-dev -- ls -l /dev/kvm
```
Options can be toggled on an existing container machine.
```bash
container machine set -n dev virtualization=true kernel=/path/to/vmlinux-kvm
container machine stop dev
container machine run -n dev -- ls -l /dev/kvm
# reset to the default kernel
container machine set -n dev kernel=
```
## Bring your own container machine image
Any Linux image that includes `/sbin/init` works as a container machine. For example, this Dockerfile builds an Ubuntu 24.04 container machine image with `systemd` and common command-line tools:
```dockerfile
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
```
Build it and create a container machine from it:
```bash
container build -t local/ubuntu-machine:latest .
container machine create local/ubuntu-machine:latest --name ubuntu
```
By default, `container` runs a built-in setup script on first boot to provision the user described above. To use your own setup instead, add an executable script at `/etc/machine/create-user.sh` to the image. It runs once, as root, on first boot, with these variables set:
- `CONTAINER_GID`
- `CONTAINER_HOME`
- `CONTAINER_MACHINE_ID`
- `CONTAINER_UID`
- `CONTAINER_USER`
+114
View File
@@ -0,0 +1,114 @@
# `config.toml` reference
> [!IMPORTANT]
> This file contains documentation for the CURRENT BRANCH. To find documentation for official releases, find the target release on the [Release Page](https://github.com/apple/container/releases) and click the tag corresponding to your release version.
>
> Example: [release 0.4.1 tag](https://github.com/apple/container/tree/0.4.1)
For a guided walk-through on setting default values, see [Container system config tutorial](./tutorials/container-system-config-tutorial.md).
Source of truth: [`Sources/ContainerPersistence/ContainerSystemConfig.swift`](../Sources/ContainerPersistence/ContainerSystemConfig.swift).
## Top-level schema
```toml
[build] # builder VM resources and image
[container] # default per-container resources
[dns] # default DNS domain for DNS resolution on host
[kernel] # guest kernel binary path and download URL
[network] # default subnets for new networks
[registry] # default registry domain
[vminit] # default vminitd image to use
[plugin.<id>] # zero or more plugin-scoped sections
```
All top-level sections are optional. Omitted sections fall back to their defaults wholesale.
## `[build]`
Resources and image used for the builder VM that runs `container build`.
| Key | Type | Default | Description |
|-----------|-------------|------------------------------------------------------|-----------------------------------------------------------------------------|
| `rosetta` | `Bool` | `true` | Whether the builder VM uses Rosetta translation for non-native architectures. |
| `cpus` | `Int` | `2` | CPU count for the builder VM. |
| `memory` | [MemorySize](#memorysize-format) | `"2048mb"` | RAM allocation for the builder VM. |
| `image` | `String` | `ghcr.io/apple/container-builder-shim/builder:<tag>` | Reference for the builder image. The tag segment is taken from the project's bundled `container-builder-shim` version. |
## `[container]`
Defaults applied when `container run` / `container create` is invoked without `--cpus` or `--memory`.
| Key | Type | Default | Description |
|----------|------------|---------|----------------------------------------------------------------------------|
| `cpus` | `Int` | `4` | Default CPU count per container. |
| `memory` | [MemorySize](#memorysize-format) | `"1g"` | Default RAM per container. |
## `[dns]`
| 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. |
## `[kernel]`
Guest kernel used when launching container VMs. Defaults change per release as kernels are bumped — check the [source](../Sources/ContainerPersistence/ContainerSystemConfig.swift) for current values.
| Key | Type | Default | Description |
|--------------|----------|--------------------------------------------------------------------------------------------------------|------------------------------------------------------------------------------|
| `binaryPath` | `String` | `"opt/kata/share/kata-containers/vmlinux-6.18.15-186"` | Path **inside** the downloaded kernel archive that points to the kernel binary. |
| `url` | `URL` | `"https://github.com/kata-containers/kata-containers/releases/download/3.28.0/kata-static-3.28.0-arm64.tar.zst"` | Archive to download when no kernel is installed. Encoded and decoded as a plain string in TOML. |
## `[network]`
Default subnets used when creating networks without explicit `--subnet` / `--subnet-v6` flags.
| Key | Type | Default | Description |
|------------|------------|---------|---------------------------------------------------------------------------------------------------|
| `subnet` | [CIDRv4?](#cidrv4--cidrv6) | unset | IPv4 CIDR (e.g. `"192.168.100.0/24"`). When unset, the system auto-allocates a non-overlapping subnet. |
| `subnetv6` | [CIDRv6?](#cidrv4--cidrv6) | unset | IPv6 CIDR (e.g. `"fd00:abcd::/64"`). When unset, the system auto-allocates. |
## `[registry]`
| Key | Type | Default | Description |
|----------|----------|--------------|--------------------------------------------------------------------------------------------------------------|
| `domain` | `String` | `"docker.io"` | Registry assumed when an image reference omits the registry host (e.g. `alpine``docker.io/library/alpine`). |
## `[vminit]`
| Key | Type | Default | Description |
|---------|----------|--------------------------------------------------------|----------------------------------------------------------------------------------------------------------|
| `image` | `String` | `ghcr.io/apple/containerization/vminit:<tag>` | Reference for the `vminitd` image used to boot container VMs. The tag segment is taken from the project's bundled `containerization` version. |
## `[plugin.<id>]`
Plugins can ship their own configuration schemas under `[plugin.<id>]`, where `<id>` is the plugin's identifier. Each plugin defines and reads its own section — values under one plugin's section cannot leak into another's. Consult the documentation for the specific plugin you want to configure.
| Key | Type | Notes |
|--------------------|----------|---------------------------------------------------------------------------------------------|
| `<plugin-defined>` | varies | Schema is defined by the plugin. TODO: Add tutorial on setting plugin specific values. |
## Type formats
### MemorySize format
Quoted string with a numeric prefix and a binary unit suffix. Parsing is case-insensitive; the suffix may be one of:
| Suffix family | Unit | Example values |
|---------------------|--------------------------|------------------------|
| `b` | bytes | `"1024b"` |
| `k`, `kb`, `kib` | kibibytes (1024 bytes) | `"512k"`, `"512kb"` |
| `m`, `mb`, `mib` | mebibytes (1024 KiB) | `"2048mb"` |
| `g`, `gb`, `gib` | gibibytes (1024 MiB) | `"4g"`, `"4gb"` |
| `t`, `tb`, `tib` | tebibytes | `"1t"` |
| `p`, `pb`, `pib` | pebibytes | `"1p"` |
All units are **binary** (powers of 1024), even when written with `kb`/`mb`/`gb`. The encoded form uses lowercase suffix `b`/`kb`/`mb`/`gb`/`tb`/`pb`, e.g. a value parsed from `"2g"` is emitted as `"2gb"`.
A bare integer (e.g. `"2048"`) parses as bytes.
Source: [`Sources/ContainerPersistence/Measurement+Parse.swift`](../Sources/ContainerPersistence/Measurement+Parse.swift).
### `CIDRv4` / `CIDRv6`
Quoted string. IPv4 example: `"192.168.100.0/24"`. IPv6 example: `"fd00:abcd::/64"`. The loader rejects invalid CIDR strings at decode time.
+771
View File
@@ -0,0 +1,771 @@
# How-to
> [!IMPORTANT]
> This file contains documentation for the CURRENT BRANCH. To find documentation for official releases, find the target release on the [Release Page](https://github.com/apple/container/releases) and click the tag corresponding to your release version.
>
> Example: [release 0.4.1 tag](https://github.com/apple/container/tree/0.4.1)
How to use the features of `container`.
## Configure memory and CPUs for your containers
Since the containers created by `container` are lightweight virtual machines, consider the needs of your containerized application when you use `container run`. The `--memory` and `--cpus` options allow you to override the default memory and CPU limits for the virtual machine. The default values are 1 gigabyte of RAM and 4 CPUs. You can use abbreviations for memory units; for example, to run a container for image `big` with 8 CPUs and 32 GiBytes of memory, use:
```bash
container run --rm --cpus 8 --memory 32g big
```
## Configure memory and CPUs for large builds
When you first run `container build`, `container` starts a *builder*, which is a utility container that builds images from your `Dockerfile`s. As with anything you run with `container run`, the builder runs in a lightweight virtual machine, so for resource-intensive builds, you may need to increase the memory and CPU limits for the builder VM.
By default, the builder VM receives 2 GiBytes of RAM and 2 CPUs. You can change these limits by starting the builder container before running `container build`:
```bash
container builder start --cpus 8 --memory 32g
```
If your builder is already running and you need to modify the limits, just stop, delete, and restart the builder:
```bash
container builder stop
container builder delete
container builder start --cpus 8 --memory 32g
```
## Share host files with your container
With the `--volume` option of `container run`, you can share data between the host system and one or more containers, and you can persist data across multiple container runs. The volume option allows you to mount a folder on your host to a filesystem path in the container.
This example mounts a folder named `assets` on your Desktop to the directory `/content/assets` in a container:
<pre>
% ls -l ~/Desktop/assets
total 8
-rw-r--r--@ 1 fido staff 2410 May 13 18:36 link.svg
% container run --volume ${HOME}/Desktop/assets:/content/assets docker.io/python:alpine ls -l /content/assets
total 4
-rw-r--r-- 1 root root 2410 May 14 01:36 link.svg
%
</pre>
The argument to `--volume` in the example consists of the full pathname for the host folder and the full pathname for the mount point in the container, separated by a colon.
The `--mount` option uses a comma-separated `key=value` syntax to achieve the same result:
<pre>
% container run --mount source=${HOME}/Desktop/assets,target=/content/assets docker.io/python:alpine ls -l /content/assets
total 4
-rw-r--r-- 1 root root 2410 May 14 01:36 link.svg
%
</pre>
## Build and run a multiplatform image
Using the [project from the tutorial example](./tutorials/start-here.md#set-up-a-simple-project), you can create an image to use both on Apple silicon Macs and on x86-64 servers.
When building the image, just add `--arch` options that direct the builder to create an image supporting both the `arm64` and `amd64` architectures:
```bash
container build --arch arm64 --arch amd64 --tag registry.example.com/fido/web-test:latest --file Dockerfile .
```
Try running the command `uname -a` with the `arm64` variant of the image to see the system information that the virtual machine reports:
<pre>
% container run --arch arm64 --rm registry.example.com/fido/web-test:latest uname -a
Linux 7932ce5f-ec10-4fbe-a2dc-f29129a86b64 6.1.68 #1 SMP Mon Mar 31 18:27:51 UTC 2025 aarch64 GNU/Linux
%
</pre>
When you run the command with the `amd64` architecture, the x86-64 version of `uname` runs under Rosetta translation, so that you will see information for an x86-64 system:
<pre>
% container run --arch amd64 --rm registry.example.com/fido/web-test:latest uname -a
Linux c0376e0a-0bfd-4eea-9e9e-9f9a2c327051 6.1.68 #1 SMP Mon Mar 31 18:27:51 UTC 2025 x86_64 GNU/Linux
%
</pre>
The command to push your multiplatform image to a registry is no different than that for a single-platform image:
```bash
container image push registry.example.com/fido/web-test:latest
```
## 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
[
{
"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
[
{
"status": "running",
"networks": [
{
"address": "192.168.64.3/24",
"gateway": "192.168.64.1",
"hostname": "my-web-server.test.",
"network": "default"
}
],
"configuration": {
"mounts": [],
"hostname": "my-web-server",
"id": "my-web-server",
"resources": {
"cpus": 4,
"memoryInBytes": 1073741824,
},
...
</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 == "running" ) | [ .configuration.id, .networks[0].address ]'
[
"my-web-server",
"192.168.64.3/24"
]
[
"buildkit",
"192.168.64.2/24"
]
</pre>
## Forward traffic from `localhost` to your container
Use the `--publish` option to forward TCP or UDP traffic from your loopback IP to the container you run. The option value has the form `[host-ip:]host-port:container-port[/protocol]`, where protocol may be `tcp` or `udp`, case insensitive.
If your container attaches to multiple networks, the ports you publish forward to the IP address of the interface attached to the first network.
To forward requests from port 8080 on the IPv4 loopback IP to a NodeJS webserver on container port 8000, run:
```bash
container run -d --rm -p 127.0.0.1:8080:8000 node:latest npx http-server -a :: -p 8000
```
Test access using `curl`:
```console
% curl http://127.0.0.1:8080
<!doctype html>
<html>
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width">
<title>Index of /</title>
...
<br><address>Node.js v25.2.1/ <a href="https://github.com/http-party/http-server">http-server</a> server running @ 127.0.0.1:8080</address>
</body></html>
```
To forward requests from port 8080 on the IPv6 loopback IP to a NodeJS webserver on container port 8000, run:
```bash
container run -d --rm -p '[::1]:8080:8000' node:latest npx http-server -a :: -p 8000
```
Test access using `curl`:
```console
% curl -6 'http://[::1]:8080'
<!doctype html>
<html>
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width">
<title>Index of /</title>
...
<br><address>Node.js v25.2.1/ <a href="https://github.com/http-party/http-server">http-server</a> server running @ [::1]:8080</address>
</body></html>
```
## 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
```
## Set a custom MAC address for your container
Use the `mac` option to specify a custom MAC address for your container's network interface. This is useful for:
- Network testing scenarios requiring predictable MAC addresses
- Consistent network configuration across container restarts
The MAC address must be in the format `XX:XX:XX:XX:XX:XX` (with colons or hyphens as separators). Set the two least significant bits of the first octet to `10` (locally signed, unicast address).
```bash
container run --network default,mac=02:42:ac:11:00:02 ubuntu:latest
```
To verify the MAC address is set correctly, read the interface MAC directly from sysfs inside the container:
```console
% container run --rm --network default,mac=02:42:ac:11:00:02 ubuntu:latest cat /sys/class/net/eth0/address
02:42:ac:11:00:02
```
If you don't specify a MAC address, `container` will generate one for you. The generated address has a first nibble set to hexadecimal `f` (`fX:XX:XX:XX:XX:XX`) in case you want to minimize the very small chance of conflict between your MAC address and generated addresses.
## 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}:/run/host-services/ssh-auth.sock" --env SSH_AUTH_SOCK=/run/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=/run/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'...
...
```
## Create and use a separate isolated network
> [!NOTE]
> This feature is available on macOS 26 and later.
Running `container system start` creates a vmnet network named `default` to which your containers will attach unless you specify otherwise.
You can create a separate isolated network using `container network create`.
This command creates a network named `foo`:
```bash
container network create foo
```
You can also specify custom IPv4 and IPv6 subnets when creating a network:
```bash
container network create foo --subnet 192.168.100.0/24 --subnet-v6 fd00:1234::/64
```
The `foo` network, the default network, and any other networks you create are isolated from one another. A container on one network has no connectivity to containers on other networks.
Run `container network list` to see the networks that exist:
```console
% container network list
NETWORK STATE SUBNET
default running 192.168.64.0/24
foo running 192.168.65.0/24
%
```
Run a container that is attached to that network using the `--network` flag:
```console
container run -d --name my-web-server --network foo --rm web-test
```
Use `container ls` to see that the container is on the `foo` subnet:
```console
% container ls
ID IMAGE OS ARCH STATE IP
my-web-server web-test:latest linux arm64 running 192.168.65.2
```
You can delete networks that you create once no containers are attached:
```bash
container stop my-web-server
container network delete foo
```
Networks support both IPv4 and IPv6. When creating a network without explicit subnet options, the system uses default values if configured via system properties (see below), or automatically allocates subnets. The system validates that custom subnets don't overlap with existing networks.
## Configure default network subnets
You can customize the default IPv4 and IPv6 subnets used for new networks by editing your runtime configuration file at `~/.config/container/config.toml`:
```toml
[network]
subnet = "192.168.100.1/24"
subnetv6 = "fd00:abcd::/64"
```
These settings apply to networks created without explicit `--subnet` or `--subnet-v6` options.
## View container logs
The `container logs` command displays the output from your containerized application:
<pre>
% container run -d --name my-web-server --rm registry.example.com/fido/web-test:latest
my-web-server
% curl http://my-web-server.test
&lt;!DOCTYPE html>&lt;html>&lt;head>&lt;title>Hello&lt;/title>&lt;/head>&lt;body>&lt;h1>Hello, world!&lt;/h1>&lt;/body>&lt;/html>
% container logs my-web-server
192.168.64.1 - - [15/May/2025 03:00:03] "GET / HTTP/1.1" 200 -
%
</pre>
Use the `--boot` option to see the logs for the virtual machine boot and init process:
<pre>
% container logs --boot my-web-server
[ 0.098284] cacheinfo: Unable to detect cache hierarchy for CPU 0
[ 0.098466] random: crng init done
[ 0.099657] brd: module loaded
[ 0.100707] loop: module loaded
[ 0.100838] virtio_blk virtio2: 1/0/0 default/read/poll queues
[ 0.101051] virtio_blk virtio2: [vda] 1073741824 512-byte logical blocks (550 GB/512 GiB)
...
[ 0.127467] EXT4-fs (vda): mounted filesystem without journal. Quota mode: disabled.
[ 0.127525] VFS: Mounted root (ext4 filesystem) readonly on device 254:0.
[ 0.127635] devtmpfs: mounted
[ 0.127773] Freeing unused kernel memory: 2816K
[ 0.143252] Run /sbin/vminitd as init process
2025-05-15T02:24:08+0000 info vminitd : [vminitd] vminitd booting...
2025-05-15T02:24:08+0000 info vminitd : [vminitd] serve vminitd api
2025-05-15T02:24:08+0000 debug vminitd : [vminitd] starting process supervisor
2025-05-15T02:24:08+0000 debug vminitd : port=1024 [vminitd] booting grpc server on vsock
...
2025-05-15T02:24:08+0000 debug vminitd : exits=[362: 0] pid=363 [vminitd] checking for exit of managed process
2025-05-15T02:24:08+0000 debug vminitd : [vminitd] waiting on process my-web-server
[ 1.122742] IPv6: ADDRCONF(NETDEV_CHANGE): eth0: link becomes ready
2025-05-15T02:24:39+0000 debug vminitd : sec=1747275879 usec=478412 [vminitd] setTime
%
</pre>
## Monitor container resource usage
The `container stats` command displays real-time resource usage statistics for your running containers, similar to the `top` command for processes. This is useful for:
- Monitoring CPU and memory consumption
- Tracking network and disk I/O
- Identifying resource-intensive containers
- Verifying container resource limits are appropriate
By default, `container stats` shows live statistics for all running containers in an interactive display:
```console
% container stats
Container ID Cpu % Memory Usage Net Rx/Tx Block I/O Pids
my-web-server 2.45% 45.23 MiB / 1.00 GiB 1.23 MiB / 856.00 KiB 4.50 MiB / 2.10 MiB 3
db 125.12% 512.50 MiB / 2.00 GiB 5.67 MiB / 3.21 MiB 125.00 MiB / 89.00 MiB 12
```
To monitor specific containers, provide their names or IDs:
```console
% container stats my-web-server db
```
For a single snapshot (non-interactive), use the `--no-stream` flag:
```console
% container stats --no-stream my-web-server
Container ID Cpu % Memory Usage Net Rx/Tx Block I/O Pids
my-web-server 30.45% 45.23 MiB / 1.00 GiB 1.23 MiB / 856.00 KiB 4.50 MiB / 2.10 MiB 3
```
You can also output statistics in JSON format for scripting:
```console
% container stats --format json --no-stream my-web-server | jq
[
{
"id": "my-web-server",
"memoryUsageBytes": 47431680,
"memoryLimitBytes": 1073741824,
"cpuUsageUsec": 1234567,
"networkRxBytes": 1289011,
"networkTxBytes": 876544,
"blockReadBytes": 4718592,
"blockWriteBytes": 2202009,
"numProcesses": 3
}
]
```
**Understanding the metrics:**
- **Cpu %**: Percentage of CPU usage. ~100% = one fully utilized core. A multi-core container can show > 100%.
- **Memory Usage**: Current memory usage vs. the container's memory limit.
- **Net Rx/Tx**: Network bytes received and transmitted.
- **Block I/O**: Disk bytes read and written.
- **Pids**: Number of processes running in the container.
## Control Linux capabilities
By default, containers start with a restricted set of Linux capabilities:
`CAP_AUDIT_WRITE`, `CAP_CHOWN`, `CAP_DAC_OVERRIDE`, `CAP_FOWNER`, `CAP_FSETID`, `CAP_KILL`, `CAP_MKNOD`, `CAP_NET_BIND_SERVICE`, `CAP_NET_RAW`, `CAP_SETFCAP`, `CAP_SETGID`, `CAP_SETPCAP`, `CAP_SETUID`, `CAP_SYS_CHROOT`
You can customize the capability set using `--cap-add` and `--cap-drop` with `container run` or `container create`.
Capability names can be specified with or without the `CAP_` prefix, and are case-insensitive:
These are equivalent:
```bash
container run --cap-add CAP_NET_ADMIN alpine ip link set lo down
container run --cap-add NET_ADMIN alpine ip link set lo down
container run --cap-add net_admin alpine ip link set lo down
```
To grant all capabilities:
```bash
container run --cap-add ALL alpine sh -c "ip link set lo down && echo ok"
```
To drop all capabilities and selectively re-add only what you need:
```bash
container run --cap-drop ALL --cap-add SETUID --cap-add SETGID alpine id
```
Adds are processed after drops, so `--cap-drop ALL --cap-add ALL` results in all capabilities being granted.
To grant all capabilities except specific ones:
```bash
container run --cap-add ALL --cap-drop NET_ADMIN alpine sh
```
To drop a single capability from the default set:
```console
% container run --cap-drop CHOWN alpine chown 100 /tmp
chown: /tmp: Operation not permitted
```
## Expose virtualization capabilities to a container
> [!NOTE]
> This feature requires a M3 or newer Apple silicon machine and a Linux kernel that supports virtualization. For a kernel configuration that has all of the right features enabled, see https://github.com/apple/containerization/blob/0.5.0/kernel/config-arm64#L602.
You can enable virtualization capabilities in containers by using the `--virtualization` option of `container run` and `container create`.
If your machine does not have support for nested virtualization, you will see the following:
```console
container run --name nested-virtualization --virtualization --kernel /path/to/a/kernel/with/virtualization/support --rm ubuntu:latest sh -c "dmesg | grep kvm"
Error: unsupported: "nested virtualization is not supported on the platform"
```
When nested virtualization is enabled successfully, `dmesg` will show output like the following:
```console
container run --name nested-virtualization --virtualization --kernel /path/to/a/kernel/with/virtualization/support --rm ubuntu:latest sh -c "dmesg | grep kvm"
[ 0.017245] kvm [1]: IPA Size Limit: 40 bits
[ 0.017499] kvm [1]: GICv3: no GICV resource entry
[ 0.017501] kvm [1]: disabling GICv2 emulation
[ 0.017506] kvm [1]: GIC system register CPU interface enabled
[ 0.017685] kvm [1]: vgic interrupt IRQ9
[ 0.017893] kvm [1]: Hyp mode initialized successfully
```
## Run a container with a provided init process
By default, the command you specify in `container run` runs as PID 1 inside the container. This means it is responsible for reaping zombie processes and handling signals, which many applications are not designed to do. The `--init` flag runs a lightweight init process as PID 1 that automatically forwards signals and reaps orphaned child processes.
```bash
container run --init ubuntu:latest my-app
```
The init process is also available with `container create`:
```bash
container create --init --name my-container ubuntu:latest my-app
container start my-container
```
## Use a custom init image
The `--init-image` flag allows you to specify a custom init filesystem image for the lightweight VM that runs your container. This enables:
- Custom boot-time logic before the OCI container starts
- Running additional processes and daemons (e.g., eBPF network filters, logging agents) inside the VM
- Debugging or instrumenting the init process
### Create a custom init image
A custom init image wraps the default `vminitd` binary, allowing you to run custom logic before handing off to the standard init process.
**1. Create a wrapper binary (example in Go for easy cross-compilation):**
```go
// wrapper.go
package main
import (
"os"
"syscall"
)
func main() {
// Write a message to kernel log
kmsg, err := os.OpenFile("/dev/kmsg", os.O_WRONLY, 0)
if err == nil {
kmsg.WriteString("<6>custom-init: === CUSTOM INIT IMAGE RUNNING ===\n")
kmsg.Close()
}
// Execute the real vminitd
err = syscall.Exec("/sbin/vminitd.real", os.Args, os.Environ())
if err != nil {
os.Exit(1)
}
}
```
**2. Build the wrapper for Linux arm64:**
```bash
CGO_ENABLED=0 GOOS=linux GOARCH=arm64 go build -o wrapper wrapper.go
```
**3. Create a Containerfile:**
Use the `vminit` image tag corresponding to the `scVersion` value in the project `Package.swift` file.
Or, use `vminit:latest` if you have a local `containerization` project in [edit mode](../BUILDING.md#develop-using-a-local-copy-of-containerization).
```dockerfile
FROM ghcr.io/apple/containerization/vminit:0.34.0 AS base
FROM ghcr.io/apple/containerization/vminit:0.34.0
COPY --from=base /sbin/vminitd /sbin/vminitd.real
COPY wrapper /sbin/vminitd
```
**4. Build the custom init image:**
```bash
container build -t local/custom-init:latest .
```
### Run a container with a custom init image
```bash
container run --name my-container --init-image local/custom-init:latest alpine:latest echo "hello"
```
### Verify the custom init is running
Check the VM boot logs to confirm your custom init code executed:
```console
% container logs --boot my-container | grep custom-init
[ 0.129230] custom-init: === CUSTOM INIT IMAGE RUNNING ===
```
## Use container machines
Container machines are persistent Linux environments built from OCI images — your home directory is mounted in, the user account matches your host account, and the filesystem survives stop and start. See [container-machine.md](./container-machine.md) for the full guide.
## Configure system properties
The `container system property` subcommand manages the configuration settings for the `container` CLI and services. You can customize various aspects of container behavior, including build settings, default images, and network configuration.
Use `container system property list` to show all properties that have set defaults:
```console
% bin/container system property ls
[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"
```
### Example: Disable Rosetta for builds
If you want to prevent the use of Rosetta translation during container builds on Apple Silicon Macs, set the following in `~/.config/container/config.toml`:
```toml
[build]
rosetta = false
```
This is useful when you want to ensure builds only produce native arm64 images and avoid any x86_64 emulation.
## View system logs
The `container system logs` command allows you to look at the log messages that `container` writes:
<pre>
% container system logs | tail -8
2025-06-02 16:46:11.560780-0700 0xf6dc5 Info 0x0 61684 0 container-apiserver: [com.apple.container:APIServer] Registering plugin [id=com.apple.container.container-runtime-linux.my-web-server]
2025-06-02 16:46:11.699095-0700 0xf6ea8 Info 0x0 61733 0 container-runtime-linux: [com.apple.container:RuntimeLinuxHelper] starting container-runtime-linux [uuid=my-web-server]
2025-06-02 16:46:11.699125-0700 0xf6ea8 Info 0x0 61733 0 container-runtime-linux: [com.apple.container:RuntimeLinuxHelper] configuring XPC server [uuid=my-web-server]
2025-06-02 16:46:11.700908-0700 0xf6ea8 Info 0x0 61733 0 container-runtime-linux: [com.apple.container:RuntimeLinuxHelper] starting XPC server [uuid=my-web-server]
2025-06-02 16:46:11.703028-0700 0xf6ea8 Info 0x0 61733 0 container-runtime-linux: [com.apple.container:RuntimeLinuxHelper] `bootstrap` xpc handler [uuid=my-web-server]
2025-06-02 16:46:11.720836-0700 0xf6dc3 Info 0x0 61689 0 container-network-vmnet: [com.apple.container:NetworkVmnetHelper] allocated attachment [hostname=my-web-server.test.] [address=192.168.64.2/24] [gateway=192.168.64.1] [id=default]
2025-06-02 16:46:12.293193-0700 0xf6eaa Info 0x0 61733 0 container-runtime-linux: [com.apple.container:RuntimeLinuxHelper] `start` xpc handler [uuid=my-web-server]
2025-06-02 16:46:12.368723-0700 0xf6e93 Info 0x0 61684 0 container-apiserver: [com.apple.container:APIServer] Handling container my-web-server Start.
%
</pre>
## Generating and installing completion scripts
### Overview
The `container --generate-completion-script [zsh|bash|fish]` command generates completion scripts for the provided shell. Below is a detailed guide on how to install the completion scripts.
> [!NOTE]
> See the [swift-argument-parser documentation](https://apple.github.io/swift-argument-parser/documentation/argumentparser/installingcompletionscripts/#Installing-Zsh-Completions) for more information about generating and installing shell completion scripts.
### Installing `zsh` completions
If you have [oh-my-zsh](https://ohmyz.sh/) installed, you already have a directory of automatically loaded completion scripts — `.oh-my-zsh/completions`. Copy your new completion script to that directory. If the `completions` directory does not exist, simply make it.
```zsh
mkdir -p ~/.oh-my-zsh/completions
container --generate-completion-script zsh > ~/.oh-my-zsh/completions/_container
source ~/.oh-my-zsh/completions/_container
```
> [!NOTE]
> Your completion script must have the filename `_container`.
Without oh-my-zsh, youll need to add a path for completion scripts to your function path, and turn on completion script autoloading. First, add these lines to your `~/.zshrc` file:
```bash
fpath=(~/.zsh/completion $fpath)
autoload -U compinit
compinit
```
Next, create a directory at `~/.zsh/completion` and copy the completion script to the new directory.
```zsh
mkdir -p ~/.zsh/completion
container --generate-completion-script zsh > ~/.zsh/completion/_container
source ~/.zshrc
```
### Installing `bash` completions
If you have [bash-completion](https://github.com/scop/bash-completion) installed, you can just copy your new completion script to the `bash_completion.d` directory.
> [!NOTE]
> The path to the directory is dependent on how bash-completion was installed. Find the correct path and then copy the completion script there. For example, if you used homebrew to install `bash-completion`:
> ```bash
> container --generate-completion-script bash > /opt/homebrew/etc/bash_completion.d/container
> source /opt/homebrew/etc/bash_completion.d/container
> ```
Without bash-completion, youll need to source the completion script directly. Create and copy it to a directory such as `~/.bash_completions`.
```bash
mkdir -p ~/.bash_completions
container --generate-completion-script bash > ~/.bash_completions/container
source ~/.bash_completions/container
```
Furthermore, you can add the following line to `~/.bash_profile` or `~/.bashrc`, in order for every new bash session to have autocompletion ready.
```bash
source ~/.bash_completions/container
```
### Installing `fish` completions
Copy the completion script to any path listed in the environment variable `$fish_completion_path`.
```bash
container --generate-completion-script fish > ~/.config/fish/completions/container.fish
```
+77
View File
@@ -0,0 +1,77 @@
# Technical Overview
> [!IMPORTANT]
> This file contains documentation for the CURRENT BRANCH. To find documentation for official releases, find the target release on the [Release Page](https://github.com/apple/container/releases) and click the tag corresponding to your release version.
>
> Example: [release 0.4.1 tag](https://github.com/apple/container/tree/0.4.1)
A brief description and technical overview of `container`.
## What are containers?
Containers are a way to package an application and its dependencies into a single unit. At runtime, containers provide isolation from the host machine as well as other colocated containers, allowing applications to run securely and efficiently in a wide variety of environments.
Containerization is an important server-side technology that is used throughout the software lifecycle:
- Backend developers use containers on their personal systems to create predictable execution environments for applications, and to develop and test their applications under conditions that better approximate how they would run in the datacenter.
- Continuous integration and deployment (CI/CD) systems use containerization to perform reproducible builds of applications, package the results as deployable images, and deploy them to the datacenter.
- Datacenters run container orchestration platforms that use the images to run containerized applications in a reliable, highly available compute cluster.
None of this workflow would be practical without ensuring interoperability between different container implementations. The Open Container Initiative (OCI) creates and maintains these standards for container images and runtimes.
## How does `container` run my container?
Many operating systems support containers, but the most commonly encountered containers are those that run on the Linux operating system. With macOS, the typical way to run Linux containers is to launch a Linux virtual machine (VM) that hosts all of your containers.
`container` runs containers differently. Using the open source [Containerization](https://github.com/apple/containerization) package, it runs a lightweight VM for each container that you create. This approach has the following properties:
- Security: Each container has the isolation properties of a full VM, using a minimal set of core utilities and dynamic libraries to reduce resource utilization and attack surface.
- Privacy: When sharing host data using `container`, you mount only necessary data into each VM. With a shared VM, you need to mount all data that you may ever want to use into the VM, so that it can be mounted selectively into containers.
- Performance: Containers created using `container` require less memory than full VMs, with boot times that are comparable to containers running in a shared VM.
Since `container` consumes and produces standard OCI images, you can easily build with and run images produced by other container applications, and the images that you build will run everywhere.
`container` and the underlying Containerization package integrate with many of the key technologies and frameworks of macOS:
- The Virtualization framework for managing Linux virtual machines and their attached devices.
- The vmnet framework for managing the virtual network to which the containers attach.
- XPC for interprocess communication.
- Launchd for service management.
- Keychain services for access to registry credentials.
- The unified logging system for application logging.
You use the `container` command line interface (CLI) to start and manage your containers, build container images, and transfer images from and to OCI container registries. The CLI uses a client library that communicates with `container-apiserver` and its helpers.
The `container-apiserver` is a launch agent that launches when you run the `container system start` command, and terminates when you run `container system stop`. It provides the client APIs for managing container and network resources.
When `container-apiserver` starts, it launches an XPC helper `container-core-images` that exposes an API for image management and manages the local content store, and another XPC helper `container-network-vmnet` for the virtual network. For each container that you create, `container-apiserver` launches a container runtime helper `container-runtime-linux` that exposes the management API for that specific container.
![diagram showing `container` functional organization](/docs/assets/functional-model-light.svg)
## What limitations does `container` have today?
With the initial release of `container`, you get basic facilities for building and running containers, but many common containerization features remain to be implemented. Consider [contributing](../CONTRIBUTING.md) new features and bug fixes to `container` and the Containerization projects!
### Releasing container memory to macOS
The macOS Virtualization framework implements only partial support for memory ballooning, which is a technology that allows virtual machines to dynamically use and relinquish host memory. When you create a container, the underlying virtual machine only uses the amount of memory that the containerized application needs. For example, you might start a container using the option `--memory 16g`, but see that the application is only using 2 GiBytes of RAM in the macOS Activity Monitor.
Currently, memory pages freed to the Linux operating system by processes running in the container's VM are not relinquished to the host. If you run many memory-intensive containers, you may need to occasionally restart them to reduce memory utilization.
### macOS 15 limitations
`container` relies on the new features and enhancements present in macOS 26. You can run `container` on macOS 15, but you will need to be aware of some user experience and functional limitations. There is no plan to address issues found with macOS 15 that cannot be reproduced on macOS 26.
#### Network isolation
The vmnet framework in macOS 15 can only provide networks where the attached containers are isolated from one another. Container-to-container communication over the virtual network is not possible.
#### Multiple networks
In macOS 15, all containers attach to the default vmnet network. The `container network` commands are not available on macOS 15, and using the `--network` option for `container run` or `container create` will result in an error.
#### Container IP addresses
In macOS 15, limitations in the vmnet framework mean that the container network can only be created when the first container starts. Since the network XPC helper provides IP addresses to containers, and the helper has to start before the first container, it is possible for the network helper and vmnet to disagree on the subnet address, resulting in containers that are completely cut off from the network.
Normally, vmnet creates the container network using the CIDR address 192.168.64.1/24, and on macOS 15, `container` defaults to using this CIDR address in the network helper. If your containers have no network access on macOS 15, see [All networking fails on macOS 15](troubleshooting.md#all-networking-fails-on-macos-15) for diagnosis and remediation steps.
@@ -0,0 +1,94 @@
# Customize `container` default configuration values
> [!IMPORTANT]
> This file contains documentation for the CURRENT BRANCH. To find documentation for official releases, find the target release on the [Release Page](https://github.com/apple/container/releases) and click the tag corresponding to your release version.
>
> Example: [release 0.4.1 tag](https://github.com/apple/container/tree/0.4.1)
Take a guided tour of setting configurations for `container` CLI commands and services.
## Configuration sources
The `container` service loads values from these TOML files at startup, with first-match-wins precedence:
1. Your user file at `~/.config/container/config.toml`.
2. An optional file shipped with the `container` package install at `<installRoot>/etc/container/config.toml`.
Any key absent from both files falls back to a hardcoded default. For the full schema and defaults, see the [`config.toml` reference](../container-system-config.md).
## Create a custom user TOML configuration file
The `container` service reads your file once at startup, so restart the service whenever you want changes to take effect.
### Open or create your config file
Your editable config lives at `~/.config/container/config.toml`. Create it if it does not exist:
```bash
mkdir -p ~/.config/container
touch ~/.config/container/config.toml
```
### Set the values you want to customize
Open the file in the editor of your choice and add only the sections and keys you want to change.
For this tutorial, increase the default CPU and memory limits used for each new container and set a DNS domain for resolving container IP addresses from the host.
```toml
[container]
cpus = 8
memory = "4g"
[dns]
domain = "test"
```
Each top-level table maps directly to a section of [ContainerSystemConfig](../container-system-config.md).
### Restart the `container` service
To make your edits take effect, stop and start the system:
```bash
container system stop
container system start
```
### Verify the values are loaded
Use `container system property list` (alias `ls`) to print the merged configuration that the `container` service is using.
```console
% container system property list
[build]
cpus = 2
memory = "2048mb"
rosetta = true
image = "ghcr.io/apple/container-builder-shim/builder:0.11.0"
[container]
cpus = 8
memory = "4gb"
[dns]
domain = "test"
[kernel]
binaryPath = "opt/kata/share/kata-containers/vmlinux-6.18.15-186"
url = "https://github.com/kata-containers/kata-containers/releases/download/3.28.0/kata-static-3.28.0-arm64.tar.zst"
[network]
[registry]
domain = "docker.io"
[vminit]
image = "ghcr.io/apple/containerization/vminit:0.34.0"
```
For machine-readable output, pass `--format json`:
```bash
container system property list --format json
```
+352
View File
@@ -0,0 +1,352 @@
# Tutorial
> [!IMPORTANT]
> This file contains documentation for the CURRENT BRANCH. To find documentation for official releases, find the target release on the [Release Page](https://github.com/apple/container/releases) and click the tag corresponding to your release version.
>
> Example: [release 0.4.1 tag](https://github.com/apple/container/tree/0.4.1)
Take a guided tour of `container` by building, running, and publishing a simple web server image.
## Try out the `container` CLI
Start the application, and try out some basic commands to familiarize yourself with the command line interface (CLI) tool.
### Start the container service
Start the services that `container` uses:
```bash
container system start
```
If you have not installed a Linux kernel yet, the command will prompt you to install one:
<pre>
% container system start
Verifying apiserver is running...
Installing base container filesystem...
No default kernel configured.
Install the recommended default kernel from [https://github.com/kata-containers/kata-containers/releases/download/3.17.0/kata-static-3.17.0-arm64.tar.xz]? [Y/n]: y
Installing kernel...
%
</pre>
Then, verify that the application is working by running a command to list all containers:
```bash
container list --all
```
If you haven't created any containers yet, the command outputs an empty list:
<pre>
% container list --all
ID IMAGE OS ARCH STATE IP
%
</pre>
### Get CLI help
You can get help for any `container` CLI command by appending the `--help` option:
<pre>
% container --help
OVERVIEW: A container platform for macOS
USAGE: container [--debug] <subcommand>
OPTIONS:
--debug Enable debug output [environment: CONTAINER_DEBUG]
--version Show the CLI version (single line).
-h, --help Show help information.
Detailed version information is available under the system command:
```
container system version [--format json|table]
```
CONTAINER SUBCOMMANDS:
create Create a new container
delete, rm Delete one or more containers
exec Run a new command in a running container
inspect Display information about one or more containers
kill Kill one or more running containers
list, ls List containers
logs Fetch container stdio or boot logs
run Run a container
start Start a container
stop Stop one or more running containers
IMAGE SUBCOMMANDS:
build Build an image from a Dockerfile
image, i Manage images
registry, r Manage registry configurations
SYSTEM SUBCOMMANDS:
builder Manage an image builder instance
system, s Manage system components
%
</pre>
### Abbreviations
You can save keystrokes by abbreviating commands and options. For example, abbreviate the `container list` command to `container ls`, and the `--all` option to `-a`:
<pre>
% container ls -a
ID IMAGE OS ARCH STATE IP
%
</pre>
Use the `--help` flag to see which abbreviations exist.
### Set up a local DNS domain (optional)
`container` includes an embedded DNS service that simplifies access to your containerized applications. If you want to configure a local DNS domain named `test` for this tutorial, run:
```bash
sudo container system dns create test
```
Enter your administrator password when prompted. The first command requires administrator privileges to create a file containing the domain configuration under the `/etc/resolver` directory, and to tell the macOS DNS resolver to reload its configuration files.
With the domain set to `test`, if you use `--name my-web-server` to start a container, queries to `my-web-server.test` will respond with that container's IP address. You can customize the domain in `~/.config/container/config.toml`.
## Build an image
Set up a `Dockerfile` for a basic Python web server, and use it to build a container image named `web-test`.
### Set up a simple project
Start a terminal, create a directory named `web-test` for the files needed to create the container image:
```bash
mkdir web-test
cd web-test
```
In the `web-test` directory, create a file named `Dockerfile` with this content:
```dockerfile
FROM docker.io/python:alpine
WORKDIR /content
RUN apk add curl
RUN echo '<!DOCTYPE html><html><head><title>Hello</title></head><body><h1>Hello, world!</h1></body></html>' > index.html
CMD ["python3", "-m", "http.server", "80", "--bind", "0.0.0.0"]
```
The `FROM` line instructs the `container` builder to start with a base image containing the latest production version of Python 3.
The `WORKDIR` line creates a directory `/content` in the image, and makes it the current directory.
The first `RUN` line adds the `curl` command to your image, and the second `RUN` line creates a simple HTML landing page named `/content/index.html`.
The `CMD` line configures the container to run a simple web server in Python on port 80. Since the working directory is `/content`, the web server runs in that directory and delivers the content of the file `/content/index.html` when a user requests the index page URL.
The server listens on the wildcard address `0.0.0.0` to allow connections from the host and other containers. You can safely use the listen address `0.0.0.0` inside the container, because external systems have no access to the virtual network to which the container attaches.
### Build the web server image
Run the `container build` command to create an image with the name `web-test` from your `Dockerfile`:
```bash
container build --tag web-test --file Dockerfile .
```
The last argument `.` tells the builder to use the current directory (`web-test`) as the root of the build context. You can copy files within the build context into your image using the `COPY` command in your Dockerfile.
After the build completes, list the images. You should see both the base image and the image that you built in the results:
<pre>
% container image list
NAME TAG DIGEST
python alpine b4d299311845
web-test latest 25b99501f174
%
</pre>
## Run containers
Using your container image, run a web server and try out different ways of interacting with it.
### Start the webserver
Use `container run` to start a container named `my-web-server` that runs your webserver:
```bash
container run --name my-web-server --detach --rm web-test
```
The `--detach` flag runs the container in the background, so that you can continue running commands in the same terminal. The `--rm` flag causes the container to be removed automatically after it stops.
When you list containers now, `my-web-server` is present, along with the container that `container` started to build your image. Note that its IP address, shown in the `IP` column, is `192.168.64.3`:
<pre>
% container ls
ID IMAGE OS ARCH STATE IP
buildkit ghcr.io/apple/container-builder-shim/builder:0.0.3 linux arm64 running 192.168.64.2
my-web-server web-test:latest linux arm64 running 192.168.64.3
%
</pre>
Open the website, using the container's IP address in the URL:
```bash
open http://192.168.64.3
```
If you configured the local domain `test` earlier in the tutorial, you can also open the page with the full hostname for the container:
```bash
open http://my-web-server.test
```
### Monitor container resource usage
Now that your web server is running, you can monitor its resource usage with the `container stats` command:
```bash
container stats my-web-server
```
This displays real-time statistics about CPU usage, memory consumption, network traffic, disk I/O, and the number of running processes:
<pre>
% container stats --no-stream my-web-server
Container ID Cpu % Memory Usage Net Rx/Tx Block I/O Pids
my-web-server 0.23% 12.45 MiB / 1.00 GiB 856.00 KiB / 1.2 KiB 2.10 MiB / 512 KiB 2
%
</pre>
> [!NOTE]
> Without the `--no-stream` flag, `container stats` continuously updates the display in real-time, similar to the `top` command. Press Ctrl+C to exit the live view.
### Run other commands in the container
You can run other commands in `my-web-server` by using the `container exec` command. To list the files under the content directory, run an `ls` command:
<pre>
% container exec my-web-server ls /content
index.html
%
</pre>
If you want to poke around in the container, run a shell and issue one or more commands:
<pre>
% container exec --tty --interactive my-web-server sh
/content # ls
index.html
/content # uname -a
Linux my-web-server 6.12.28 #1 SMP Tue May 20 15:19:05 UTC 2025 aarch64 Linux
/content # exit
%
</pre>
The `--tty` and `--interactive` flag allow you to interact with the shell from your host terminal. The `--tty` flag tells the shell in the container that its input is a terminal device, and the `--interactive` flag connects what you input in your host terminal to the input of the shell in the container.
You will often see these two options abbreviated and specified together as `-ti` or `-it`.
### Access the web server from another container
Your web server is accessible from other containers as well as from your host. Launch a second container using your `web-test` image, and this time, specify a `curl` command to retrieve the `index.html` content from the first container.
> [!NOTE]
> Container relies on the new features and enhancements present in macOS 26.
> As a result, the functionality of accessing the web server from another container will not work on macOS 15.
> See https://github.com/apple/container/blob/main/docs/technical-overview.md#macos-15-limitations for more details.
```bash
container run -it --rm web-test curl http://192.168.64.3
```
The output should appear as:
<pre>
% container run -it --rm web-test curl http://192.168.64.3
&lt;!DOCTYPE html>&lt;html>&lt;head>&lt;title>Hello&lt;/title>&lt;/head>&lt;body>&lt;h1>Hello, world!&lt;/h1>&lt;/body>&lt;/html>
%
</pre>
If you set up the `test` domain earlier, you can achieve the same result with:
```bash
container run -it --rm web-test curl http://my-web-server.test
```
## Run a published image
Push your image to a container registry, publishing it so that you and others can use it.
### Publish the web server image
To publish your image, you need to push images to a registry service that stores the image for future use. Typically, you need to authenticate with a registry to push an image. This example assumes that you have an account at a hypothetical registry named `some-registry.example.com` with username `fido` and a password or token `my-secret`, and that your personal repository name is the same as your username.
To sign into a secure registry with your login credentials, enter your username and password at the prompts after running:
```bash
container registry login some-registry.example.com
```
Create another name for your image that includes the registry name, your repository name, and the image name, with the tag `latest`:
```bash
container image tag web-test some-registry.example.com/fido/web-test:latest
```
Then, push the image:
```bash
container image push some-registry.example.com/fido/web-test:latest
```
> [!NOTE]
> By default `container` is configured to use Docker Hub.
> You can change the default registry by setting `domain` under `[registry]` in `~/.config/container/config.toml`:
> ```toml
> [registry]
> domain = "some-registry.example.com"
> ```
> See the other sub commands under `container registry` for more options.
### Pull and run your image
To validate your published image, stop your current web server container, remove the image that you built, and then run using the remote image:
```bash
container stop my-web-server
container image delete web-test some-registry.example.com/fido/web-test:latest
container run --name my-web-server --detach --rm some-registry.example.com/fido/web-test:latest
```
## Clean up
Stop your container and shut down the application.
### Shut down the web server
Stop your web server container with:
```bash
container stop my-web-server
```
If you list all running and stopped containers, you will see that the `--rm` flag you supplied with the `container run` command caused the container to be removed:
<pre>
% container list --all
ID IMAGE OS ARCH STATE IP
buildkit ghcr.io/apple/container-builder-shim/builder:0.0.3 linux arm64 running 192.168.64.2
%
</pre>
### Stop the container service
When you want to stop `container` completely, run:
```bash
container system stop
```