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 data
  • installRoot: Location of container binaries
  • logRoot: Optional directory for log files
  • apiServerVersion, 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 /health endpoints for fast liveness checks and launchd for OS-level process supervision.
  • ClientHealthCheck.ping(timeout:) in ClientHealthCheck.swift implements timeout-protected HTTP requests to prevent CLI hangs.
  • The SystemHealth struct in SystemHealth.swift provides structured metadata including paths, versions, and build information.
  • launchd integration via com.apple.container.apiserver ensures 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:

Share the following with your agent to get started:
curl -s "https://instagit.com/install.md"

Works with
Claude Codex Cursor VS Code OpenClaw Any MCP Client

Maintain an open-source project? Get it listed too →