Problem
freshdock currently updates containers individually. Docker Compose projects,
however, contain relationships between containers that make an isolated update
unsafe. Two failure modes I have hit in production:
1. One-shot init/migrate containers are skipped.
A typical stack looks like this:
yaml
migrate:
image: app:latest
command: ./bin/run-migrate.sh
restart: "no"
web:
image: app:latest
depends_on:
migrate:
condition: service_completed_successfully
migrate sits in exited state after a successful run. freshdock pulls a new
app:latest and recreates web, but leaves the exited migrate container
untouched. Result: new application code against an old database schema.
Observed on a GlitchTip stack — the app came up "healthy" but every login
failed with column ..._organizationsocialapp.is_public does not exist, and
the ingest endpoint threw IndexError: tuple index out of range on a raw SQL
row unpack. Nothing crashed, so no health-based guard would have caught it.
2. Dependent containers are not restarted.
Already tracked separately for network_mode: container:X (see the
gluetun/qBittorrent issue), but it is the same root cause: freshdock has no
notion of a container group. depends_on is the Compose-native version of the
same relationship.
Why this is tractable
Compose already writes the entire dependency graph into container labels, so
there is no YAML to parse and no need to locate the source file:
Label | Contains
-- | --
com.docker.compose.project | project name — groups containers
com.docker.compose.service | service name within the project
com.docker.compose.depends_on | e.g. migrate:service_completed_successfully:true
com.docker.compose.container-number | replica index
com.docker.compose.project.config_files | path(s) to the compose file(s)
com.docker.compose.project.working_dir | project directory
Note that docker ps must be listed with all: true for one-shot containers
to show up at all — that is likely the concrete bug behind failure mode 1.
Proposed behaviour
When an image update applies to a container that carries
com.docker.compose.project:
- Collect all containers in that project (including stopped/exited ones)
that reference the updated image.
- Build a DAG from
com.docker.compose.depends_on across the whole project.
- Stop dependents, recreate the updated containers, then start in topological
order.
- Honour the dependency conditions:
service_completed_successfully — run and block until exit code 0. On a
non-zero exit, abort the rollout, leave dependents stopped, and report.
service_healthy — wait for the healthcheck to pass, with a timeout.
service_started — proceed immediately.
- Log per-project rather than per-container, so the operator sees one
coherent rollout.
Simpler alternative
Detect the Compose project and shell out to
docker compose --project-directory <working_dir> up -d after pulling. Far
less code, and Compose does the ordering natively. Downsides: requires the
compose binary and filesystem access to the config files, which is not
available when freshdock runs inside a container against the socket only. Might
be worth supporting as an opt-in mode
(freshdock.mode=compose) alongside the native implementation.
Related: a notify-only mode
Orthogonal but relevant. Even with correct ordering, an application update can
introduce a breaking change that does not crash — a config key that has been
renamed, a removed entrypoint script, a changed default port. The GlitchTip
case above is exactly that: the container was healthy and the feature was
broken.
For stateful applications, "tell me an update exists, do not apply it" is often
the behaviour you actually want. Something like:
yaml
labels:
- "freshdock.enable=true"
- "freshdock.mode=notify"
This gives users a middle ground between full auto-update and removing the
label entirely, which is currently the only workaround.
Suggested scope
The one-shot container fix (all: true when listing, plus honouring
service_completed_successfully) is small and closes the sharpest edge on its
own. Full DAG ordering can follow as a second PR.
Problem
freshdock currently updates containers individually. Docker Compose projects, however, contain relationships between containers that make an isolated update unsafe. Two failure modes I have hit in production:
1. One-shot init/migrate containers are skipped.
A typical stack looks like this:
migratesits inexitedstate after a successful run. freshdock pulls a newapp:latestand recreatesweb, but leaves the exitedmigratecontainer untouched. Result: new application code against an old database schema.Observed on a GlitchTip stack — the app came up "healthy" but every login failed with
column ..._organizationsocialapp.is_public does not exist, and the ingest endpoint threwIndexError: tuple index out of rangeon a raw SQL row unpack. Nothing crashed, so no health-based guard would have caught it.2. Dependent containers are not restarted.
Already tracked separately for
network_mode: container:X(see the gluetun/qBittorrent issue), but it is the same root cause: freshdock has no notion of a container group.depends_onis the Compose-native version of the same relationship.Why this is tractable
Compose already writes the entire dependency graph into container labels, so there is no YAML to parse and no need to locate the source file:
Note that
docker psmust be listed withall: truefor one-shot containers to show up at all — that is likely the concrete bug behind failure mode 1.Proposed behaviour
When an image update applies to a container that carries
com.docker.compose.project:com.docker.compose.depends_onacross the whole project.service_completed_successfully— run and block until exit code 0. On a non-zero exit, abort the rollout, leave dependents stopped, and report.service_healthy— wait for the healthcheck to pass, with a timeout.service_started— proceed immediately.Simpler alternative
Detect the Compose project and shell out to
docker compose --project-directory <working_dir> up -dafter pulling. Far less code, and Compose does the ordering natively. Downsides: requires the compose binary and filesystem access to the config files, which is not available when freshdock runs inside a container against the socket only. Might be worth supporting as an opt-in mode (freshdock.mode=compose) alongside the native implementation.Related: a notify-only mode
Orthogonal but relevant. Even with correct ordering, an application update can introduce a breaking change that does not crash — a config key that has been renamed, a removed entrypoint script, a changed default port. The GlitchTip case above is exactly that: the container was healthy and the feature was broken.
For stateful applications, "tell me an update exists, do not apply it" is often the behaviour you actually want. Something like:
This gives users a middle ground between full auto-update and removing the label entirely, which is currently the only workaround.
Suggested scope
The one-shot container fix (
all: truewhen listing, plus honouringservice_completed_successfully) is small and closes the sharpest edge on its own. Full DAG ordering can follow as a second PR.