{"record":{"id":"f29ce6283947ab72","repo":"hashicorp/nomad","slug":"consul-ingress-service-doesn-t-support-associating","errorCode":null,"errorMessage":"Consul Ingress Service doesn't support associating hosts to a service for the \"tcp\" protocol","messagePattern":"Consul Ingress Service doesn't support associating hosts to a service for the \"tcp\" protocol","errorType":"validation","errorClass":null,"httpStatus":null,"severity":"error","filePath":"nomad/structs/services.go","lineNumber":2431,"sourceCode":"\tif s == nil {\n\t\treturn nil\n\t}\n\n\t// pre-validate service Name and Hosts before passing along to consul:\n\t// https://developer.hashicorp.com/consul/docs/connect/config-entries/ingress-gateway#services\n\n\tif s.Name == \"\" {\n\t\treturn errors.New(\"Consul Ingress Service requires a name\")\n\t}\n\n\tswitch protocol {\n\tcase \"tcp\":\n\t\tif s.Name == \"*\" {\n\t\t\treturn errors.New(`Consul Ingress Service doesn't support wildcard name for \"tcp\" protocol`)\n\t\t}\n\n\t\tif len(s.Hosts) != 0 {\n\t\t\treturn errors.New(`Consul Ingress Service doesn't support associating hosts to a service for the \"tcp\" protocol`)\n\t\t}\n\tdefault:\n\t\tif s.Name == \"*\" && len(s.Hosts) != 0 {\n\t\t\treturn errors.New(`Consul Ingress Service with a wildcard \"*\" service name can not also specify hosts`)\n\t\t}\n\t}\n\n\treturn nil\n}\n\n// ConsulIngressListener is used to configure a listener on a Consul Ingress\n// Gateway.\ntype ConsulIngressListener struct {\n\tPort     int\n\tProtocol string\n\tServices []*ConsulIngressService\n}\n","sourceCodeStart":2413,"sourceCodeEnd":2449,"githubUrl":"https://github.com/hashicorp/nomad/blob/482b49bf1aec006f089bcfc7e632d8f6ac303e5e/nomad/structs/services.go#L2413-L2449","documentation":"Ingress service entries on a tcp listener cannot declare hosts, because tcp listeners have no HTTP Host header to match against; Consul routes purely by port. Nomad enforces this in validateIngressService before calling Consul.","triggerScenarios":"An ingress listener with protocol = \"tcp\" whose service block includes a non-empty hosts list.","commonSituations":"Reusing an http-mode service entry (with hosts) under a tcp listener; assuming hosts act as allowlists for tcp rather than routing matchers.","solutions":["Remove the hosts block from the tcp-protocol ingress service entry","Switch the listener protocol to \"http\" if hostname-based routing via hosts is needed"],"exampleFix":"// before\nservices {\n  name  = \"db\"\n  hosts = [\"db.example.com\"]\n}\n// after\nservices {\n  name = \"db\"\n}","handlingStrategy":"validation","validationCode":"function validateTcpHosts(listener) {\n  if (listener.protocol === \"tcp\") {\n    for (const s of listener.services ?? []) {\n      if ((s.hosts ?? []).length > 0) throw new Error(`ingress service ${s.name}: hosts not allowed for tcp protocol`);\n    }\n  }\n}","typeGuard":"function tcpHasHosts(s, proto) { return proto === \"tcp\" && Array.isArray(s.hosts) && s.hosts.length > 0; }","tryCatchPattern":"try {\n  await nomad.jobs.validate(job);\n} catch (e) {\n  if (e.message.includes(\"doesn't support associating hosts\")) {\n    console.error(\"Remove hosts from tcp-protocol ingress services\");\n  } else throw e;\n}","preventionTips":["Remember hosts are HTTP Host-header matchers, meaningless for tcp","Strip hosts blocks when repurposing http service entries as tcp","Validate ingress stanzas with nomad job validate before apply"],"tags":["nomad","consul","ingress-gateway","tcp","hosts"],"backgroundTag":"invalid-config-combination","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"}