The one-click Docker container update self-hosted workflow in Stashboard is one of its most powerful features — and the one most likely to surprise you if something goes wrong. One button press triggers a six-step pipeline: connect to the host, inspect the running container, pull the new image, stop and remove the old container, recreate it with the identical config, then verify health. This post walks every step based on the actual backend source code, explains what is and is not preserved across a recreate, and tells you when it is safe to press that button.
What Triggers a One-Click Docker Container Update Self-Hosted?
Each Docker watch in Stashboard polls a registry on a configured schedule. When a new image digest is detected, the container card displays an “Update available” badge and enables the Update now button. Clicking it launches the one-click Docker container update self-hosted process — no terminal, no manual docker pull, no SSH session needed. The same button also works as a forced re-pull when no digest change was detected, useful for :latest tags that were silently overwritten at the registry without changing the digest pointer. In that case the confirmation dialog says “re-pull the configured tag” instead of “pull the new image”, so you always know which scenario you are in.
The Complete Lifecycle: How the One-Click Docker Container Update Self-Hosted Really Runs
The orchestration lives in DockerImageUpdater.cs inside UpdateAsync(). Every step must succeed before the next one starts.
Step 1 — Connect and Inspect
Stashboard opens a connection using the transport defined on the watch’s Docker connection (Unix socket, TCP/TLS, or SSH tunnel) and immediately calls InspectContainerAsync(). This captures the current image ID and resolves its registry digest — this value becomes the PreviousDigest stored in the audit record. If the host is unreachable or the container name does not exist, the process stops here with status HostUnreachable or ContainerNotFound. The running container is never modified.
Step 2 — Choose Path: Raw Recreate or Compose
Before pulling anything, Stashboard calls ShouldUseCompose(). The Compose path activates only when all four conditions are true: the connection uses a local Unix socket, a ComposeProjectPath is configured, the container carries a com.docker.compose.service label, and the docker compose binary is reachable. If all four pass, Stashboard runs docker compose pull followed by docker compose up -d, which respects dependency ordering declared in your Compose file. Otherwise it falls back to the raw recreate path.
Step 3 — Pull the New Image
The pull runs via CreateImageAsync() with credentials resolved from the watch’s registry configuration. ⚠️ If the pull fails — wrong credentials, rate-limited, registry unreachable — the process stops and returns PullFailed. The running container is completely untouched. This is the safest possible failure mode: fix the registry issue and click again.
Step 4 — Stop and Remove the Old Container
Only after a successful pull does Stashboard stop the container (30-second graceful timeout) and remove it with Force=true, RemoveVolumes=false. The force flag handles containers stuck in a restarting loop. ⚠️ Once the remove call returns, the old container is gone. There is no snapshot, no checkpoint, and no automatic rollback. If anything fails after this point you will recover manually.
Step 5 — Recreate with the Full Original Config
This is the step that makes the one-click Docker container update self-hosted feature safe for everyday use: the new container is created by passing the original ContainerInspectResponse objects directly into CreateContainerAsync(). Because the full Config and HostConfig structs are reused, the following are preserved exactly as they were:
- Environment variables — every
-eorenv_filevalue - Bind mounts and named volumes —
HostConfig.Mountscopied verbatim; data is never touched - Port bindings — the same host ports are reused
- Restart policy —
always,unless-stopped,on-failure— all preserved - Labels — including
com.docker.compose.*labels so Compose still recognises the container after the update - Networks — the primary network is attached at create time; secondary networks are re-attached via
ReattachSecondaryNetworksAsync()after the container is created - Logging driver, security options, DNS settings — everything in
HostConfigis carried over
One thing that is deliberately not preserved: ephemeral network fields such as IPAddress, EndpointID, and Gateway. The code strips these via StripEphemeralEndpointFields() before the create call, because reusing the old container’s runtime IP would collide with the stale IPAM reservation left behind after removal.
Step 6 — Start and Verify Health
After the container is created, Stashboard starts it and enters a polling loop: up to 10 inspect calls spaced 3 seconds apart (a 30-second window by default). If the container defines a Docker HEALTHCHECK, Stashboard waits for the status to reach healthy. If no HEALTHCHECK is present, Running=true is accepted as sufficient. If the container reports unhealthy or is still in starting state when the window closes, the final status is downgraded to RecreateFailed — even though the container process itself is running. For reading logs after a problematic update, Dozzle is a practical companion tool that gives you a live log stream without opening a terminal.
The Confirmation Dialog: What It Shows and Why You Should Read It
Before any one-click Docker container update self-hosted operation runs, Stashboard shows a modal that displays the image reference and container name, describes the exact path that will be taken (raw recreate or Compose-aware recreate), and includes a bold warning that recreating a running container causes brief downtime and may require manual recovery if a step fails after the old container is removed. It also links directly to the project documentation. If you are re-pulling a tag with no detected digest change, the dialog explicitly says so — you are never left guessing whether a newer image was found.
Audit Log: Every One-Click Docker Container Update Self-Hosted Is Recorded
Every invocation — success or failure — writes a row to the DockerUpdateAttempts table. The record stores the user who triggered the action, the image reference and container name at the time of the click, the PreviousDigest and NewDigest, the final status, a human-readable error message on failure, and (since v3.2) whether health was verified and the exact timestamp when it was confirmed. You can browse this history in the “Update history” accordion on each watch card, newest first, up to 50 rows.
The five possible statuses:
- Success — container recreated and health verified (or no
HEALTHCHECKdefined) - PullFailed — image pull failed; original container was never touched
- RecreateFailed — something failed after the pull; check the error field for the exact step
- HostUnreachable — Docker daemon could not be contacted at all
- ContainerNotFound — the container name in the watch config does not exist on the host
⚠️ Risks You Need to Understand Before Clicking
The Writable Socket Requirement
The one-click Docker container update self-hosted path requires write access to the Docker socket. If you mounted it read-only (/var/run/docker.sock:/var/run/docker.sock:ro), every stop, remove, and create call will fail with a permission error and the update will return RecreateFailed immediately. You must remount the socket without the :ro flag to use the Update now button. If your security policy requires a read-only socket you will need to update containers manually. See Docker volumes and mounts explained for a refresher on mount modes and when each is appropriate.
Container Recreated but Reported Unhealthy
If the audit record shows RecreateFailed but docker ps shows the container running, the container started but failed its HEALTHCHECK within the 30-second polling window. The container keeps running according to its restart policy. Check its logs to understand why the healthcheck is failing — usually a dependency (a database, a config file, an upstream service) is not yet ready when the container starts. You can extend the HEALTHCHECK start period in your Compose file to give the service more time before checks begin.
No Automatic Rollback
Stashboard does not implement automatic rollback. If the old container has been removed and the new one fails to start correctly, recovery is manual. The PreviousDigest in the audit record tells you exactly which image was running before — use it to pin a known-good version: docker run ... image@sha256:<PreviousDigest>. This is the same situation you face when doing a manual container update — Stashboard automates the steps but not the recovery. For production-critical services on your homelab, test the new image in a staging container before triggering a one-click Docker container update self-hosted on the live instance.
When Is a One-Click Docker Container Update Self-Hosted the Right Move?
The one-click Docker container update self-hosted feature is well suited for home lab services where brief downtime is acceptable, the container is managed by a Compose file (so Stashboard picks the safer Compose path automatically), and data lives in named volumes or bind mounts rather than the container layer. It is less suitable for services with long startup sequences that are likely to trip the 30-second health window, or for containers whose configuration was assembled over time with manual docker exec changes that are not captured in environment variables or mounted config files. If you use digest-pinned hardened images, the PreviousDigest in the audit log gives you a precise rollback target if the new image turns out to be broken.
Conclusion
The one-click Docker container update self-hosted button in Stashboard is not magic — it is a disciplined sequence of Docker API calls that preserves every piece of your original container config while swapping in a fresh image. When it works, it saves you from opening a terminal. When it fails, the audit log tells you exactly where things went wrong and what digest to pin for manual recovery. Understand the lifecycle, read the confirmation dialog, keep your socket writable, and the one-click Docker container update self-hosted workflow becomes a reliable and auditable part of your homelab maintenance routine.
Stashboard is open source — you can find the full source code on GitHub and pull the ready-to-run image directly from Docker Hub. Issues, PRs, and stars are always welcome. 🙌
