Skip to main content

External services

Declare the hosts outside the cluster that your application connects to, and know what the platform does with them.

spec.networking.outbound.external on a PlatformApplication lists the hosts outside the cluster that your application talks to, such as a managed database, a cache, or a third-party API. For each entry, the platform registers the host with the service mesh as an Istio ServiceEntry.

The field doesn't open or close any network path, and it doesn't change what a hostname resolves to. The What this field doesn't do section explains both.

outbound.external takes effect from Platform Application Operator 0.1.429. On an earlier version, the field is accepted but generates nothing. To find out which version your cluster runs, ask the platform team.

Declare an external host​

Add one entry per host and port to your PlatformApplication. This example connects to an Azure Database for MySQL server through its private endpoint:

# .platform/kubernetes/base/application.yaml
spec:
networking:
outbound:
external:
- host: orders-db.mysql.database.azure.com
port: 3306
protocol: TCP

Each entry has the following fields:

FieldRequiredDescription
hostYesThe hostname your application connects to. Use the name the provider gives you, not an IP address.
portYesThe port your application connects to, from 1 to 65535.
protocolNoHow the mesh treats the connection. Set it for every entry. For the accepted values, see Choose a protocol.

To reach the same host on two ports, add two entries. To stop declaring a host, remove its entry. The operator deletes the ServiceEntry on its next reconcile.

In your application, connect to the host by name, exactly as you declared it. You don't need IP addresses, hostAliases, or /etc/hosts entries.

For the full field reference, see spec.networking.outbound.external in the PlatformApplication reference.

Choose a protocol​

The protocol tells the mesh how to treat the connection. When you leave it out, the generated port is named HTTP-<port> and has no protocol set.

Set protocol to the value that matches your connection:

Your connectionSet protocol to
MySQL, PostgreSQL, Redis, MongoDB, or any other database or non-HTTP protocolTCP
An HTTPS APIHTTPS
Plain HTTP/1.1HTTP
HTTP/2 without TLSHTTP2
gRPCGRPC
Another protocol over TLSTLS

The field also accepts MONGO. It only adds MongoDB metrics for pods with a sidecar, and only on unencrypted connections, so use TCP instead.

A database connection declared without protocol: TCP gets an HTTP port name. Set TCP explicitly so the mesh doesn't treat it as HTTP.

What the operator generates​

For each entry, the operator creates one ServiceEntry in your application's namespace with these settings:

SettingValueWhat it means
Name<application>-<host>-<port>Shortened with a hash suffix if it exceeds 253 characters.
hostsYour hostOne host per entry.
portsYour port, named <PROTOCOL>-<port>For example, TCP-3306.
locationMESH_EXTERNALThe host is outside the mesh.
resolutionDNSThe mesh resolves host through cluster DNS each time it connects.
exportTo.Visible only in your application's namespace.

The operator also adds entries that you don't declare, based on your application's resources:

  • A crdb resource adds default-public.cockroachdb-system on port 26257.
  • Any other resource adds *.amazonaws.com on port 443.

How private endpoints resolve​

A private endpoint gives a managed Azure service a private IP address in your cluster's network. Its public hostname, such as orders-db.mysql.database.azure.com, returns that private IP only when the query reaches Azure DNS, which holds the privatelink record.

On Azure clusters, cluster DNS forwards the following zones to Azure DNS:

  • mysql.database.azure.com (Azure Database for MySQL)
  • redis.azure.net (Azure Managed Redis)

Forwarding gets the query to Azure DNS. For Azure DNS to answer with the private IP, two things also have to be in place on the Azure side:

  • The endpoint's privatelink DNS zone, such as privatelink.mysql.database.azure.com, is linked to your cluster's virtual network. Without the link, Azure DNS returns the public IP.
  • The zone holds an A record for the endpoint. The endpoint's DNS zone group normally writes it when the endpoint is created.

When all three are in place, cluster DNS returns the private IP for the host, and the ServiceEntry resolves to it too. When you set up a private endpoint, confirm the zone link and the record with whoever manages the endpoint.

For a private endpoint in any other zone, such as postgres.database.azure.com or redis.cache.windows.net, ask the platform team to add the zone to your cluster's DNS forwarding. Until they do, the name resolves to the service's public IP, and the connection either fails or leaves the private network.

What this field doesn't do​

  • It isn't an egress firewall. The field generates no NetworkPolicy and blocks nothing: leaving a host out doesn't stop your application reaching it, and listing a host doesn't grant access to it. Don't rely on it as a security boundary.
  • It doesn't fix DNS. Because every entry uses resolution: DNS, the mesh connects to whatever cluster DNS returns for the host. If cluster DNS returns the wrong address, the ServiceEntry uses the wrong address too. Fix the DNS record or the forwarding, as described in the preceding section.
  • It doesn't pin an IP address. You can't set a fixed IP for a host.

Troubleshoot​

If your namespace runs in Istio ambient mode, DNS looks different from inside a pod. Before you trust a DNS result from your pod, check the following.

nslookup names a server but the answer doesn't come from it. In an ambient pod, the mesh's node proxy (ztunnel) captures every DNS query and resolves it through cluster DNS, whichever server you pass:

nslookup orders-db.mysql.database.azure.com 168.63.129.16

This command doesn't query Azure DNS from an ambient pod. To test what a specific DNS server returns, run the query from a pod outside the mesh. Ask the platform team if you can't start one.

The host resolves to 240.240.x.x. This is expected. The mesh gives each ServiceEntry host a virtual address in 240.240.0.0/16 and routes connections to it to the real destination. Your application connects to the virtual address, and the mesh resolves the host when it opens the connection.

An IPv6 address such as 2001:2::1 is unreachable. The mesh also allocates an IPv6 virtual address. Pods without IPv6 fail on it at once with Network unreachable, and most clients then try the IPv4 address. If your client tries only the first address it gets, configure it to prefer IPv4.

The host resolves to a public IP. Azure DNS didn't give cluster DNS a private answer. There are two likely causes, and the fix differs, so ask the platform team to check both before changing anything:

  • The host's zone isn't forwarded to Azure DNS.
  • The endpoint's privatelink zone isn't linked to your cluster's virtual network, so Azure DNS returns the public record even though forwarding works.

For more information, see How private endpoints resolve.

A pod outside the mesh gets NXDOMAIN from Azure DNS. The private endpoint's record is missing from its privatelink DNS zone. This can happen when another private endpoint for the same server is deleted. Ask the platform team to restore the record.

No ServiceEntry exists. If you have read access to your namespace, list the entries:

kubectl get serviceentries -n NAMESPACE

Replace NAMESPACE with your application's namespace. If the list is empty, your cluster may run an operator version earlier than 0.1.429. Ask the platform team.