docker/compose · error
copying between services is not supported
Error message
copying between services is not supported
What it means
The Compose copy API (docker compose cp) only supports copying between the host and containers of one service, in one direction. The source and destination arguments are parsed as SERVICE:PATH; when BOTH sides carry a service prefix, the direction bitmask becomes fromService|toService (acrossServices) and the copy is rejected before any container is contacted. This is an intentional limitation: Compose has no primitive to stream an archive directly between two service containers.
Source
Thrown at pkg/compose/cp.go:70
projectName = strings.ToLower(projectName)
srcService, srcPath := splitCpArg(options.Source)
destService, dstPath := splitCpArg(options.Destination)
var direction copyDirection
var serviceName string
var copyFunc func(ctx context.Context, containerID string, srcPath string, dstPath string, opts api.CopyOptions) error
if srcService != "" {
direction |= fromService
serviceName = srcService
copyFunc = s.copyFromContainer
}
if destService != "" {
direction |= toService
serviceName = destService
copyFunc = s.copyToContainer
}
if direction == acrossServices {
return errors.New("copying between services is not supported")
}
if direction == 0 {
return errors.New("unknown copy direction")
}
containers, err := s.listContainersTargetedForCopy(ctx, projectName, options, direction, serviceName)
if err != nil {
return err
}
g := errgroup.Group{}
for _, cont := range containers {
ctr := cont
g.Go(func() error {
name := getCanonicalContainerName(ctr)
var msg string
if direction == fromService {View on GitHub (pinned to ddc4b044b6)
Solutions
- Copy in two steps: `docker compose cp svc_a:/path ./tmpfile` then `docker compose cp ./tmpfile svc_b:/path`
- If both services can share the file, mount the same volume or bind directory into both services and write to it instead of copying
- Use `docker compose exec svc_a sh -c 'tar c ...' | docker compose exec -T svc_b tar x ...` for streaming pipelines in scripts
- If you called the Go API, run two Copy calls (one fromService, one toService) with an intermediate host path
Example fix
# before docker compose cp db:/dump.sql app:/dump.sql # after docker compose cp db:/dump.sql ./dump.sql && docker compose cp ./dump.sql app:/dump.sql
Defensive patterns
Strategy: validation
Validate before calling
# before calling compose cp, ensure exactly one side has a service prefix
cp_args_check() {
case "$1" in *:*) src_svc=1;; *) src_svc=0;; esac
case "$2" in *:*) dst_svc=1;; *) dst_svc=0;; esac
[ $((src_svc + dst_svc)) -eq 1 ] || { echo "need exactly one SERVICE: path" >&2; return 2; }
} Prevention
- Wrap compose cp in a script helper that rejects two-sided service:service arguments up front
- Standardize on host-staging copies in pipelines rather than service-to-service assumptions
When it happens
Trigger: Calling docker compose cp svc_a:/path svc_b:/path (or api.ComposeService.Copy with Source='svc_a:/file' and Destination='svc_b:/file'), i.e. both splitCpArg results yield a non-empty service name.
Common situations: Trying to move files between two services in one command (e.g. dumping a database from the db service into the app service), scripting backups where source and target are both containers, or assuming compose cp mirrors docker cp semantics (docker cp also cannot container->container, so users coming from scp expect it to work).
Related errors
- unknown copy direction
- source can not be empty
- destination can not be empty
- invalid filter '${filter}'
- --index requires one service to be selected
AI-assisted analysis of docker/compose@ddc4b044b6 (2026-08-15).
Data as JSON: /api/errors/fcd06d8729370c2d.
Report an issue: GitHub.