hashicorp/nomad · error

service %q cannot use address_mode="alloc_ipv6", only servic

Error message

service %q cannot use address_mode="alloc_ipv6", only services defined in a "group" block can use this mode

What it means

Same constraint as address_mode="alloc" but for the IPv6 variant: address_mode="alloc_ipv6" is only permitted on services declared directly in a "group" block. Task-level services cannot claim the allocation's IPv6 address because the address belongs to the allocation, not to an individual task's network namespace. Thrown during Job.Register validation and accumulated in the MultiError.

Source

Thrown at nomad/structs/structs.go:8490

	addServicePort := func(label, service string) {
		if _, ok := servicePorts[label]; !ok {
			servicePorts[label] = map[string]struct{}{}
		}
		servicePorts[label][service] = struct{}{}
	}
	knownServices := make(map[string]struct{})
	for i, service := range t.Services {
		if err := service.Validate(); err != nil {
			outer := fmt.Errorf("service[%d] %+q validation failed: %s", i, service.Name, err)
			mErr.Errors = append(mErr.Errors, outer)
		}

		if service.AddressMode == AddressModeAlloc {
			mErr.Errors = append(mErr.Errors, fmt.Errorf("service %q cannot use address_mode=\"alloc\", only services defined in a \"group\" block can use this mode", service.Name))
		}

		if service.AddressMode == AddressModeAllocIPv6 {
			mErr.Errors = append(mErr.Errors, fmt.Errorf("service %q cannot use address_mode=\"alloc_ipv6\", only services defined in a \"group\" block can use this mode", service.Name))
		}

		// Ensure that services with the same name are not being registered for
		// the same port
		if _, ok := knownServices[service.Name+service.PortLabel]; ok {
			mErr.Errors = append(mErr.Errors, fmt.Errorf("service %q is duplicate", service.Name))
		}
		knownServices[service.Name+service.PortLabel] = struct{}{}

		if service.PortLabel != "" {
			if service.AddressMode == "driver" {
				// Numeric port labels are valid for address_mode=driver
				_, err := strconv.Atoi(service.PortLabel)
				if err != nil {
					// Not a numeric port label, add it to list to check
					addServicePort(service.PortLabel, service.Name)
				}
			} else {

View on GitHub (pinned to 482b49bf1a)

Solutions

  1. Move the service block from the `task` to the `group` level so alloc_ipv6 is legal.
  2. Remove `address_mode = "alloc_ipv6"` from the task-level service and rely on the default driver mode.
  3. Use `nomad job validate` locally to catch the misplacement before submitting.

Example fix

// before
task "app" {
  service {
    name = "svc6"
    address_mode = "alloc_ipv6"
  }
}

// after
group "web" {
  service {
    name = "svc6"
    address_mode = "alloc_ipv6"
  }
  task "app" { }
}
Defensive patterns

Strategy: validation

Validate before calling

for _, tg := range job.TaskGroups {
  for _, t := range tg.Tasks {
    for _, s := range t.Services {
      if s.AddressMode == "alloc_ipv6" {
        return fmt.Errorf("service %q: move alloc_ipv6 to group level", s.Name)
      }
    }
  }
}

Prevention

When it happens

Trigger: Submitting a job where a service nested inside a `task` block sets `address_mode = "alloc_ipv6"` (AddressModeAllocIPv6).

Common situations: Configuring IPv6-only clusters and copy-pasting group service stanzas into tasks; experimenting with IPv6 address advertisement at the wrong nesting level.

Related errors


AI-assisted analysis of hashicorp/nomad@482b49bf1a (2026-09-04). Data as JSON: /api/errors/899d9d7a888890dd. Report an issue: GitHub.