{"record":{"id":"721c94e8066f8730","repo":"nektos/act","slug":"links-are-only-supported-for-user-defined-networks","errorCode":null,"errorMessage":"links are only supported for user-defined networks","messagePattern":"links are only supported for user-defined networks","errorType":"validation","errorClass":null,"httpStatus":null,"severity":"error","filePath":"pkg/container/docker_cli.go","lineNumber":875,"sourceCode":"\tif copts.macAddress != \"\" {\n\t\tn.MacAddress = copts.macAddress\n\t}\n\tif copts.linkLocalIPs.Len() > 0 {\n\t\tn.LinkLocalIPs = toNetipAddrSlice(copts.linkLocalIPs.GetSlice())\n\t}\n\treturn nil\n}\n\nfunc parseNetworkAttachmentOpt(ep opts.NetworkAttachmentOpts) (*network.EndpointSettings, error) {\n\tif strings.TrimSpace(ep.Target) == \"\" {\n\t\treturn nil, errors.New(\"no name set for network\")\n\t}\n\tif !container.NetworkMode(ep.Target).IsUserDefined() {\n\t\tif len(ep.Aliases) > 0 {\n\t\t\treturn nil, errors.New(\"network-scoped aliases are only supported for user-defined networks\")\n\t\t}\n\t\tif len(ep.Links) > 0 {\n\t\t\treturn nil, errors.New(\"links are only supported for user-defined networks\")\n\t\t}\n\t}\n\n\tepConfig := &network.EndpointSettings{\n\t\tGwPriority: ep.GwPriority,\n\t}\n\tepConfig.Aliases = append(epConfig.Aliases, ep.Aliases...)\n\tif len(ep.DriverOpts) > 0 {\n\t\tepConfig.DriverOpts = make(map[string]string)\n\t\tepConfig.DriverOpts = ep.DriverOpts\n\t}\n\tif len(ep.Links) > 0 {\n\t\tepConfig.Links = ep.Links\n\t}\n\tif ep.IPv4Address.IsValid() || ep.IPv6Address.IsValid() || len(ep.LinkLocalIPs) > 0 {\n\t\tepConfig.IPAMConfig = &network.EndpointIPAMConfig{\n\t\t\tIPv4Address:  ep.IPv4Address,\n\t\t\tIPv6Address:  ep.IPv6Address,","sourceCodeStart":857,"sourceCodeEnd":893,"githubUrl":"https://github.com/nektos/act/blob/4f411281417e88660bea1c1a1749aa71ae0bd60f/pkg/container/docker_cli.go#L857-L893","documentation":"Thrown by parseNetworkAttachmentOpt when the container options string declares links (--network name:alias or --link style link entries parsed into ep.Links) against a network mode that is not user-defined. Docker links are a legacy feature that only function on networks you created yourself; they are meaningless on 'host', 'none', the predefined 'bridge', or 'default'. The parser therefore rejects the combination before ever contacting the daemon.","triggerScenarios":"A workflow container options string like 'options: --network bridge --link redis:redis' where parsing yields a Links list on a non-user-defined target, or '--network host:alias(link)'. Any NetworkAttachmentOpts whose Target fails IsUserDefined() while ep.Links is non-empty.","commonSituations":"Porting legacy docker-compose v1 style 'links:' blocks into act container options; combining '--network host' (often set for performance or to reach host services) with a copied '--link' flag; misunderstanding that links are deprecated in favor of user-defined network DNS.","solutions":["Drop the --link flag; on user-defined networks (including act's default per-run network) services are reachable by service name via DNS.","If you genuinely need legacy links, create a user-defined network first ('docker network create act-net') and use '--network act-net' plus the link.","Replace link-based hostnames with the service name already registered by act's service containers.","Audit the options string for stale compose-v1 syntax when migrating a workflow."],"exampleFix":"# before\ncontainer:\n  image: node:20\n  options: --network host --link myredis:redis\n\n# after\ncontainer:\n  image: node:20\n  options: --network host\nservices:\n  redis:\n    image: redis:7","handlingStrategy":"validation","validationCode":"package main\n\nimport (\n\t\"fmt\"\n\t\"strings\"\n)\n\nfunc validateNoLinksOnBuiltinNetwork(options string) error {\n\tfields := strings.Fields(options)\n\tbuiltin := false\n\tfor _, f := range fields {\n\t\tv := strings.TrimPrefix(strings.TrimPrefix(f, \"--network\"), \"=\")\n\t\tif strings.HasPrefix(f, \"--network\") && !strings.ContainsAny(v, \":.\") {\n\t\t\tswitch v {\n\t\t\tcase \"host\", \"bridge\", \"none\", \"default\":\n\t\t\t\tbuiltin = true\n\t\t\t}\n\t\t}\n\t\tif f == \"--link\" || strings.HasPrefix(f, \"--link=\") {\n\t\t\tif builtin {\n\t\t\t\treturn fmt.Errorf(\"--link cannot be combined with a built-in network\")\n\t\t\t}\n\t\t}\n\t}\n\treturn nil\n}","typeGuard":null,"tryCatchPattern":"err := runPlan(plan)\nif err != nil && strings.Contains(err.Error(), \"links are only supported for user-defined networks\") {\n    // drop legacy --link flags and rely on service-name DNS, then retry\n    cfg.ContainerOptions = stripFlag(cfg.ContainerOptions, \"--link\")\n    err = runPlan(plan)\n}","preventionTips":["Prefer DNS-based service discovery on user-defined networks over legacy links.","Remove compose-v1 'links:' syntax when porting workflows to act container options.","Keep act's default per-run network instead of overriding --network unless required."],"tags":["docker","network","links","legacy","configuration"],"backgroundTag":null,"analyzedSha":"4f411281417e88660bea1c1a1749aa71ae0bd60f","analyzedAt":"2026-08-15T09:19:46.307Z","schemaVersion":2},"datasetVersion":"2026-08-15T22:17:37.221Z"}