Description
When a volumes: entry uses a relative host path (e.g. ../data:/data), container-compose resolves it relative to the process's current working directory instead of the compose file's directory. This differs from Docker Compose's documented behavior, where relative host paths in volumes: resolve relative to the location of the compose file — which is also the base this codebase already uses for build.context and env_file.
Steps to reproduce
Directory layout:
project/
├── data/
│ └── marker.txt
└── deploy/
└── docker-compose.yml
deploy/docker-compose.yml:
services:
app:
image: alpine
command: ["sleep", "infinity"]
volumes: ["../data:/data"]
Run from the project root (one level above deploy/):
cd project
container-compose -f deploy/docker-compose.yml up -d app
container exec app ls /data
Expected behavior
Same as docker compose: ../data resolves relative to deploy/docker-compose.yml's directory → project/data → /data inside the container contains marker.txt.
Actual behavior
container-compose resolves ../data relative to the process CWD (project/) instead, landing one level too high (e.g. a sibling of project/, or $HOME/data if invoked from a directory two levels under $HOME). Since that path usually doesn't exist, container-compose silently creates it and mounts an empty directory rather than failing loudly — /data inside the container is empty instead of containing marker.txt.
The bug disappears if you cd deploy && container-compose -f docker-compose.yml up (CWD == compose file's directory), which confirms the path base is the issue, not the volume syntax.
Root cause
In Sources/Container-Compose/Helper Functions.swift, composeVolumeToRunArgs resolves the bind-mount source with:
let fullHostPath = resolvedPath(for: source, relativeTo: URL(fileURLWithPath: cwd, isDirectory: true))
where cwd (ComposeProject.swift) is:
public var cwd: String { process.cwd ?? fileManager.currentDirectoryPath }
— the process's working directory (or an explicit --cwd/--workdir override, which per container-compose help up sets the container's internal working directory, not a host-side compose base path).
This is inconsistent with how the same codebase resolves other compose-relative paths: build.context (ComposeUp.swift:1330) and env_file (ComposeUp.swift:915) are both correctly resolved relative to composeDirectory (the compose file's own directory). Only the volumes: bind-mount path uses the wrong base.
Suggested fix
Resolve relative bind-mount sources in composeVolumeToRunArgs relative to composeDirectory (matching build.context/env_file), not cwd. cwd/--workdir can keep its documented meaning (container-internal workdir).
Environment
- container-compose 1.1.0 (also reproduced against a fresh shallow clone of
main — same code path present)
- container CLI 1.2.2
- macOS 26.6.1 (Apple Silicon)
Description
When a
volumes:entry uses a relative host path (e.g.../data:/data),container-composeresolves it relative to the process's current working directory instead of the compose file's directory. This differs from Docker Compose's documented behavior, where relative host paths involumes:resolve relative to the location of the compose file — which is also the base this codebase already uses forbuild.contextandenv_file.Steps to reproduce
Directory layout:
deploy/docker-compose.yml:Run from the project root (one level above
deploy/):Expected behavior
Same as
docker compose:../dataresolves relative todeploy/docker-compose.yml's directory →project/data→/datainside the container containsmarker.txt.Actual behavior
container-composeresolves../datarelative to the process CWD (project/) instead, landing one level too high (e.g. a sibling ofproject/, or$HOME/dataif invoked from a directory two levels under$HOME). Since that path usually doesn't exist,container-composesilently creates it and mounts an empty directory rather than failing loudly —/datainside the container is empty instead of containingmarker.txt.The bug disappears if you
cd deploy && container-compose -f docker-compose.yml up(CWD == compose file's directory), which confirms the path base is the issue, not the volume syntax.Root cause
In
Sources/Container-Compose/Helper Functions.swift,composeVolumeToRunArgsresolves the bind-mount source with:where
cwd(ComposeProject.swift) is:— the process's working directory (or an explicit
--cwd/--workdiroverride, which percontainer-compose help upsets the container's internal working directory, not a host-side compose base path).This is inconsistent with how the same codebase resolves other compose-relative paths:
build.context(ComposeUp.swift:1330) andenv_file(ComposeUp.swift:915) are both correctly resolved relative tocomposeDirectory(the compose file's own directory). Only thevolumes:bind-mount path uses the wrong base.Suggested fix
Resolve relative bind-mount sources in
composeVolumeToRunArgsrelative tocomposeDirectory(matchingbuild.context/env_file), notcwd.cwd/--workdircan keep its documented meaning (container-internal workdir).Environment
main— same code path present)