Skip to main content

Create a Public DNS Name

Use a fully qualified domain name (FQDN) for a LoadBalancer Service

The external address of a Kubernetes LoadBalancer Service can change if the Service is re-created or its configuration changes. To ensure a stable human-readable address for publicly exposed Services, you can request a fully qualified domain name (FQDN) or wildcard record for your LoadBalancer Service.

Overview

  • To allocate an FQDN for your LoadBalancer Service, set the service.beta.kubernetes.io/external-hostname annotation in your manifest using short, long, or wildcard format.
  • In response, the External Hostname Controller creates a corresponding record in the .coreweave.app domain, which you can inspect using kubectl.
Update (August 25, 2025)
  • Prior to August 25, 2025: The controller mutated the service.beta.kubernetes.io/external-hostname annotation to long format if it was specified in short format.

  • Now: The controller no longer mutates the annotation. Instead, the FQDN and its allocation status are set in the .status.conditions field on the Service object, specifically under the ExternalRecords type.

No changes are required to your manifests or annotations. Only the method for retrieving the name has changed. Both short and long formats are still acceptable in the annotation.

Create an FQDN using short format

To create a public FQDN for a LoadBalancer Service, set the service.beta.kubernetes.io/external-hostname annotation in your manifest with the desired hostname.

This manifest uses the short format to create the hostname foo for the LoadBalancer Service. Note the highlighted annotations.

loadbalancer-with-DNS-example.yaml
apiVersion: v1
kind: Service
metadata:
annotations:
service.beta.kubernetes.io/external-hostname: foo
service.beta.kubernetes.io/coreweave-load-balancer-type: public
name: example-sshd
spec:
type: LoadBalancer
externalTrafficPolicy: Local
ports:
- name: sshd
port: 22
protocol: TCP
targetPort: sshd
selector:
app.kubernetes.io/name: sshd

The FQDN for the Service is constructed from the hostname, the organization's Org ID, and the cluster name, using the following pattern:

<hostname>.<Org_ID>-<cluster_name>.coreweave.app

For example, assuming the following:

  • An Org ID of abc123
  • A cluster named mycluster
  • An annotation of service.beta.kubernetes.io/external-hostname set to foo

The manifest shown above creates this FQDN:

foo.abc123-mycluster.coreweave.app

Create an FQDN using long format

You can set the annotation using the long format (full FQDN) only if it exactly follows the pattern described above and matches the Org ID and cluster name.

For Org ID abc123 and cluster mycluster, these two hostname formats produce the same FQDN:

external-hostname annotationFormatFQDN
fooShort formatfoo.abc123-mycluster.coreweave.app
foo.abc123-mycluster.coreweave.appLong formatfoo.abc123-mycluster.coreweave.app

However, if the Org ID and cluster name in the annotation do not match the actual values, then the annotation is treated as short format.

Consider this erroneous scenario:

  • Org ID abc123
  • Cluster mycluster
  • Annotation set to foo.xyz456-mycluster.coreweave.app

Because the Org ID in the annotation (xyz456) does not match the actual Org ID (abc123), the controller treats the entire annotation as short format.

The resulting FQDN is foo.xyz456-mycluster.coreweave.app.abc123-mycluster.coreweave.app, which is unintended.

Wildcard format

To create a wildcard DNS record for a LoadBalancer Service, set the service.beta.kubernetes.io/external-hostname annotation to *.

This creates a wildcard DNS record for the Service in the format *.abc123-mycluster.coreweave.app.

loadbalancer-wildcard-example.yaml
apiVersion: v1
kind: Service
metadata:
annotations:
service.beta.kubernetes.io/external-hostname: *
service.beta.kubernetes.io/coreweave-load-balancer-type: public
name: example-sshd
spec:
type: LoadBalancer
externalTrafficPolicy: Local
ports:
- name: sshd
port: 22
protocol: TCP
targetPort: sshd
selector:
app.kubernetes.io/name: sshd

Secure the endpoint with TLS

Assigning a public DNS name to a Service does not automatically issue a TLS certificate. To secure the endpoint, it's recommended to use cert-manager to obtain and manage a certificate for the FQDN or wildcard record.

View the allocated record

To view the allocated FQDN for a Service, inspect the Service status conditions:

$
kubectl get svc <my-service> -o=jsonpath='{.status.conditions[?(@.type=="ExternalRecords")].message}'

Replace <my-service> with the name of your Service, such as example-sshd from the example manifest.

Observe the allocation via events and status

Services provide events and status conditions with information about DNS record allocation.

To view events and status conditions for a Service named <my-service>:

$
kubectl describe svc <my-service>

The output includes:

Events:
Type Reason Age From Message
---- ------ ---- ---- -------
Normal IPAMIPAllocationSet 3s ipamOperator IPAllocation created for Service: example-sshd
Warning DomainNotAllowed 3s external-hostname-controller Domain foo is not allowed, appending orgid-cluster-name.coreweave.app
Normal DNSRecordUpdated 3s external-hostname-controller Updated DNS record for service default/example-sshd

To see the status conditions for the Service, look for the status.conditions field in the output:

$
kubectl get svc <my-service> -o yaml

The output includes:

status:
conditions:
- lastTransitionTime: "2025-08-18T16:07:35Z"
message: successfully created ip allocations
reason: IPAllocationSuccess
status: "True"
type: CoreWeaveLoadBalancerReady
- lastTransitionTime: "2025-08-18T16:07:35Z"
message: foo.orgid-cluster-name.coreweave.app
reason: EndpointsUpdated
status: "True"
type: ExternalRecords
- lastTransitionTime: "2025-08-18T16:07:35Z"
message: 12.34.56.78
reason: EndpointsUpdated
status: "True"
type: ExternalTargets

If any errors occur during record allocation, they are reflected in an event:

Events:
Type Reason Age From Message
---- ------ ---- ---- -------
Warning HostnameConflict 1s (x2 over 1s) external-hostname-controller Skipping record creation - conflicting hostname orgid-cluster-name.coreweave.app found in service: default/example-sshd

A corresponding False status condition is also created:

status:
conditions:
- lastTransitionTime: "2025-08-18T16:13:54Z"
message: ""
reason: EndpointsUpdated
status: "False"
type: ExternalRecords
- lastTransitionTime: "2025-08-18T16:13:54Z"
message: ""
reason: EndpointsUpdated
status: "False"
type: ExternalTargets