How an update runs
The ordered steps of a guarded install: database backup, image pull by digest, a new app container beside the old one, a health check, then the switchover.
An install replaces the app and web containers. It never touches postgres or dokku,
and it does not change the compose file, volumes, ports or environment. See
What an update changes.
The design keeps the running deployment serving traffic until the new version has proved
itself healthy, and it records every step so the page can show you where things stand.
Phase 1: prepare, while the current version keeps serving#
-
Backup. The application dumps the Postgres database with
pg_dumpin custom format to/data/backups/pre-update-<timestamp>.dump, where the timestamp is UTC, for examplepre-update-20261003T141500Z.dump. It then reads the dump back withpg_restore --listand gives the file its final name only if it is complete and readable. A failed or timed-out backup stops the install before anything changes. The dump is kept after the install either way, until the housekeeping described in Docker housekeeping removes all but the 2 newest./datais thewi-prod-app-datavolume. The dump is not shown on the Updates page. See When an update fails for what you can say about it. -
Pull. The application pulls the new
appandwebimages by digest, the exact digests the check reported, never by re-resolving the tag. Before it moves any tag, it also tags the image each container is currently running aspre-update-<timestamp>, so the previous images stay on the box as a recovery set. It then points the running tag (for examplelatest) at the new images. -
Start the new
appbeside the current one. A newappcontainer, called the "blue" container, starts from the new image next to the current one. It is named like the current container with-blueappended, and it has its own network alias, so the web server keeps sending traffic only to the currentapp. Database migrations run when blue starts, because the container's entrypoint applies them before it serves. -
Health-check blue. The application polls blue's
/healthzendpoint every 5 seconds for up to 120 seconds. A container that exits, restarts repeatedly, or never answers fails the install. Because migrations run at start, a migration failure means blue never becomes healthy, and the health check catches it. -
Hand off. The application starts a short-lived helper container from the same image and finishes its own work. The helper performs the switchover. This is separate because stopping the old
appwould kill the very process that was running the install.
Phase 2: the switchover, run by the helper#
The helper performs these steps in order. If one fails, its name appears in parentheses in the failure message on the Updates page.
| Step | What happens |
|---|---|
preflight |
Checks that everything it needs exists, before it stops anything. |
stop_old_app |
Stops the current app. |
remove_old_app |
Removes it. |
rename_blue |
Renames blue to the canonical app name. |
realias_blue |
Reconnects blue to the network with the app alias. |
restart_app |
Restarts the promoted app, so its database connections are opened on its final network identity. |
health_check_app |
Checks /healthz through the app alias, the same way the web server reaches it. |
stop_old_web |
Stops the current web. |
remove_old_web |
Removes it. |
recreate_web |
Creates web again from the new image. |
health_check_web |
Checks that the new web answers. |
web is the only container holding host ports 80 and 443, so the new one cannot start until the
old one is gone. That is the brief gap in access you are warned about in the confirmation dialog.
Caddy's certificate volume is carried over to the new web, so the certificate survives
updates. The update itself deletes no images and no backup. What it leaves behind, the
previous images, the helper container and the older dumps, is trimmed afterwards by the
cleanup described next.
After the update: cleanup#
Once an update has succeeded and the new app has had 15 minutes to prove itself, the
application runs a Docker housekeeping cleanup. It removes the finished helper container, the
pre-update-<timestamp> image tags older than the number of previous versions you chose to
keep, unused workbench images, and all but the 2 newest database dumps. Before it removes the
helper container it stores that container's log on the update run, so the detail behind a
switchover stays available under Details in the Update history card on the Updates
page. The same cleanup also runs weekly and on Clean up now. A cleanup never starts while
an update is in progress. See Docker housekeeping.
Statuses#
The install records one of these statuses as it goes, and the page shows the five working ones as the step list:
backing_up, pulling, starting_blue, health_checking, cutover_in_progress, then
success or failed.
Update runs that stop mid-way#
If an install makes no progress for ten minutes and the workflow behind it is no longer alive, for example because the host rebooted partway through, the next status check marks it failed with the message "install did not complete — no active process found, marked failed after timeout". That frees you to start a new install.