From 6cf12720574b90b8485d56a72a41b556e9fd2ed7 Mon Sep 17 00:00:00 2001 From: aevesdocker Date: Thu, 2 Jul 2026 11:05:29 +0100 Subject: [PATCH 1/3] compose: init containers --- content/manuals/compose/compose-sdk.md | 5 - .../compose/how-tos/init-containers.md | 180 ++++++++++++++++++ content/manuals/compose/how-tos/lifecycle.md | 6 +- .../compose/how-tos/provider-services.md | 2 +- content/reference/compose-file/services.md | 41 ++++ data/summary.yaml | 2 + 6 files changed, 227 insertions(+), 9 deletions(-) create mode 100644 content/manuals/compose/how-tos/init-containers.md diff --git a/content/manuals/compose/compose-sdk.md b/content/manuals/compose/compose-sdk.md index 09ac2335ad32..b2616a88b5db 100644 --- a/content/manuals/compose/compose-sdk.md +++ b/content/manuals/compose/compose-sdk.md @@ -4,11 +4,6 @@ keywords: docker compose sdk, compose api, Docker developer SDK title: Using the Compose SDK linkTitle: Compose SDK weight: 60 -params: - sidebar: - badge: - color: green - text: New --- {{< summary-bar feature_name="Compose SDK" >}} diff --git a/content/manuals/compose/how-tos/init-containers.md b/content/manuals/compose/how-tos/init-containers.md new file mode 100644 index 000000000000..2a104d883c0c --- /dev/null +++ b/content/manuals/compose/how-tos/init-containers.md @@ -0,0 +1,180 @@ +--- +title: Use init containers in Compose +linkTitle: Use init containers +weight: 120 +description: Use pre_start init containers to run setup tasks before a service starts in Docker Compose. +keywords: docker compose init containers, pre_start, docker compose migrations, volume permissions, docker compose lifecycle +params: + sidebar: + badge: + color: green + text: New +--- + +{{< summary-bar feature_name="Compose pre_start" >}} + +Init containersare short-lived containers that run before a service's main container starts. They execute sequentially, each running to completion before the next begins. If any step exits with a non-zero code, the service will not start. + +Use them for setup work that must finish before the application boots: running database migrations, fixing volume permissions, generating dynamic configuration, or executing any ordered sequence of prerequisites. + + +Compose models init containers as [`pre_start`](/reference/compose-file/services.md#pre_start) lifecycle hooks. Unlike [`post_start`](/reference/compose-file/services.md#post_start) and [`pre_stop`](/reference/compose-file/services.md#pre_stop), which run a command inside the running service container, each `pre_start` step runs in its own ephemeral container created after the service container is created +but before it is started. + +## When not to use init containers + +For static files and secrets, use the native [`configs`](/reference/compose-file/configs.md) and [`secrets`](/reference/compose-file/secrets.md) top-level elements instead. Compose mounts them directly into containers with a configurable target path, mode, UID, and GID. + +For background tasks with their own lifecycle. For example, scheduled backups, post-exit cleanup, periodic maintenance. Those tasks run independently of a service's startup sequence. + +## How `pre_start` containers run + +Each step in a service's `pre_start` list: + +- Runs in its own ephemeral container, created after the service container is created but before it starts. +- Inherits the service's image by default. Set `image` to override. +- Joins the same networks as the service, so it can reach services declared in [`depends_on`](/reference/compose-file/services.md#depends_on). +- Shares the service's volume mounts, so files written to a shared volume are immediately visible to the service. +- Must exit `0` for the next step, and the service itself, to start. A non-zero exit aborts startup for the service and anything that depends on it. + +A `pre_start` step is skipped on subsequent `docker compose up` runs if it previously succeeded and its definition hasn't changed nor when the service container restarts under its `restart` policy. It reruns if the definition changes, the previous run failed, or the service is recreated with `--force-recreate`. + +## Examples + +### Run a database migration before the app starts + +In the following example, `app` waits for `db` to be healthy, then runs +`./manage.py migrate` in an ephemeral container that reuses the app's +image. The service container only starts once the migration exits `0`. + +```yaml +services: + app: + image: myapp:latest + depends_on: + db: + condition: service_healthy + pre_start: + - command: ["./manage.py", "migrate"] + + db: + image: postgres:18 + healthcheck: + test: ["CMD-SHELL", "pg_isready -U $${POSTGRES_USER} -d $${POSTGRES_DB}"] + interval: 10s + retries: 5 + start_period: 30s + timeout: 10s +``` + +If the migration fails, `app` does not start and the failure is reported in +the `docker compose up` output. + +### Fix volume ownership before a non-root service starts + +Named volumes are created with root ownership. When the service runs as a +non-root user, you can use a `pre_start` step to adjust ownership before +the service mounts the volume. + +```yaml +services: + app: + image: myapp:latest + user: "1000:1000" + volumes: + - data:/data + pre_start: + - image: busybox + user: root + command: sh -c 'chown -R 1000:1000 /data' + +volumes: + data: +``` + +The `pre_start` step uses a different image (`busybox`) and runs as `root`, +even though the service itself runs as user `1000`. + +### Chain multiple setup steps + +`pre_start` steps run in declared order. The next step only starts once the +previous one exits `0`. In the following example, the application waits +for migrations to finish, then for seed data to load, before starting. + +```yaml +services: + app: + image: myapp:latest + depends_on: + db: + condition: service_healthy + pre_start: + - command: ["./manage.py", "migrate"] + - command: ["./manage.py", "loaddata", "fixtures.json"] + + db: + image: postgres:18 +``` + +Each step runs in its own ephemeral container. If the second step fails, +the first step is not rolled back, but `app` does not start. + +### Replace the one-shot service pattern + +Before `pre_start`, the common way to express "run X before Y starts" was +to model the setup work as a service with `restart: "no"` and have the main +service `depends_on` it with `condition: service_completed_successfully`: + +```yaml +services: + migrate: + image: myapp:latest + command: ["./manage.py", "migrate"] + restart: "no" + + app: + image: myapp:latest + depends_on: + migrate: + condition: service_completed_successfully +``` + +The equivalent expressed with `pre_start`: + +```yaml +services: + app: + image: myapp:latest + pre_start: + - command: ["./manage.py", "migrate"] +``` + +`pre_start` is preferable because: + +- The setup work is modeled as a subordinate step of the service, not as a peer service that exits immediately. +- Completed steps do not appear as exited services in `docker compose ps`. +- Chaining several setup steps does not require a web of `depends_on` edges between one-shot services. +- The ephemeral container inherits the service's image by default, so no duplicate `image:` declaration is needed. + +The one-shot service pattern still has its place when the setup work is a shared concern that multiple services depend on, or when it needs to be addressed independently of any single service. + +## Limitations + +- `pre_start` supports running the hook once for the service as a whole + (`per_replica: false`). Running a `pre_start` step once per replica + (`per_replica: true`) is not yet supported. +- Volume mounts shared across replicas (named volumes, bind mounts) are + reachable from a `pre_start` step. Per-instance mounts such as `tmpfs` + or anonymous volumes cannot be addressed by a single shared run. +- `pre_start` is not re-triggered when you scale a service up. The step + runs again only on definition change, prior failure, or + `--force-recreate`. + +## Reference and additional information + +- [`pre_start`](/reference/compose-file/services.md#pre_start) +- [`post_start`](/reference/compose-file/services.md#post_start) +- [`pre_stop`](/reference/compose-file/services.md#pre_stop) +- [`depends_on`](/reference/compose-file/services.md#depends_on) +- [Use lifecycle hooks](/manuals/compose/how-tos/lifecycle.md) +- [Control startup order](/manuals/compose/how-tos/startup-order.md) diff --git a/content/manuals/compose/how-tos/lifecycle.md b/content/manuals/compose/how-tos/lifecycle.md index a5924ae3cfc3..65f1ebd992b3 100644 --- a/content/manuals/compose/how-tos/lifecycle.md +++ b/content/manuals/compose/how-tos/lifecycle.md @@ -1,9 +1,9 @@ --- title: Using lifecycle hooks with Compose linkTitle: Use lifecycle hooks -weight: 20 -description: Learn how to use Docker Compose lifecycle hooks like post_start and pre_stop to customize container behavior. -keywords: docker compose lifecycle hooks, post_start, pre_stop, docker compose entrypoint, docker container stop hooks, compose hook commands +weight: 110 +description: Learn how to use Docker Compose lifecycle hooks like pre_start, post_start, and pre_stop to customize container behavior. +keywords: docker compose lifecycle hooks, post_start, pre_stop, pre_start, docker compose entrypoint, docker container stop hooks, compose hook commands --- {{< summary-bar feature_name="Compose lifecycle hooks" >}} diff --git a/content/manuals/compose/how-tos/provider-services.md b/content/manuals/compose/how-tos/provider-services.md index 4d9eb7a58101..c44a9f2f6e51 100644 --- a/content/manuals/compose/how-tos/provider-services.md +++ b/content/manuals/compose/how-tos/provider-services.md @@ -2,7 +2,7 @@ title: Use provider services description: Learn how to use provider services in Docker Compose to integrate external capabilities into your applications keywords: compose, docker compose, provider, services, platform capabilities, integration, model runner, ai -weight: 112 +weight: 130 --- {{< summary-bar feature_name="Compose provider services" >}} diff --git a/content/reference/compose-file/services.md b/content/reference/compose-file/services.md index 9bbe421dc891..a2cbe62955b1 100644 --- a/content/reference/compose-file/services.md +++ b/content/reference/compose-file/services.md @@ -1752,6 +1752,47 @@ services: For more information, see [Use lifecycle hooks](/manuals/compose/how-tos/lifecycle.md). +### pre_start + +{{< summary-bar feature_name="Compose pre_start" >}} + +`pre_start` defines a sequence of init containers to run before the service container is started. Each step runs to completion, in declared order, and the service container only starts once every step has exited `0`. A non-zero exit fails the bring-up of the service and its dependents. + +Unlike `post_start` and `pre_stop`, which run a command inside the running service container, each `pre_start` step runs in its own ephemeral container, created after the service container is created but before it is started. Possible values are: + +- `command`: The command to run. Optional when the chosen image's entrypoint already runs the intended command. +- `image`: The image used for the ephemeral container. If omitted, the parent service's image is used. +- `user`: The user to run the command. If not set, defaults to the user declared in `image` (or to the main service command's user when `image` is omitted). +- `privileged`: Lets the `pre_start` command run with privileged access. +- `working_dir`: The working directory in which to run the command. If not set, it is run in the same working directory as the main service command. +- `environment`: Sets the environment variables to run the `pre_start` command. The command inherits the `environment` set for the service's main command, and this section lets you append or override values. +- `per_replica: false`: Whether the hook runs once for the service as a whole before any replica starts. + +A `pre_start` container joins the same networks as the service, so it can reach services declared in `depends_on`, and shares the service's declared volume mounts so files it produces in a shared volume are visible to the service. With `per_replica: false` and a scaled service, only mounts that are shared across replicas (named volumes, bind mounts) are usable. Per-instance mounts (`tmpfs`, anonymous volumes) cannot be addressed by a single run. + +A `pre_start` step that has already succeeded for its current definition is not re-run on a subsequent `up`, nor when the service container restarts under its `restart` policy. A step runs again when its definition changes, when the previous run did not succeed, or when the service is recreated. + +```yaml +services: + app: + image: myapp:latest + depends_on: + db: + condition: service_healthy + pre_start: + - command: ["./manage.py", "migrate"] + - image: busybox + command: sh -c 'chown -R 1000:1000 /data' + volumes: + - data:/data + + db: + image: postgres:16 + +volumes: + data: +``` + ### `pre_stop` {{< summary-bar feature_name="Compose pre stop" >}} diff --git a/data/summary.yaml b/data/summary.yaml index 1732712cbf5d..c1e8e9acdbe0 100644 --- a/data/summary.yaml +++ b/data/summary.yaml @@ -111,6 +111,8 @@ Compose label file: requires: Docker Compose [2.32.2](https://github.com/docker/compose/releases/tag/v2.32.2) and later Compose lifecycle hooks: requires: Docker Compose [2.30.0](https://github.com/docker/compose/releases/tag/v2.30.0) and later +Compose pre_start: + requires: Docker Compose [5.3.0](https://github.com/docker/compose/releases/tag/v5.3.0) and later Compose mac address: requires: Docker Compose [2.23.2](https://github.com/docker/compose/releases/tag/v2.23.2) and later Compose menu: From 976ffdcaf62ce492081cb022f57d875cbb4f1989 Mon Sep 17 00:00:00 2001 From: aevesdocker Date: Thu, 2 Jul 2026 11:14:02 +0100 Subject: [PATCH 2/3] vale --- .../compose/how-tos/init-containers.md | 25 +++++++------------ 1 file changed, 9 insertions(+), 16 deletions(-) diff --git a/content/manuals/compose/how-tos/init-containers.md b/content/manuals/compose/how-tos/init-containers.md index 2a104d883c0c..79b2a96cee58 100644 --- a/content/manuals/compose/how-tos/init-containers.md +++ b/content/manuals/compose/how-tos/init-containers.md @@ -13,19 +13,17 @@ params: {{< summary-bar feature_name="Compose pre_start" >}} -Init containersare short-lived containers that run before a service's main container starts. They execute sequentially, each running to completion before the next begins. If any step exits with a non-zero code, the service will not start. +Init containers are short-lived containers that run before a service's main container starts. They execute sequentially, each running to completion before the next begins. If any step exits with a non-zero code, the service will not start. Use them for setup work that must finish before the application boots: running database migrations, fixing volume permissions, generating dynamic configuration, or executing any ordered sequence of prerequisites. - -Compose models init containers as [`pre_start`](/reference/compose-file/services.md#pre_start) lifecycle hooks. Unlike [`post_start`](/reference/compose-file/services.md#post_start) and [`pre_stop`](/reference/compose-file/services.md#pre_stop), which run a command inside the running service container, each `pre_start` step runs in its own ephemeral container created after the service container is created -but before it is started. +Compose models init containers as [`pre_start`](/reference/compose-file/services.md#pre_start) lifecycle hooks. Unlike [`post_start`](/reference/compose-file/services.md#post_start) and [`pre_stop`](/reference/compose-file/services.md#pre_stop), which run a command inside the running service container, each `pre_start` step runs in its own ephemeral container created after the service container is created but before it is started. ## When not to use init containers -For static files and secrets, use the native [`configs`](/reference/compose-file/configs.md) and [`secrets`](/reference/compose-file/secrets.md) top-level elements instead. Compose mounts them directly into containers with a configurable target path, mode, UID, and GID. +For static files and secrets, use the native [`configs`](/reference/compose-file/configs.md) and [`secrets`](/reference/compose-file/secrets.md) top-level elements instead. Compose mounts them directly into containers with a configurable target path, mode, UID, and GID. No init container required. -For background tasks with their own lifecycle. For example, scheduled backups, post-exit cleanup, periodic maintenance. Those tasks run independently of a service's startup sequence. +For background tasks with their own lifecycle - scheduled backups, post-exit cleanup, periodic maintenance — init containers are the wrong tool. Those tasks run independently of service startup, not before it. ## How `pre_start` containers run @@ -37,7 +35,7 @@ Each step in a service's `pre_start` list: - Shares the service's volume mounts, so files written to a shared volume are immediately visible to the service. - Must exit `0` for the next step, and the service itself, to start. A non-zero exit aborts startup for the service and anything that depends on it. -A `pre_start` step is skipped on subsequent `docker compose up` runs if it previously succeeded and its definition hasn't changed nor when the service container restarts under its `restart` policy. It reruns if the definition changes, the previous run failed, or the service is recreated with `--force-recreate`. +A `pre_start` step is skipped on subsequent `docker compose up` runs if it previously succeeded, its definition hasn't changed, or when the service container restarts under its `restart` policy. It reruns if the definition changes, the previous run failed, or the service is recreated with `--force-recreate`. ## Examples @@ -156,19 +154,14 @@ services: - Chaining several setup steps does not require a web of `depends_on` edges between one-shot services. - The ephemeral container inherits the service's image by default, so no duplicate `image:` declaration is needed. -The one-shot service pattern still has its place when the setup work is a shared concern that multiple services depend on, or when it needs to be addressed independently of any single service. +The one-shot service pattern still has its place when the setup work is a shared concern that multiple services depend on, or when it needs to be addressable independently of any single service. ## Limitations -- `pre_start` supports running the hook once for the service as a whole - (`per_replica: false`). Running a `pre_start` step once per replica - (`per_replica: true`) is not yet supported. -- Volume mounts shared across replicas (named volumes, bind mounts) are - reachable from a `pre_start` step. Per-instance mounts such as `tmpfs` +- `pre_start` uns once for the service as a whole, not once per replica (`per_replica: false`). Per-replica execution (`per_replica: true`) is not yet supported. +- Volume mounts shared across replicas (named volumes, bind mounts) are accessible from a `pre_start` step. Per-instance mounts such as `tmpfs` or anonymous volumes cannot be addressed by a single shared run. -- `pre_start` is not re-triggered when you scale a service up. The step - runs again only on definition change, prior failure, or - `--force-recreate`. +- `pre_start` does not re-trigger when you scale a service up. A step runs again only on definition change, prior failure, or `--force-recreate`. ## Reference and additional information From 8d75617c68d1a6ff93186013e8f14816987bccc7 Mon Sep 17 00:00:00 2001 From: Allie Sadler <102604716+aevesdocker@users.noreply.github.com> Date: Thu, 2 Jul 2026 11:41:39 +0100 Subject: [PATCH 3/3] Update content/manuals/compose/how-tos/init-containers.md Co-authored-by: Guillaume Lours <705411+glours@users.noreply.github.com> --- content/manuals/compose/how-tos/init-containers.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/content/manuals/compose/how-tos/init-containers.md b/content/manuals/compose/how-tos/init-containers.md index 79b2a96cee58..526abe63e3d4 100644 --- a/content/manuals/compose/how-tos/init-containers.md +++ b/content/manuals/compose/how-tos/init-containers.md @@ -158,7 +158,7 @@ The one-shot service pattern still has its place when the setup work is a shared ## Limitations -- `pre_start` uns once for the service as a whole, not once per replica (`per_replica: false`). Per-replica execution (`per_replica: true`) is not yet supported. +- `pre_start` runs once for the service as a whole, not once per replica (`per_replica: false`). Per-replica execution (`per_replica: true`) is not yet supported. - Volume mounts shared across replicas (named volumes, bind mounts) are accessible from a `pre_start` step. Per-instance mounts such as `tmpfs` or anonymous volumes cannot be addressed by a single shared run. - `pre_start` does not re-trigger when you scale a service up. A step runs again only on definition change, prior failure, or `--force-recreate`.