# ufoscout/docker-compose-wait for container startup dependencies

> ufoscout/docker-compose-wait is a small Rust utility that delays an application until configured TCP endpoints or local paths are available. It is aimed at Docker Compose, Kubernetes, Docker stack, and similar container workflows that need startup coordination.

**ufoscout/docker-compose-wait** — A simple script to wait for other docker images to be started while using docker-compose (or Kubernetes or docker stack or whatever)

- Repository: https://github.com/ufoscout/docker-compose-wait
- Stars: 1,707 · Forks: 143
- Language: Rust
- License: Apache-2.0
- Published: 2026-10-16 · Updated: 2026-10-16 · Language: en
- Canonical page: https://hysenlabs.com/projects/ufoscout-docker-compose-wait

## What docker-compose-wait coordinates

docker-compose-wait solves a container startup-order problem. An application container may start before MongoDB, Postgres, MySQL, or another service is accepting connections. The utility is intended to run before the application and can wait for a fixed delay, TCP endpoints, or files and directories on the container's local filesystem.

The primary audience is developers assembling multi-container deployments with Docker Compose. The README also describes Kubernetes, Docker stack, and other systems where a container can launch another process. This is not a service discovery system, scheduler, or application health checker. It receives explicit targets through environment variables and checks those targets locally from the waiting container.

The central limitation is the meaning of readiness. A successful TCP check confirms that a connection to the configured host and port can be opened. It does not confirm that a database has completed its own initialization, that a migration is finished, or that an API can serve the exact request the application will make. Deployments that require application-level readiness still need their own retry or health behavior.

## Environment-driven waiting and command handoff

The configuration model is centered on environment variables. `WAIT_HOSTS` accepts comma-separated `host:port` pairs, while `WAIT_PATHS` accepts comma-separated local file or directory paths. `WAIT_COMMAND` supplies the command to run after the checks finish. If `WAIT_HOSTS` is not declared, the README states that the script executes without waiting.

The remaining controls separate the total wait limit, connection timeout, initial delay, post-success delay, and retry interval. `WAIT_TIMEOUT` limits the total number of seconds spent waiting for all configured hosts and paths, with a default of 30 seconds. `WAIT_HOST_CONNECT_TIMEOUT` controls one TCP connection attempt and defaults to 5 seconds. `WAIT_SLEEP_INTERVAL` controls the delay between retries and defaults to 1 second. `WAIT_LOGGER_LEVEL` accepts `debug`, `info`, `error`, or `off`, with `debug` as the default.

The command handoff is important. According to the README, the invoked command completely replaces the `wait` process. In a shell-based image, the application is therefore chained after `/wait`. In a `scratch` or distroless image, `wait` must be the `ENTRYPOINT`, and the configured command and arguments must be expressed without shell expansion.

## Docker Compose integration and first use

The documented pattern copies the published executable into an application image, then runs it before the application. The following Dockerfile excerpt comes from the README. The `FROM` image and application files represent the surrounding application, while the copy step obtains the wait executable from the project's published image.

```dockerfile
FROM alpine

COPY --from=ghcr.io/ufoscout/docker-compose-wait:latest /wait /wait

ADD MySuperApp.sh /MySuperApp.sh

CMD /wait && /MySuperApp.sh
```

The Compose service then declares the targets through `WAIT_HOSTS`. This example waits for three named services and their database ports:

```yaml
version: "3"

services:
  mongo:
    image: mongo:3.4
    hostname: mongo
    ports:
      - "27017:27017"

  postgres:
    image: "postgres:9.4"
    hostname: postgres
    ports:
      - "5432:5432"

  mysql:
    image: "mysql:5.7"
    hostname: mysql
    ports:
    - "3306:3306"

  mySuperApp:
    image: "mySuperApp:latest"
    hostname: mySuperApp
    environment:
      WAIT_HOSTS: postgres:5432, mysql:3306, mongo:27017
```

The application service is documented as starting only after all three `host:port` pairs are available. If the executable is placed directly in a service without embedding it in the Dockerfile, the README notes that `command:` must be wrapped in `sh`:

```bash
command: sh -c "/wait && /MySuperApp.sh"
```

## Images without a shell and architecture selection

The shell form is not universal. The README specifically addresses distroless images and images based on `scratch`, where `sh` is absent. In that situation, the wait process can replace itself with the application through an explicit command, but Docker must invoke `/wait` directly in exec form. A wildcard or other shell-style token cannot be expanded because there is no shell to expand it.

The documented scratch example builds the application in one stage, copies the static executable and application into a scratch image, sets `WAIT_COMMAND`, and defines the entrypoint:

```dockerfile
FROM golang

COPY myApp /app
WORKDIR /app
RUN go build -o /myApp -ldflags '-s -w -extldflags -static' ./...

FROM scratch

COPY --from=ghcr.io/ufoscout/docker-compose-wait:latest /wait /wait

COPY --from=0 /myApp /myApp
ENV WAIT_COMMAND="/myApp arg1 argN..."
ENTRYPOINT ["/wait"]
```

