docker/cli · error

duplicate mount target

Error message

duplicate mount target

What it means

Thrown by updateMounts (cli/command/service/update.go:934) while indexing newly-added mounts by their Target path. If two `--mount-add` flags specify the same container destination, the second collides in the mountsByTarget map and the error fires, since a container cannot have two mounts bound to one path.

Solutions

  1. Give each mount a distinct target path.
  2. If the intent is to replace an existing mount, remove the old one first with `--mount-rm <target>` then add the new source for the same target.
  3. Dedupe the mount list by target before issuing the update.

Example fix

// before
docker service update \
  --mount-add type=bind,src=/a,target=/data \
  --mount-add type=volume,src=vol,target=/data web

// after
docker service update \
  --mount-add type=bind,src=/a,target=/data \
  --mount-add type=volume,src=vol,target=/cache web
Defensive patterns

Strategy: validation

Validate before calling

// Dedupe mount targets before building the --mount-add list.
seen := map[string]struct{}{}
for _, m := range mounts {
	if _, dup := seen[m.Target]; dup {
		return fmt.Errorf("duplicate mount target %q", m.Target)
	}
	seen[m.Target] = struct{}{}
}

Prevention

When it happens

Trigger: Passing multiple `--mount-add` with an identical `target=`/`dst=`, e.g. `docker service update --mount-add type=bind,src=/a,target=/data --mount-add type=volume,src=vol,target=/data <svc>`.

Common situations: Copy-pasting a mount flag and editing only the source while leaving the target; templating that renders the same target twice; refactoring a volume list and forgetting to dedupe destinations.

Related errors


AI-assisted analysis of docker/cli@4f84911bfe (2026-08-07). Data as JSON: /api/errors/02ce7b85b0ecd720. Report an issue: GitHub.

Appendix: source

Thrown at cli/command/service/update.go:934

	keyFunc func(string) string,
) []string {
	newSeq := []string{}
	for _, item := range seq {
		if _, exists := toRemove[keyFunc(item)]; !exists {
			newSeq = append(newSeq, item)
		}
	}
	return newSeq
}

func updateMounts(flags *pflag.FlagSet, mounts *[]mount.Mount) error {
	mountsByTarget := map[string]mount.Mount{}

	if flags.Changed(flagMountAdd) {
		values := flags.Lookup(flagMountAdd).Value.(*opts.MountOpt).Value()
		for _, mnt := range values {
			if _, ok := mountsByTarget[mnt.Target]; ok {
				return errors.New("duplicate mount target")
			}
			mountsByTarget[mnt.Target] = mnt
		}
	}

	// Add old list of mount points minus updated one.
	for _, mnt := range *mounts {
		if _, ok := mountsByTarget[mnt.Target]; !ok {
			mountsByTarget[mnt.Target] = mnt
		}
	}

	newMounts := make([]mount.Mount, 0, len(mountsByTarget))

	toRemove := buildToRemoveSet(flags, flagMountRemove)

	for _, mnt := range mountsByTarget {
		if _, exists := toRemove[mnt.Target]; !exists {

View on GitHub (pinned to 4f84911bfe)