# How to Use the Init Process Flag and Custom Init Images in Container

> Learn to use the init process flag and custom init images in containers for specialized boot-time logic. Control PID 1 behavior and signal forwarding efficiently.

- Repository: [Apple/container](https://github.com/apple/container)
- Tags: how-to-guide
- Published: 2026-06-11

---

**Use the `--init` flag to run the built-in `vminitd` init process as PID 1 for signal forwarding and zombie reaping, or specify `--init-image <image>` to provide a custom init image that replaces the default binary for specialized boot-time logic.**

The Apple Container project provides mechanisms to handle PID 1 responsibilities in lightweight VMs. When containers run without proper init handling, user applications may ignore termination signals or leave zombie processes behind. The `--init` and `--init-image` flags solve this by injecting proper init management into the container lifecycle according to the source code in `apple/container`.

## Understanding the Init Process in Containers

### Why PID 1 Matters

When a container starts, the specified command becomes **PID 1** inside the VM. This process is responsible for reaping orphaned child processes and forwarding termination signals like `SIGTERM` and `SIGINT` to the running workload. Most user-space applications are not designed to handle these responsibilities, leading to resource leaks and unresponsive containers.

### The Built-in Init Solution

The Container CLI provides a lightweight built-in init process called `vminitd`. When enabled, this binary runs as PID 1, starts the container's main process as a child, and handles signal forwarding and process reaping via `waitpid()` loops.

## Using the `--init` Flag for Signal Management

The `--init` flag is defined in [`Sources/Services/ContainerAPIService/Client/Flags.swift`](https://github.com/apple/container/blob/main/Sources/Services/ContainerAPIService/Client/Flags.swift) at lines 61-63:

```swift
@Flag(name: .customLong("init"),
      help: "Run an init process inside the container that forwards signals and reaps processes")
public var useInit = false

```

When you pass this flag, the runtime creates a VM where `vminitd` runs as the first process. This ensures that signals from the host are properly forwarded to your application and that orphaned child processes are cleaned up automatically.

Run a container with the built-in init process:

```bash
container run --init ubuntu:latest my-app

```

The command `my-app` runs as a child of the lightweight init, which will reap any orphaned processes and forward signals appropriately.

You can also use the flag with `container create` for later management:

```bash
container create --init --name my-container ubuntu:latest my-app
container start my-container

```

## Creating and Using Custom Init Images

The `--init-image` flag allows you to replace the default `vminitd` binary with a custom image containing specialized boot-time logic. This is declared in [`Sources/Services/ContainerAPIService/Client/Flags.swift`](https://github.com/apple/container/blob/main/Sources/Services/ContainerAPIService/Client/Flags.swift) at lines 64-68:

```swift
@Option(name: .long,
        help: .init("Use a custom init image instead of the default", valueName: "image"))
public var initImage: String?

```

Custom init images run inside the same lightweight VM before the OCI container starts, enabling you to inject helper daemons, install eBPF filters, or print diagnostic messages.

### Building a Custom Init Wrapper

Below is a minimal Go wrapper that logs a diagnostic message to the kernel log and then hands off to the real `vminitd` binary (typically renamed `vminitd.real`):

```go
// wrapper.go
package main

import (
    "os"
    "syscall"
)

func main() {
    // Write a diagnostic line to the kernel log
    if kmsg, err := os.OpenFile("/dev/kmsg", os.O_WRONLY, 0); err == nil {
        kmsg.WriteString("<6>custom-init: === CUSTOM INIT IMAGE RUNNING ===\n")
        kmsg.Close()
    }

    // Exec the original vminitd binary
    if err := syscall.Exec("/sbin/vminitd.real", os.Args, os.Environ()); err != nil {
        os.Exit(1)
    }
}

```

Build the wrapper for the VM's architecture:

```bash
CGO_ENABLED=0 GOOS=linux GOARCH=arm64 go build -o wrapper wrapper.go

```

Create a Dockerfile that replaces the default `vminitd` with your wrapper:

```dockerfile

# Use the same vminit base as the upstream project

FROM ghcr.io/apple/containerization/vminit:0.33.3 AS base

FROM ghcr.io/apple/containerization/vminit:0.33.3
COPY --from=base /sbin/vminitd /sbin/vminitd.real   # keep the original as .real

COPY wrapper /sbin/vminitd                         # replace with our wrapper

```

Build the custom init image:

```bash
container build -t local/custom-init:latest .

```

### Running with `--init-image`

Execute a container using your custom init image:

```bash
container run --name my-container \
    --init-image local/custom-init:latest \
    alpine:latest echo "hello world"

```

## Verifying Custom Init Execution

To confirm that your custom init image ran successfully, check the boot logs:

```bash
container logs --boot my-container | grep custom-init

```

Expected output:

```text
[    0.129230] custom-init: === CUSTOM INIT IMAGE RUNNING ===

```

This verification method uses the `container logs --boot` command to access kernel-level messages from the VM initialization phase.

## Summary

- The `--init` flag runs the built-in `vminitd` binary as PID 1 to handle signal forwarding and zombie process reaping automatically.
- The `--init-image <image>` flag allows you to specify a custom init image that replaces the default `vminitd` binary for specialized boot-time behavior.
- Custom init images typically wrap the original `vminitd` binary (as `vminitd.real`) to add diagnostic logging, helper daemons, or other pre-container initialization.
- Both flags are defined in [`Sources/Services/ContainerAPIService/Client/Flags.swift`](https://github.com/apple/container/blob/main/Sources/Services/ContainerAPIService/Client/Flags.swift) and documented in [`docs/command-reference.md`](https://github.com/apple/container/blob/main/docs/command-reference.md) and [`docs/how-to.md`](https://github.com/apple/container/blob/main/docs/how-to.md).
- Verify custom init execution using `container logs --boot` to check kernel messages from the VM initialization phase.

## Frequently Asked Questions

### What is the difference between `--init` and `--init-image`?

The `--init` flag enables the default built-in init process (`vminitd`) that handles signal forwarding and zombie reaping. The `--init-image` flag specifies a custom OCI image to use instead of the default init binary, allowing you to inject custom boot-time logic while still maintaining init responsibilities. When using `--init-image`, you typically still need the underlying init functionality, so custom images often wrap and then exec the original `vminitd` binary.

### How do I build a custom init image for the Container CLI?

Build a binary that performs your custom initialization (such as logging to `/dev/kmsg` or setting up eBPF filters), then execs `/sbin/vminitd.real` to hand off to the standard init process. Package this binary in a Docker image based on `ghcr.io/apple/containerization/vminit`, replacing `/sbin/vminitd` with your wrapper while keeping the original as `vminitd.real`. Build for the target architecture (typically `arm64` or `amd64`) with `CGO_ENABLED=0`.

### Can I use custom init images with `container create`?

Yes, the `--init-image` flag works with both `container run` and `container create` commands. When used with `container create`, the custom init image is configured in the container metadata and used when the container subsequently starts via `container start`.

### Where are the init flags defined in the source code?

The `--init` and `--init-image` flags are declared in [`Sources/Services/ContainerAPIService/Client/Flags.swift`](https://github.com/apple/container/blob/main/Sources/Services/ContainerAPIService/Client/Flags.swift) at lines 61-68. The `--init` flag uses the `@Flag` property wrapper with a custom long name, while `--init-image` uses the `@Option` property wrapper. User-level documentation appears in [`docs/how-to.md`](https://github.com/apple/container/blob/main/docs/how-to.md) (lines 41-55) and [`docs/command-reference.md`](https://github.com/apple/container/blob/main/docs/command-reference.md) (lines 56-58).