Skip to content

Relative bind-mount volume paths resolve against process CWD instead of compose file directory #147

Description

@bworld666

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)

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions