From cc0843391f11cc8cc83c089aa10a911be2c13f7b Mon Sep 17 00:00:00 2001 From: Jose Diaz-Gonzalez Date: Sun, 10 May 2026 21:17:09 -0400 Subject: [PATCH] feat: add docker healthcheck to dokku container The official dokku/dokku image gains a HEALTHCHECK directive backed by a loopback-only HTTP endpoint at `127.0.0.1:18080/_dokku/health`. The endpoint reports 200 once first-boot bootstrap finishes, sshd and nginx are accepting connections, and `dokku ps:restore` completes; otherwise it returns 503. Changes are scoped to the Docker overlay and Dockerfile so debian-package installs are unaffected. --- Dockerfile | 3 ++ docker/etc/my_init.d/10_dokku_init | 3 ++ docker/etc/nginx/conf.d/zz-dokku-health.conf | 16 +++++++++ .../runsvdir/default/dokku-restore/finish | 8 +++-- docs/getting-started/install/docker.md | 33 ++++++++++++++++++- 5 files changed, 60 insertions(+), 3 deletions(-) create mode 100644 docker/etc/nginx/conf.d/zz-dokku-health.conf diff --git a/Dockerfile b/Dockerfile index 616983a1d..6dca6ff8e 100644 --- a/Dockerfile +++ b/Dockerfile @@ -81,3 +81,6 @@ RUN \ && rm -f /usr/local/openresty/nginx/conf/sites-enabled/default /usr/share/openresty/html/index.html \ && sed -i '/imklog/d' /etc/rsyslog.conf \ && rm -f /var/log/btmp /var/log/wtmp /var/log/*log /var/log/apt/* /var/log/dokku/*.log /var/log/nginx/* /var/log/openresty/* + +HEALTHCHECK --interval=30s --timeout=10s --start-period=5m --retries=3 \ + CMD curl -fsS http://127.0.0.1:18080/_dokku/health || exit 1 diff --git a/docker/etc/my_init.d/10_dokku_init b/docker/etc/my_init.d/10_dokku_init index 38948d4af..c740a3a09 100755 --- a/docker/etc/my_init.d/10_dokku_init +++ b/docker/etc/my_init.d/10_dokku_init @@ -35,6 +35,9 @@ main() { support-userns + rm -f /var/run/dokku/ready + mkdir -p /var/run/dokku + if [[ ! -d /mnt/dokku ]]; then log-info "Creating missing /mnt/dokku" mkdir -p /mnt/dokku diff --git a/docker/etc/nginx/conf.d/zz-dokku-health.conf b/docker/etc/nginx/conf.d/zz-dokku-health.conf new file mode 100644 index 000000000..92c1475e3 --- /dev/null +++ b/docker/etc/nginx/conf.d/zz-dokku-health.conf @@ -0,0 +1,16 @@ +# Internal health endpoint for the official dokku/dokku Docker image. +# Bound to loopback so it is reachable only from inside the container +# and never conflicts with user app vhosts on 80/443. +server { + listen 127.0.0.1:18080; + server_name _; + access_log off; + default_type text/plain; + + location = /_dokku/health { + if (-f /var/run/dokku/ready) { + return 200 "ok\n"; + } + return 503 "not ready\n"; + } +} diff --git a/docker/etc/runit/runsvdir/default/dokku-restore/finish b/docker/etc/runit/runsvdir/default/dokku-restore/finish index ea80c20c0..84270a92c 100755 --- a/docker/etc/runit/runsvdir/default/dokku-restore/finish +++ b/docker/etc/runit/runsvdir/default/dokku-restore/finish @@ -10,6 +10,10 @@ if [[ "$1" -ne 0 ]]; then fi echo "Done restoring dokku apps." -exec sleep infinity -exit 0 +while ! (echo > /dev/tcp/127.0.0.1/22) >/dev/null 2>&1; do sleep 1; done +while ! (echo > /dev/tcp/127.0.0.1/80) >/dev/null 2>&1; do sleep 1; done +touch /var/run/dokku/ready +echo "Dokku is ready." + +exec sleep infinity diff --git a/docs/getting-started/install/docker.md b/docs/getting-started/install/docker.md index 489762f1b..b2d7b0a9d 100644 --- a/docs/getting-started/install/docker.md +++ b/docs/getting-started/install/docker.md @@ -44,7 +44,38 @@ services: restart: unless-stopped ``` -The above command will start a new docker container that is ready when a message similar to `Runit started as PID 12345` appears. +## Container readiness + +The image ships with a Docker `HEALTHCHECK` that flips from `starting` to `healthy` once first-boot bootstrap is complete (skel restored, `plugin-list` plugins installed, core install triggers fired), nginx and sshd are accepting connections, and `dokku ps:restore` has finished. Until those conditions hold, the container is considered unhealthy and dependent services should not yet send traffic. + +To check the current state from the host: + +```shell +docker inspect --format='{{.State.Health.Status}}' dokku +``` + +Internally, the check is backed by a loopback-only HTTP endpoint: + +```shell +docker exec dokku curl -fsS http://127.0.0.1:18080/_dokku/health +``` + +The endpoint binds to `127.0.0.1:18080` inside the container by design - it is not published to the host and does not interfere with user app vhosts on ports 80/443. + +Compose dependents can gate on the healthcheck via `depends_on` with `condition: service_healthy`: + +```yaml +services: + dokku: + image: dokku/dokku:0.38.2 + # ... + bootstrap: + image: alpine:3.20 + command: ["echo", "dokku is ready"] + depends_on: + dokku: + condition: service_healthy +``` Dokku is run in the following configuration: