docker/cli · error
service is already attached to network
Error message
service is already attached to network %s
What it means
Raised by docker service update when --network-add targets a network the service is already attached to. The updateNetworks function resolves the network name to an ID and checks it against existingNetworks; a duplicate would create a redundant attachment, so the CLI rejects it rather than silently no-op.
Solutions
- Verify the service's current networks with `docker service inspect <service>` (look at Spec.TaskTemplate.Networks) and skip networks already attached.
- Use a different network name/id, or omit the --network-add for the duplicate.
- Make your automation idempotent: diff desired vs. existing networks before invoking update.
- If the intent is to change attachment options (aliases), remove and re-add with --network-rm then --network-add.
Example fix
// before
docker service update --network-add mynet myservice # myservice already on mynet
// after
docker service inspect myservice --format '{{json .Spec.TaskTemplate.Networks}}'
# then only add networks not already present Defensive patterns
Strategy: validation
Validate before calling
// Before calling docker service update --network-add, check current attachments
// Pseudocode using the daemon API:
func desiredNetworksToAdd(existing []string, desired []string) []string {
have := map[string]struct{}{}
for _, n := range existing {
have[n] = struct{}{}
}
var add []string
for _, d := range desired {
if _, ok := have[d]; !ok {
add = append(add, d)
}
}
return add // pass these (and only these) to --network-add
} Try / catch
// Treat the duplicate as a no-op success in automation
err := svcUpdate(ctx, addNetworks)
if err != nil && strings.Contains(err.Error(), "already attached to network") {
// idempotent: the desired state is already satisfied
return nil
} Prevention
- Diff desired vs. existing networks from service inspect before issuing update.
- Treat 'already attached' as an idempotent success in wrappers.
- Avoid re-running raw add commands; reconcile against current state.
When it happens
Trigger: Running `docker service update --network-add <name-or-id> <service>` where <name-or-id> resolves (via resolveNetworkID) to the same ID as a network already present in spec.TaskTemplate.Networks. The check is at update.go:1342 inside the flagNetworkAdd branch.
Common situations: Running the same --network-add command twice (e.g. from an idempotent-but-not-reconciled deploy script); adding a network by an alias/driver-specific name that resolves to an already-attached ID; copy-pasting a network-add line during troubleshooting.
Related errors
- -- conflicts with --health-* options
- network is declared as external, but could not be found…
- network is declared as external, but it is not in the right…
- failed to create network
- every ip-range or gateway must have a corresponding subnet
AI-assisted analysis of docker/cli@4f84911bfe (2026-08-07).
Data as JSON: /api/errors/8757afe109cab046.
Report an issue: GitHub.
Appendix: source
Thrown at cli/command/service/update.go:1343
for _, nw := range specNetworks {
if _, exists := idsToRemove[nw.Target]; exists {
continue
}
newNetworks = append(newNetworks, nw)
existingNetworks[nw.Target] = struct{}{}
}
if flags.Changed(flagNetworkAdd) {
values := flags.Lookup(flagNetworkAdd).Value.(*opts.NetworkOpt)
networks := convertNetworks(*values)
for _, nw := range networks {
nwID, err := resolveNetworkID(ctx, apiClient, nw.Target)
if err != nil {
return err
}
if _, exists := existingNetworks[nwID]; exists {
return fmt.Errorf("service is already attached to network %s", nw.Target)
}
nw.Target = nwID
newNetworks = append(newNetworks, nw)
existingNetworks[nw.Target] = struct{}{}
}
}
sort.Slice(newNetworks, func(i, j int) bool {
return newNetworks[i].Target < newNetworks[j].Target
})
spec.TaskTemplate.Networks = newNetworks
return nil
}
// updateCredSpecConfig updates the value of the credential spec Config field
// to the config ID if the credential spec has changed. it mutates the passed
// spec. it does not handle the case where the credential spec specifies aView on GitHub (pinned to 4f84911bfe)