apple / apple/container

[Tracking]: restart policy and auto-start on boot — status of #158, #286, #1201, #1258 and a request for maintainer direction

Open
#2,142 2 comments 2 reactions 0 assignees View on GitHub
Dominant language
Swift
Stars
49.9k
Forks
1.8k
Avg merge
1d 20h
Merged PRs (30d)
22

Description

### Feature or enhancement request details

This is a **tracking / status issue**, not a new feature request. Everything here is already
requested and, in large part, already implemented — it is spread across two issues and two PRs,
and the work has been blocked on an unanswered maintainer decision since 2026-06-22.

Opening this to put the state in one place and ask for one specific direction call. Happy for a
maintainer to close it as a duplicate if the discussion is better held on one of the existing
threads — but please answer the question in "What we are asking for" first.

#### Current state

| Thread | What it covers | State | Last activity |
|---|---|---|---|
| #158 | Auto-start containers on boot | Open issue, unassigned | 2026-02-07 |
| #286 | `--restart` flag on `run`/`create` | Open issue, unassigned | 2026-02-07 |
| #1201 | PR: `--system-start` flag (@saehejkang) | Open, **draft**, paused pending direction on #1258 | 2026-02-24 |
| #1258 | PR: full restart policy (@JaewonHur) | Open, **ready for review** | 2026-06-22 |

Nothing has merged. As of today the only way to get a container back after a macOS reboot is a
user-maintained `launchd` agent.

#### What is actually blocking this

PR #1258 has a working implementation and an agreed design. It ends with a direct question to
Apple from @stephenlclarke, who maintains a fork with the work already split into three
review-sized branches:

> Would Apple prefer this PR branch to be refreshed into that smaller stacked shape, or would a
> new first PR for only the create-time API/CLI contract be easier to review?

That question has had no maintainer response for roughly two months. Three contributors
(@JaewonHur, @saehejkang, @stephenlclarke) are each holding off to avoid duplicating the others'
work, so the net effect is that a finished feature is idle on a review-logistics decision rather
than on a technical one.

#### What we are asking for

1. **A direction call on #1258**: keep the single PR, or restack it as create-time API/CLI first,
then runtime behavior, then timing controls. Either answer unblocks it — the ambiguity is the
blocker, not the choice.
2. **A semantics decision**, which #1201 is explicitly waiting on: does `--restart always` also
imply start-on-`container system start`, or does boot behavior stay a separate
`--system-start` flag? #1201 is paused until this is settled.
3. **A rough signal on whether this is on the roadmap at all.** If restart policy is deliberately
out of scope for `apple/container`, saying so plainly is a fine outcome — it lets everyone stop
holding branches open and lets downstream tooling commit to the `launchd` approach.

#### Field notes from running the `launchd` workaround

Sharing these because two of them contradict things stated earlier in these threads, and one is a
trap other users will hit.

**1. `container start` on an already-running container is a clean no-op in 1.2.2.**
The workaround in #158 carries a known-issues note that starting a running container "will stop it
and throw an error." I could not reproduce that on `container` 1.2.2 (Homebrew): it exits 0 and
leaves the container running. Whatever caused that appears to have been fixed. Worth knowing,
because it means a boot script does not strictly need a running/stopped filter.

**2. `brew services start container` is actively harmful — do not recommend it.**
Homebrew's plist sets `KeepAlive` on `container system start`. That command registers the services
and exits immediately, so `launchd` treats every exit as a crash and relaunches it in a loop.
Symptom: `/opt/homebrew/var/log/container.log` fills with repeated `Launching container-apiserver…`
lines while `brew services list` still reports the service as `stopped`. This looks like a
plausible auto-start solution and silently is not one.

**3. Detecting whether the system is up needs an exact field match.**
`container system status` prints `apiserver is not running and not registered with launchd` on
failure — a substring that contains `running`. Scripts doing `container system status | grep -q running`
get a false positive and skip the start. Matching the status field exactly avoids this:

```sh
container system status 2>&1 | awk '$1 == "status" && $2 == "running"'
```

**4. A generic agent works, and survives a real reboot.**
Rather than one plist per container (#158) or a label convention requiring `jq` (the follow-up in
#158), this enumerates every container at runtime, so containers created later are covered with no
plist edits and no dependencies beyond `/bin/sh`:

Boot script (POSIX sh, no dependencies)

```sh
#!/bin/sh
CONTAINER_BIN="${CONTAINER_BIN:-/opt/homebrew/bin/container}"
CONTAINER_AUTOSTART_SKIP="${CONTAINER_AUTOSTART_SKIP:-buildkit}"

log() { echo "[$(date '+%Y-%m-%d %H:%M:%S')] $*"; }

[ -x "$CONTAINER_BIN" ] || { log "error: $CONTAINER_BIN not executable"; exit 1; }

log "starting container system services"
"$CONTAINER_BIN" system start --enable-kernel-install --timeout 60 || {
log "error: 'container system start' failed"; exit 1; }

# 'ls -q' lists running containers, 'ls -a -q' lists all of them;
# the difference is what needs starting.
RUNNING=$("$CONTAINER_BIN" ls -q 2>/dev/null)

"$CONTAINER_BIN" ls -a -q 2>/dev/null | while IFS= read -r id; do
[ -n "$id" ] || continue
skip=false
for s in $CONTAINER_AUTOSTART_SKIP; do [ "$id" = "$s" ] && skip=true; done
$skip && { log "skip $id (excluded)"; continue; }
printf '%s\n' "$RUNNING" | grep -qx "$id" && { log "skip $id (already running)"; continue; }
if "$CONTAINER_BIN" start "$id" >/dev/null 2>&1; then log "started $id"; else log "FAILED to start $id"; fi
done
log "done"
```

Driven by a `RunAtLoad=true`, `KeepAlive=false` LaunchAgent in `~/Library/LaunchAgents`.
`buildkit` is skipped by default since `container build` starts it on demand.

Verified across an actual macOS reboot, not just a `launchctl kickstart`: the agent fired ~30s
after boot and brought both application containers back up.

None of this is a substitute for the feature. A `launchd` agent cannot restart a container that
crashes while the machine is up, it has no backoff or `on-failure` semantics, and every user
reinvents it slightly differently — which is precisely what #286 and #1258 exist to fix.

Thanks to @JaewonHur, @saehejkang and @stephenlclarke for the implementation work already done
here, and to the maintainers for the project generally.

### Code of Conduct

- [x] I agree to follow this project's Code of Conduct

Contributor guide

Open the contributing guide

Research direction

Read the status of issues #158, #286, #1201, and #1258, especially the open PRs and their pending design questions. Start by reviewing the direction requested for #1258 and the semantics question blocking #1201; this tracking issue is done when maintainers decide the review structure, restart semantics, and roadmap status.

Written by the indexing model from the issue text.

Assessment

Tech stack
shell, swift
Domain
cli, devops
Issue type
Feature
Difficulty
5/5
Estimated time
Over a week
Activity status
Stale
Clarity
Needs clarification
Newbie friendliness
25/100

Get new issues in your inbox

A short digest of beginner-friendly GitHub issues.