{"record":{"id":"5b35665d34f9e2b0","repo":"hashicorp/nomad","slug":"defaulting-advertise-to-localhost-is-unsafe-pleas","errorCode":null,"errorMessage":"Defaulting advertise to localhost is unsafe, please set advertise manually","messagePattern":"Defaulting advertise to localhost is unsafe, please set advertise manually","errorType":"validation","errorClass":null,"httpStatus":null,"severity":"error","filePath":"command/agent/config.go","lineNumber":2566,"sourceCode":"\t}\n\n\t// Fallback to bind address first, and then try resolving the local hostname\n\tips, err := net.LookupIP(bind)\n\tif err != nil {\n\t\treturn \"\", fmt.Errorf(\"Error resolving bind address %q: %v\", bind, err)\n\t}\n\n\t// Return the first non-localhost unicast address\n\tfor _, ip := range ips {\n\t\tif ip.IsLinkLocalUnicast() || ip.IsGlobalUnicast() {\n\t\t\treturn normalizeAddrWithPort(ip.String(), defport), nil\n\t\t}\n\t\tif ip.IsLoopback() {\n\t\t\tif dev {\n\t\t\t\t// loopback is fine for dev mode\n\t\t\t\treturn normalizeAddrWithPort(ip.String(), defport), nil\n\t\t\t}\n\t\t\treturn \"\", fmt.Errorf(\"Defaulting advertise to localhost is unsafe, please set advertise manually\")\n\t\t}\n\t}\n\n\t// Bind is not localhost but not a valid advertise IP, use first private IP\n\taddr, err = listenerutil.ParseSingleIPTemplate(\"{{ GetPrivateIP }}\")\n\tif err != nil {\n\t\treturn \"\", fmt.Errorf(\"Unable to parse default advertise address: %v\", err)\n\t}\n\treturn normalizeAddrWithPort(addr, defport), nil\n}\n\n// isMissingPort returns true if an error is a \"missing port\" error from\n// net.SplitHostPort.\nfunc isMissingPort(err error) bool {\n\t// matches error const in net/ipsock.go\n\tconst missingPort = \"missing port in address\"\n\treturn err != nil && strings.Contains(err.Error(), missingPort)\n}","sourceCodeStart":2548,"sourceCodeEnd":2584,"githubUrl":"https://github.com/hashicorp/nomad/blob/482b49bf1aec006f089bcfc7e632d8f6ac303e5e/command/agent/config.go#L2548-L2584","documentation":"When falling back to the bind address, if the only IP found is loopback and the agent is not in dev mode, Consul refuses to default advertise to localhost because other nodes could not reach it. This is a deliberate safety error, not a bug.","triggerScenarios":"advertise unset, bind resolves only to 127.0.0.1/::1 (e.g. bind_addr not set while networking is loopback-only, or bind explicitly \"127.0.0.1\"), and the agent is started without -dev.","commonSituations":"Running a server/agent in a container with only the loopback interface up, forgetting bind_addr in production config, or copying a dev-mode config to a real deployment.","solutions":["Set advertise_reconnect_timeout... i.e. set advertise explicitly to the node's routable IP","Set bind_addr to a non-loopback interface address (e.g. the private IP or 0.0.0.0)","Use -dev mode only if this is truly a local development agent","Bring up a non-loopback network interface on the host"],"exampleFix":"// before\nbind_addr = \"127.0.0.1\" // prod server\n// after\nbind_addr = \"0.0.0.0\"\nadvertise_addr = \"10.0.0.5\"","handlingStrategy":"validation","validationCode":"if !dev && cfg.Advertise == \"\" {\n    ip := firstNonLoopbackLocalIP()\n    if ip == nil {\n        return errors.New(\"no non-loopback IP; refusing to start without advertise\")\n    }\n}","typeGuard":null,"tryCatchPattern":"parsed, err := net.ParseIP(candidate)\nif err != nil || parsed.IsLoopback() {\n    return errors.New(\"advertise must be a non-loopback routable IP\")\n}","preventionTips":["Never leave advertise unset outside dev mode","Verify the host has a non-loopback interface before starting Consul","Don't reuse dev-mode configs for servers","Assert at boot that the chosen advertise IP is non-loopback"],"tags":["network","config","consul","loopback","advertise-address"],"backgroundTag":"loopback-advertise-unsafe","analyzedSha":"482b49bf1aec006f089bcfc7e632d8f6ac303e5e","analyzedAt":"2026-09-04T07:54:14.808Z","contentChangedAt":"2026-09-04T07:54:14.808Z","schemaVersion":2},"datasetVersion":"2026-09-08T10:18:20.063Z"}