Skip to main content
This tutorial demonstrates how to install Istio with a locked-down outbound traffic policy (REGISTRY_ONLY) and register an external workload so in-cluster pods can reach it. We’ll use a host-run nginx as the external workload and show how WorkloadEntry and ServiceEntry allow pods to access it when the mesh denies unregistered outbound destinations.

Prerequisites

  • A Kubernetes cluster with kubectl access.
  • curl and apt (for the control-plane host demo steps).
  • istioctl (we’ll download it as part of the demo).

Verify Kubernetes is running

List all pods across namespaces to confirm your cluster is healthy:
Example (truncated):

Download and install Istio (demo: v1.18.2)

Download Istio and make istioctl available:
Create a local copy of the demo profile so we can modify mesh settings:
Edit demo.yaml to set the mesh outbound traffic policy to REGISTRY_ONLY. Add or update the following under meshConfig:
For context, the file should include the hub and the modified meshConfig:
Validate and install the modified profile:
Verify Istio system pods are running:
Setting outboundTrafficPolicy.mode to REGISTRY_ONLY blocks traffic to any destination not registered in the mesh registry. Apply this carefully in production; it can break external integrations unless those endpoints are explicitly added via ServiceEntry/WorkloadEntry.

Simulate an external application (nginx on the host)

For this demo we simulate an external workload by running nginx on the control plane host. (In production, this would typically be a VM or cloud instance.) Install nginx on the host:
Verify nginx serves locally:
Identify the host network interface used by your CNI (weave in this demo) and note its IP (example: 10.50.0.1):
You should see the weave interface with an IP like:
Confirm the host’s nginx is reachable via that IP:

Create a test pod inside the cluster (with sidecar injection enabled)

Enable Istio sidecar injection for the namespace (we use default here):
Run a test pod with an injected sidecar:
Attempt to curl the host IP from inside the pod. With REGISTRY_ONLY the sidecar blocks traffic to unregistered IPs and typically returns a 502:
Example response:
Setting meshConfig.outboundTrafficPolicy.mode to REGISTRY_ONLY requires that any external service you want pods to access must be registered with Istio (via ServiceEntry and/or WorkloadEntry). Otherwise the sidecar will reject the outbound traffic.

Register the external workload with WorkloadEntry

Create a WorkloadEntry that represents the host-run nginx. Save the following as workload-entry.yaml:
Apply it and confirm:

Create a ServiceEntry that maps a hostname to the WorkloadEntry

Create service-entry.yaml to define a logical hostname and connect it to the WorkloadEntry via a selector:
Notes:
  • hosts is the logical hostname clients will use, e.g. app.internal.com.
  • resolution: STATIC is required when the ServiceEntry is paired with WorkloadEntry(s).
  • workloadSelector.labels picks the WorkloadEntry(s) (or pods) with matching labels.
Apply the ServiceEntry:

Test access from the test pod via the registered hostname

From inside the test pod, curl the logical hostname:
You should receive the nginx HTML from the host because Istio now recognizes app.internal.com via the ServiceEntry/WorkloadEntry registration.

Add an in-cluster pod to the same workload (WorkloadEntry label)

To show that a WorkloadEntry can represent multiple endpoints, create an in-cluster pod with the same label as the WorkloadEntry:
Edit the pod’s index page to distinguish it from the host nginx:
Now requests to app.internal.com from the test pod may be served by either the external host (WorkloadEntry IP) or the in-cluster nginx pod, since they share the label and are part of the same logical service:
Istio may load-balance traffic across the registered endpoints.

Quick reference: WorkloadEntry vs ServiceEntry

Practical notes and reference

  • WorkloadEntry lets Istio manage non-Kubernetes endpoints. Use it for VMs, bare metal, and external IPs.
  • ServiceEntry maps DNS names into the mesh and describes ports/protocols and resolution. Use workloadSelector to attach WorkloadEntry(s).
  • Common WorkloadEntry fields: address (IP or DNS), serviceAccount (if mTLS is required), network, labels, and ports.
  • If location is not set in a ServiceEntry, the default is MESH_EXTERNAL. Use location: MESH_INTERNAL when the service should be considered internal to the mesh.
  • When using REGISTRY_ONLY, ensure every external dependency is added to the registry; otherwise application traffic will be blocked.
Further reading and references: Example concise references (for copy/paste): WorkloadEntry example:
ServiceEntry example (MESH_INTERNAL):
That concludes the workload-entry demo and how to register external workloads when Istio outbound traffic policy is set to REGISTRY_ONLY.

Watch Video

Practice Lab