Release 2.11.0 and later provide `wait` and `wait_x86_64` for Linux x64, plus `wait_aarch64` and `wait_arm7` for those architectures. The release assets are built with MUSL. A Raspberry Pi target without a matching prebuilt file can be built from the repository after installing Rust. The README gives `cargo build --release` as the build command, and points to Cross for cross-compilation. This creates a packaging concern: the selected binary must match the target image architecture.

## Timeout behavior and operational limits

The defaults are short by design. With no configuration, `WAIT_TIMEOUT` gives the complete set of checks 30 seconds, and a single TCP connection attempt has a five-second limit. A service that needs longer than that window will cause a failure rather than an indefinite wait. The README does not document retry counts, backoff, or a readiness probe protocol, so those behaviors should not be assumed.

`WAIT_PATHS` can delay startup until files or directories exist on the local filesystem. That is useful for a generated certificate, mounted configuration, or an initialization marker, but the utility only checks for existence according to the README. It does not state that file contents are parsed or validated. The same timeout variables cover the configured hosts and paths.

For command-heavy images, the shell requirement adds another operational dependency. A normal Compose service can use the documented `sh -c` wrapper, but a minimal image needs explicit entrypoint configuration and an application command that does not depend on shell syntax. The utility can therefore simplify a Compose file, but it does not remove the need to model the container's runtime interface correctly.

## Maintenance, release history, and license

The repository is not archived, and the recorded last push was on 2026-04-10. The latest listed release is 2.12.1, dated 2023-09-27, after 2.12.0 dated 2023-04-27 and 2.11.0 dated 2023-04-07. The repository contains Rust sources, `Cargo.lock`, `Cargo.toml`, release automation, a test Dockerfile, shell tests, and a `tests` directory, so the published utility is accompanied by repository-level test assets.

`Cargo.toml` identifies the package as `wait`, version 2.12.1, with dependencies including `port_check`, `log`, `env_logger`, `exec`, and `shell-words`. The release profile optimizes for size through `opt-level = 'z'`, link-time optimization, one codegen unit, `panic = 'abort'`, and symbol stripping. These settings are relevant when copying a small executable into a container image.

The repository states an Apache-2.0 license. That identifies the project's stated licensing terms, but it does not by itself answer questions about dependencies, redistribution, or compliance in a particular deployment. Users adopting the executable should review the repository license and the licenses of included or linked dependencies separately. The README also uses a moving `:latest` image tag in its example, while release downloads require a selected version. Pinning a release can make image behavior more repeatable than relying on `latest`.

## Conclusion

Adopt docker-compose-wait when a container must wait for several TCP services or local paths to become available and a shell-based application command is acceptable. Do not treat an open port as proof that the dependency is application-ready, because the utility checks TCP connectivity only. Before adoption, verify that the timeout values match the slowest dependency, confirm that the target image has a shell if the Compose `command:` form is used, and check that the executable architecture matches the container.

## FAQ

### How do I install docker-compose-wait?

The README gives two documented routes: copy `/wait` from `ghcr.io/ufoscout/docker-compose-wait:latest` into an image, or download the `wait` executable from a GitHub release. For an unsupported architecture, clone the repository and run `cargo build --release` after installing Rust.

### How does docker-compose-wait know which services to wait for?

Set `WAIT_HOSTS` to a comma-separated list of `host:port` pairs, such as `postgres:5432, mysql:3306, mongo:27017`. The service names and ports must be reachable from the waiting container.

### Can docker-compose-wait run in a scratch image?

Yes. The README says images without `sh`, including scratch and distroless images, should set `WAIT_COMMAND` and use `ENTRYPOINT ["/wait"]`. Because there is no shell, command arguments are not expanded by `sh`.

### Does an open TCP port prove that a database is ready?

No. The documented check waits for a TCP port to be open. The README does not describe database-level initialization checks, migration detection, or application health probes, so those requirements need separate handling.

### What timeout does docker-compose-wait use by default?

The README lists `WAIT_TIMEOUT` as the maximum number of seconds to wait for all configured hosts and paths, with a default of 30 seconds. A single TCP connection uses `WAIT_HOST_CONNECT_TIMEOUT`, whose default is 5 seconds.

## Sources

- [Issues](https://github.com/ufoscout/docker-compose-wait/issues)
- [License: Apache-2.0](https://github.com/ufoscout/docker-compose-wait/blob/master/LICENSE)
- [README](https://github.com/ufoscout/docker-compose-wait/blob/master/README.md)
- [Releases](https://github.com/ufoscout/docker-compose-wait/releases)
- [ufoscout/docker-compose-wait on GitHub](https://github.com/ufoscout/docker-compose-wait)

---

Hysen Labs editorial analysis, written from the project's own repository and release notes. Cite the canonical page: https://hysenlabs.com/projects/ufoscout-docker-compose-wait
