How Health Check and Process Monitoring Work in apple/container
The apple/container daemon exposes an HTTP /health endpoint via the container-apiserver, while the CLI uses ClientHealthCheck.ping(timeout:) with configurable timeouts to verify liveness before executing commands.
The apple/container repository provides a macOS container runtime that relies on a background daemon for orchestration. To ensure reliability, the project implements a two-layer health check and process monitoring strategy that combines HTTP-based liveness probes with launchd process supervision. This architecture allows the CLI to detect daemon state instantly and recover from failures without hanging the user terminal.
Health Check Request Flow
The HTTP Endpoint and HealthCheckService
In Sources/APIServer/APIServer+Start.swift (lines 248-260), the apiserver initializes a HealthCheckService that registers the /health route during startup. This service gathers runtime metadata and returns a JSON payload containing version information and filesystem paths.
Client Implementation with Timeouts
The ClientHealthCheck.ping(timeout:) method in Sources/Services/ContainerAPIService/Client/ClientHealthCheck.swift sends an HTTP GET request to http://127.0.0.1:2379/health. The implementation wraps all requests in configurable timeouts—.seconds(2) for status checks and .seconds(10) for version queries—preventing CLI hangs when the daemon is unresponsive.
SystemHealth Data Structure
The JSON response decodes into the SystemHealth struct defined in Sources/Services/ContainerAPIService/Client/SystemHealth.swift. This model includes:
appRoot: Path to container-app datainstallRoot: Location of container binarieslogRoot: Optional directory for log filesapiServerVersion,apiServerCommit,apiServerBuild,apiServerAppName: Daemon versioning metadata
Process Monitoring Architecture
launchd Integration
The apiserver runs as a launchd job named com.apple.container.apiserver. The CLI commands container system start, container system stop, and container system status interface with launchd via launchctl to query job states and manage the service lifecycle. This provides OS-level automatic restart capabilities if the daemon crashes.
Failure Detection and Recovery
When the health check fails or times out, the CLI treats the daemon as dead and skips further actions. For example, container system stop prints "APIServer health check failed, skipping bootout" when it cannot reach the endpoint, preventing destructive operations against an already-stopped service.
Log-Based Monitoring
The --log-root option enables file-based logging, allowing the CLI to stream real-time diagnostics via container system logs. The apiserver writes logs to the specified directory while the CLI reads these files to provide continuous monitoring capabilities.
Practical Implementation Examples
Checking Daemon Health in Swift
import ContainerAPIService
// Ping the apiserver with a 2-second timeout
do {
let health = try await ClientHealthCheck.ping(timeout: .seconds(2))
print("Daemon version:", health.apiServerVersion)
print("App root:", health.appRoot.path)
} catch {
print("Health check failed:", error)
}
CLI Status Commands
When the daemon is running:
$ container system status
┌─────────────────────┬───────────┐
│ COMPONENT │ STATUS │
├─────────────────────┼───────────┤
│ container-apiserver │ running │
└─────────────────────┴───────────┘
When the daemon is down:
$ container system status
⚠️ APIServer health check failed, skipping bootout
Summary
- The health check and process monitoring system uses a two-layer approach: HTTP
/healthendpoints for fast liveness checks and launchd for OS-level process supervision. ClientHealthCheck.ping(timeout:)inClientHealthCheck.swiftimplements timeout-protected HTTP requests to prevent CLI hangs.- The
SystemHealthstruct inSystemHealth.swiftprovides structured metadata including paths, versions, and build information. - launchd integration via
com.apple.container.apiserverensures automatic recovery and provides standard start/stop semantics. - Configurable timeouts (2 seconds for status, 10 seconds for version) balance responsiveness against network variability.
Frequently Asked Questions
How does the apple/container CLI detect if the daemon is running?
The CLI calls ClientHealthCheck.ping(timeout:) which sends an HTTP GET request to http://127.0.0.1:2379/health. If the request succeeds within the timeout window (2 seconds for status commands), the daemon is considered alive; otherwise, the CLI reports it as unavailable and skips dependent operations.
What information does the health check endpoint return?
The /health endpoint returns a JSON payload that decodes into the SystemHealth struct, containing appRoot, installRoot, logRoot paths, and versioning metadata including apiServerVersion, apiServerCommit, and apiServerBuild.
What happens if the health check times out?
If the health check exceeds its configured timeout—.seconds(2) for status checks or .seconds(10) for version queries—the CLI assumes the daemon is unhealthy. Commands like container system stop will print a warning and skip bootout operations to avoid hanging the terminal or executing invalid state transitions.
How is the container-apiserver process monitored at the OS level?
The apiserver registers as a launchd job named com.apple.container.apiserver. This provides macOS-level process supervision, ensuring the daemon restarts automatically after crashes and integrates with the system's service management framework for reliable start/stop operations.
Have a question about this repo?
These articles cover the highlights, but your codebase questions are specific. Give your agent direct access to the source. Share this with your agent to get started:
curl -s "https://instagit.com/install.md" Maintain an open-source project? Get it listed too →