Problem
Bind mounts only go one way. When a host path is mounted over a container path that the image populates, the image content is obscured, never copied out (Docker docs: "the pre-existing files are obscured by the mount", https://docs.docker.com/engine/storage/bind-mounts/). Named volumes get copy-on-first-use semantics (https://docs.docker.com/engine/storage/volumes/), but FlatRun is bind-mounts only, so we lose that.
Today the agent just creates empty dirs before compose up (internal/docker/discovery.go:315-319) and only the parent dir for file mounts (internal/docker/discovery.go:306). No image-to-host seeding exists anywhere in the agent.
Why it matters
Current templates are unaffected: the WordPress, Nextcloud, and Ghost entrypoints all self-populate an empty mount. The gaps are:
- Single-file config mounts. Mounting
./nginx.conf:/etc/nginx/nginx.conf with no host file makes the daemon create a directory at that path ("It's always created as a directory", bind mounts doc), so the container gets a directory where a file should be and crashes.
- Arbitrary user compose files. Images whose entrypoints do not self-populate leave the user with an empty dir or a broken app.
Seeding also serves the flat-file philosophy: configs become visible and editable on the host.
Proposal
Opt-in per-mount seeding (seed: true in template metadata, and optionally a deploy-time flag). When the host path is empty or missing:
ContainerCreate the image without starting it
CopyFromContainer the container path as a tar
- Extract to the host path (tar preserves ownership and modes)
- Remove the temp container
Only seed empty targets, mirroring named-volume semantics, so existing user data is never overwritten.
Costs
One image pull plus a temp container per seeded mount; large directories double first-deploy I/O.
Problem
Bind mounts only go one way. When a host path is mounted over a container path that the image populates, the image content is obscured, never copied out (Docker docs: "the pre-existing files are obscured by the mount", https://docs.docker.com/engine/storage/bind-mounts/). Named volumes get copy-on-first-use semantics (https://docs.docker.com/engine/storage/volumes/), but FlatRun is bind-mounts only, so we lose that.
Today the agent just creates empty dirs before compose up (
internal/docker/discovery.go:315-319) and only the parent dir for file mounts (internal/docker/discovery.go:306). No image-to-host seeding exists anywhere in the agent.Why it matters
Current templates are unaffected: the WordPress, Nextcloud, and Ghost entrypoints all self-populate an empty mount. The gaps are:
./nginx.conf:/etc/nginx/nginx.confwith no host file makes the daemon create a directory at that path ("It's always created as a directory", bind mounts doc), so the container gets a directory where a file should be and crashes.Seeding also serves the flat-file philosophy: configs become visible and editable on the host.
Proposal
Opt-in per-mount seeding (
seed: truein template metadata, and optionally a deploy-time flag). When the host path is empty or missing:ContainerCreatethe image without starting itCopyFromContainerthe container path as a tarOnly seed empty targets, mirroring named-volume semantics, so existing user data is never overwritten.
Costs
One image pull plus a temp container per seeded mount; large directories double first-deploy I/O.