docker/cli · error

network is declared as external, but it is not in the right…

Error message

network %q is declared as external, but it is not in the right scope: %q instead of "swarm"

What it means

Raised by validateExternalNetworks when an external network exists but its Scope is not 'swarm' (typically it is 'local', e.g. a bridge or host network). Swarm services can only attach to swarm-scoped (overlay) networks, so a local-scoped network is rejected even though it exists.

Solutions

  1. Recreate the network as overlay (swarm-scoped): `docker network rm foo && docker network create -d overlay foo`.
  2. Use a swarm-scoped driver like overlay for any network referenced by Swarm services.
  3. Verify scope before deploy: `docker network inspect foo --format '{{.Scope}}'` should print 'swarm'.

Example fix

# before
docker network create shared        # creates local bridge
# after
docker network create -d overlay shared  # creates swarm-scoped overlay
Defensive patterns

Strategy: validation

Validate before calling

// Verify scope of each external network before deploy
for _, name := range externalNetworks {
    res, err := c.NetworkInspect(ctx, name, client.NetworkInspectOptions{})
    if err != nil { return err }
    if res.Network.Scope != "swarm" {
        return fmt.Errorf("network %q scope %q; recreate as overlay", name, res.Network.Scope)
    }
}

Prevention

When it happens

Trigger: Declaring `networks: { foo: { external: true } }` where 'foo' was created with a local-scope driver (bridge, host, macvlan in local scope, or default bridge). NetworkInspect succeeds (deploy_composefile.go:97) but res.Network.Scope != "swarm" at line 103.

Common situations: Creating the network with `docker network create foo` (defaults to bridge/local) instead of `-d overlay`; reusing a network built for non-Swarm compose; a network created by `docker-compose` (local) being referenced from a Swarm stack.

Related errors


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

Appendix: source

Thrown at cli/command/stack/deploy_composefile.go:104

	}
	return serviceNetworks
}

func validateExternalNetworks(ctx context.Context, apiClient client.NetworkAPIClient, externalNetworks []string) error {
	for _, networkName := range externalNetworks {
		if !container.NetworkMode(networkName).IsUserDefined() {
			// Networks that are not user defined always exist on all nodes as
			// local-scoped networks, so there's no need to inspect them.
			continue
		}
		res, err := apiClient.NetworkInspect(ctx, networkName, client.NetworkInspectOptions{})
		switch {
		case errdefs.IsNotFound(err):
			return fmt.Errorf("network %q is declared as external, but could not be found. You need to create a swarm-scoped network before the stack is deployed", networkName)
		case err != nil:
			return err
		case res.Network.Scope != "swarm":
			return fmt.Errorf("network %q is declared as external, but it is not in the right scope: %q instead of \"swarm\"", networkName, res.Network.Scope)
		}
	}
	return nil
}

func createSecrets(ctx context.Context, dockerCLI command.Cli, secrets []swarm.SecretSpec) error {
	apiClient := dockerCLI.Client()

	for _, secretSpec := range secrets {
		res, err := apiClient.SecretInspect(ctx, secretSpec.Name, client.SecretInspectOptions{})
		switch {
		case err == nil:
			// secret already exists, then we update that
			_, err := apiClient.SecretUpdate(ctx, res.Secret.ID, client.SecretUpdateOptions{
				Version: res.Secret.Meta.Version,
				Spec:    secretSpec,
			})
			if err != nil {

View on GitHub (pinned to 4f84911bfe)