You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
Three open issues change what a workloads.*.volumes[] entry means, and each was
filed against a different symptom. They share one substrate, so deciding the
entry shape once is cheaper than touching that schema node three times.
#141 and #142 both add properties to the same node. #148 is a defect in the
planner and retention, and is included here because it is the third thing that
turns on what a volume entry means — a mount that is release-scoped versus one
that is external host state.
hasReleasePathDependency (internal/onebox/workload_plan.go:139) marks any
workload with a relative bind non-retainable, and that case is evaluated above
the revision comparison (workload_plan.go:70), so retain is unreachable for
those workloads. feat(deploy): retain workloads whose relative bind-mount content is unchanged #148's reproduction is accurate.
RetentionCandidates (internal/release/retention.go:80) protects evidence
ids, active schedule leases, the current release and its predecessor chain, and
the activation and secret checkpoints. It never inspects live container mounts,
so feat(deploy): retain workloads whose relative bind-mount content is unchanged #148's "hard part" is real: a container retained past the chain would mount a
pruned release directory.
A volume entry today has exactly mode, name, path, source. mode is the rw/ro enum and a relative source is schema-forced to ro.
backup: exists only under a services entry that declares a driver
(properties/services/additionalProperties/anyOf[1]), and its implementation is
wal-g and PostgreSQL specific (internal/engine/backup_postgres.go, backup_postgres_ops.go, and the renderService wiring in internal/app/services.go:350). There is no driver-neutral copy engine to reuse.
The entry shape, decided once.#141 proposes host_path, uid, gid and mode. host_path duplicates the existing source, and mode collides with the
existing rw/ro enum on the same node. A nested block — for example host: {uid, gid, mode} — leaves the existing keys alone and gives #142 a place
to sit beside it. Whatever is chosen, both children should be written against it
before either is implemented.
Which volumes backup: can attach to.#142's proposal attaches it to an
absolute bind source. Sampling the config corpus, most durable workload state is
in named volumes rather than binds — ext-gitea (data), ext-immich
(upload), ext-n8n (storage), ext-plausible (data), ext-paperless
(pgdata, redisdata). A backup contract that reaches only bind sources would
miss the common case. This is also a partial answer to #142's own open question
about whether one application's inventory is representative.
Whether #142 is taken at all. It moves a documented product boundary and
needs a copy engine that does not exist yet. That is a product decision, not a
backlog pull, and it should be settled before the schema work in #141 assumes it.
Where a declared-path check runs.#141's comment proposes the shared host
prerequisite gate. RequireHostPrerequisites
(internal/app/prerequisites.go:182) takes only a Runner and asserts three
constant Docker commands, so project-declared paths need a sibling gate called
from the same three sites — internal/engine/bootstrap.go:83, internal/app/preflight.go:84 and internal/engine/preflight.go:33 — rather than
a widened signature.
Why these are tracked together
Three open issues change what a
workloads.*.volumes[]entry means, and each wasfiled against a different symptom. They share one substrate, so deciding the
entry shape once is cheaper than touching that schema node three times.
provisioning metadata (owner, group, permissions) to an absolute bind source.
backup:on workload volumes. Adds a backup contract to a volumeholding durable state.
Not a schema change; it makes the planner test the content behind a relative
bind rather than its shape, so an unchanged workload stops being recreated on
every deploy.
#141 and #142 both add properties to the same node. #148 is a defect in the
planner and retention, and is included here because it is the third thing that
turns on what a volume entry means — a mount that is release-scoped versus one
that is external host state.
Verified state on
main(checked at 5e03da0)hasReleasePathDependency(internal/onebox/workload_plan.go:139) marks anyworkload with a relative bind non-retainable, and that case is evaluated above
the revision comparison (
workload_plan.go:70), soretainis unreachable forthose workloads. feat(deploy): retain workloads whose relative bind-mount content is unchanged #148's reproduction is accurate.
RetentionCandidates(internal/release/retention.go:80) protects evidenceids, active schedule leases, the current release and its predecessor chain, and
the activation and secret checkpoints. It never inspects live container mounts,
so feat(deploy): retain workloads whose relative bind-mount content is unchanged #148's "hard part" is real: a container retained past the chain would mount a
pruned release directory.
mode,name,path,source.modeis therw/roenum and a relative source is schema-forced toro.backup:exists only under aservicesentry that declares a driver(
properties/services/additionalProperties/anyOf[1]), and its implementation iswal-g and PostgreSQL specific (
internal/engine/backup_postgres.go,backup_postgres_ops.go, and therenderServicewiring ininternal/app/services.go:350). There is no driver-neutral copy engine to reuse.ob doctoralready names the gap Allow backup: on workload volumes, not only managed services #142 describes(
cmd/ob/doctor.go:496), anddocs/product.md:53lists workload-volume backupsunder "Not owned today".
Decisions that belong here rather than in a child
The entry shape, decided once. #141 proposes
host_path,uid,gidandmode.host_pathduplicates the existingsource, andmodecollides with theexisting
rw/roenum on the same node. A nested block — for examplehost: {uid, gid, mode}— leaves the existing keys alone and gives #142 a placeto sit beside it. Whatever is chosen, both children should be written against it
before either is implemented.
Which volumes
backup:can attach to. #142's proposal attaches it to anabsolute bind source. Sampling the config corpus, most durable workload state is
in named volumes rather than binds —
ext-gitea(data),ext-immich(
upload),ext-n8n(storage),ext-plausible(data),ext-paperless(
pgdata,redisdata). A backup contract that reaches only bind sources wouldmiss the common case. This is also a partial answer to #142's own open question
about whether one application's inventory is representative.
Whether #142 is taken at all. It moves a documented product boundary and
needs a copy engine that does not exist yet. That is a product decision, not a
backlog pull, and it should be settled before the schema work in #141 assumes it.
Where a declared-path check runs. #141's comment proposes the shared host
prerequisite gate.
RequireHostPrerequisites(
internal/app/prerequisites.go:182) takes only aRunnerand asserts threeconstant Docker commands, so project-declared paths need a sibling gate called
from the same three sites —
internal/engine/bootstrap.go:83,internal/app/preflight.go:84andinternal/engine/preflight.go:33— rather thana widened signature.
Suggested order
platform provisioned.
production cost, so it need not wait on Allow backup: on workload volumes, not only managed services #142.
Related, not part of this