<?xml version="1.0" encoding="utf-8"?>
<rss version="2.0">
<channel>
<title>Coder Legion - Recent posts tagged envoy</title>
<link>https://coderlegion.com/tag/envoy</link>
<description>Powered by Question2Answer</description>
<item>
<title>Istio ServiceEntry Explained: External Services, DNS, and Traffic Control</title>
<link>https://coderlegion.com/14650/istio-serviceentry-explained-external-services-dns-and-traffic-control</link>
<description>
&lt;a href=&quot;https://alexandre-vazquez.com/istio-serviceentry-explained/&quot; title=&quot;Istio ServiceEntry Explained: External Services, DNS, and Traffic Control&quot; rel=&quot;nofollow&quot;&gt;&lt;img width=&quot;1536&quot; height=&quot;1024&quot; src=&quot;https://mlk5tkvgfeoa.i.optimole.com/cb:s8WW.68/w:1536/h:1024/q:mauto/f:best/https://alexandre-vazquez.com/wp-content/uploads/2026/04/0372cff9-defc-4162-9b75-42730f112ebf.avif&quot; class=&quot;webfeedsFeaturedVisual wp-post-image&quot; alt=&quot;Istio ServiceEntry Explained: External Services, DNS, and Traffic Control&quot; style=&quot;display: block; margin: auto; margin-bottom: 5px;max-width: 100%;&quot; title=&quot;Istio ServiceEntry Explained: External Services, DNS, and Traffic Control 4&quot;&gt;&lt;/a&gt;&amp;lt;!--
SEO Title: Istio ServiceEntry Explained: External Services, DNS, and Traffic Control
Meta Description: Complete guide to Istio ServiceEntry. Control how your mesh reaches external APIs, databases, and third-party services. With production YAML examples and DNS resolution patterns.
Focus Keywords: istio serviceentry,istio service entry,istio external service,istio dns resolution
Suggested Slug: istio-serviceentry-explained
--&amp;gt;


&lt;p&gt;Every production Kubernetes cluster talks to the outside world. Your services call payment APIs, connect to managed databases, push events to SaaS analytics platforms, and reach legacy systems that will never run inside the mesh. By default, Istio lets all outbound traffic flow freely &amp;mdash; or blocks it entirely if you flip &lt;code&gt;outboundTrafficPolicy&lt;/code&gt; to &lt;code&gt;REGISTRY_ONLY&lt;/code&gt;. Neither extreme gives you what you actually need: &lt;strong&gt;selective, observable, policy-controlled access to external services&lt;/strong&gt;.&lt;/p&gt;



&lt;p&gt;That is exactly what &lt;strong&gt;Istio ServiceEntry&lt;/strong&gt; solves. It registers external endpoints in the mesh&amp;rsquo;s internal service registry so that Envoy sidecars can apply the same traffic management, security, and observability features to outbound calls that you already enjoy for east-west traffic. No new proxies, no egress gateways required for the basic case &amp;mdash; just a YAML resource that tells the mesh &amp;ldquo;this external thing exists, and here is how to reach it.&amp;rdquo;&lt;/p&gt;



&lt;p&gt;In this guide, I will walk through every field of the ServiceEntry spec, explain the four DNS resolution modes with real-world use cases, and show production-ready patterns for external APIs, databases, TCP services, and legacy workloads. We will also cover how to combine ServiceEntry with DestinationRule and VirtualService to get circuit breaking, retries, connection pooling, and even sticky sessions for external dependencies.&lt;/p&gt;



&lt;h2 class=&quot;wp-block-heading&quot;&gt;What Is a ServiceEntry&lt;/h2&gt;



&lt;p&gt;Istio maintains an internal service registry that merges Kubernetes Services with any additional entries you declare. When a sidecar proxy needs to decide how to route a request, it consults this registry. Services inside the mesh are automatically registered. Services &lt;em&gt;outside&lt;/em&gt; the mesh are not &amp;mdash; unless you create a &lt;strong&gt;ServiceEntry&lt;/strong&gt;.&lt;/p&gt;



&lt;p&gt;A ServiceEntry is a custom resource that adds an entry to the mesh&amp;rsquo;s service registry. Once registered, the external service becomes a first-class citizen: Envoy generates clusters, routes, and listeners for it, which means you get metrics (&lt;code&gt;istio_requests_total&lt;/code&gt;), access logs, distributed traces, mTLS origination, retries, timeouts, circuit breaking &amp;mdash; the full Istio feature set.&lt;/p&gt;



&lt;p&gt;Without a ServiceEntry, outbound traffic to an external host either passes through as a raw TCP connection (in &lt;code&gt;ALLOW_ANY&lt;/code&gt; mode) with no telemetry, or gets dropped with a 502/503 (in &lt;code&gt;REGISTRY_ONLY&lt;/code&gt; mode). Both outcomes are undesirable in production. The ServiceEntry bridges that gap.&lt;/p&gt;



&lt;h2 class=&quot;wp-block-heading&quot;&gt;ServiceEntry Anatomy: All Fields Explained&lt;/h2&gt;



&lt;p&gt;Let us look at a complete ServiceEntry and then break down each field.&lt;/p&gt;



&lt;pre&gt;&lt;code class=&quot;language-yaml&quot;&gt;apiVersion: networking.istio.io/v1
kind: ServiceEntry
metadata:
  name: external-api
  namespace: production
spec:
  hosts:
    - api.stripe.com
  location: MESH_EXTERNAL
  ports:
    - number: 443
      name: https
      protocol: TLS
  resolution: DNS
  exportTo:
    - &quot;.&quot;
    - &quot;istio-system&quot;&lt;/code&gt;&lt;/pre&gt;



&lt;h3 class=&quot;wp-block-heading&quot;&gt;hosts&lt;/h3&gt;



&lt;p&gt;A list of hostnames associated with the service. For external services, this is typically the DNS name your application uses (e.g., &lt;code&gt;api.stripe.com&lt;/code&gt;). For services using &lt;code&gt;HTTP&lt;/code&gt; protocols, the &lt;code&gt;hosts&lt;/code&gt; field is matched against the HTTP Host header. For non-HTTP protocols and services without a DNS name, you can use a synthetic hostname and pair it with &lt;code&gt;addresses&lt;/code&gt; or static &lt;code&gt;endpoints&lt;/code&gt;.&lt;/p&gt;



&lt;h3 class=&quot;wp-block-heading&quot;&gt;addresses&lt;/h3&gt;



&lt;p&gt;Optional virtual IP addresses associated with the service. Useful for TCP services where you want to assign a VIP that the sidecar will intercept. Not required for HTTP/HTTPS services that use hostname-based routing.&lt;/p&gt;



&lt;h3 class=&quot;wp-block-heading&quot;&gt;ports&lt;/h3&gt;



&lt;p&gt;The ports on which the external service is exposed. Each port needs a &lt;code&gt;number&lt;/code&gt;, &lt;code&gt;name&lt;/code&gt;, and &lt;code&gt;protocol&lt;/code&gt;. The protocol matters: setting it to &lt;code&gt;TLS&lt;/code&gt; tells Envoy to perform SNI-based routing without terminating TLS. Setting it to &lt;code&gt;HTTPS&lt;/code&gt; means HTTP over TLS. For databases, you&amp;rsquo;ll typically use &lt;code&gt;TCP&lt;/code&gt;.&lt;/p&gt;



&lt;h3 class=&quot;wp-block-heading&quot;&gt;location&lt;/h3&gt;



&lt;p&gt;&lt;code&gt;MESH_EXTERNAL&lt;/code&gt; or &lt;code&gt;MESH_INTERNAL&lt;/code&gt;. Use &lt;code&gt;MESH_EXTERNAL&lt;/code&gt; for services outside your cluster (third-party APIs, managed databases). Use &lt;code&gt;MESH_INTERNAL&lt;/code&gt; for services inside your infrastructure that are not part of the mesh &amp;mdash; for example, VMs running in the same VPC that do not have a sidecar, or a Kubernetes Service in a namespace without injection enabled. The location affects how mTLS is applied and how metrics are labeled.&lt;/p&gt;



&lt;h3 class=&quot;wp-block-heading&quot;&gt;resolution&lt;/h3&gt;



&lt;p&gt;How the sidecar resolves the endpoint addresses. This is the most critical field and I will dedicate the next section to it. Options: &lt;code&gt;NONE&lt;/code&gt;, &lt;code&gt;STATIC&lt;/code&gt;, &lt;code&gt;DNS&lt;/code&gt;, &lt;code&gt;DNS_ROUND_ROBIN&lt;/code&gt;.&lt;/p&gt;



&lt;h3 class=&quot;wp-block-heading&quot;&gt;endpoints&lt;/h3&gt;



&lt;p&gt;An explicit list of network endpoints. Required when resolution is &lt;code&gt;STATIC&lt;/code&gt;. Optional with &lt;code&gt;DNS&lt;/code&gt; resolution to provide labels or locality information. Each endpoint can have an &lt;code&gt;address&lt;/code&gt;, &lt;code&gt;ports&lt;/code&gt;, &lt;code&gt;labels&lt;/code&gt;, &lt;code&gt;network&lt;/code&gt;, &lt;code&gt;locality&lt;/code&gt;, and &lt;code&gt;weight&lt;/code&gt;.&lt;/p&gt;



&lt;h3 class=&quot;wp-block-heading&quot;&gt;exportTo&lt;/h3&gt;



&lt;p&gt;Controls the visibility of this ServiceEntry across namespaces. Use &lt;code&gt;&quot;.&quot;&lt;/code&gt; for the current namespace only, &lt;code&gt;&quot;*&quot;&lt;/code&gt; for all namespaces. In multi-team clusters, restrict exports to avoid namespace pollution.&lt;/p&gt;

&amp;lt;!-- /wp:post-content --&amp;gt;

&amp;lt;!-- wp:heading --&amp;gt;
&lt;h2&gt;Resolution Types: NONE vs STATIC vs DNS vs DNS_ROUND_ROBIN&lt;/h2&gt;
&amp;lt;!-- /wp:heading --&amp;gt;

&amp;lt;!-- wp:paragraph --&amp;gt;
&lt;p&gt;The &lt;code&gt;resolution&lt;/code&gt; field determines how Envoy discovers the IP addresses behind the service. Getting this wrong is the number one cause of ServiceEntry misconfigurations. Here is a clear breakdown.&lt;/p&gt;
&amp;lt;!-- /wp:paragraph --&amp;gt;

&amp;lt;!-- wp:table --&amp;gt;
&lt;figure class=&quot;wp-block-table&quot;&gt;&lt;table&gt;&lt;thead&gt;&lt;tr&gt;&lt;th&gt;Resolution&lt;/th&gt;&lt;th&gt;How It Works&lt;/th&gt;&lt;th&gt;Best For&lt;/th&gt;&lt;/tr&gt;&lt;/thead&gt;&lt;tbody&gt;&lt;tr&gt;&lt;td&gt;&lt;code&gt;NONE&lt;/code&gt;&lt;/td&gt;&lt;td&gt;Envoy uses the original destination IP from the connection. No DNS lookup by the proxy.&lt;/td&gt;&lt;td&gt;Wildcard entries, pass-through scenarios, services where the application already resolved the IP.&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;&lt;code&gt;STATIC&lt;/code&gt;&lt;/td&gt;&lt;td&gt;Envoy routes to the IPs listed in the &lt;code&gt;endpoints&lt;/code&gt; field. No DNS involved.&lt;/td&gt;&lt;td&gt;Services with stable, known IPs (e.g., on-prem databases, VMs with fixed IPs).&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;&lt;code&gt;DNS&lt;/code&gt;&lt;/td&gt;&lt;td&gt;Envoy resolves the hostname at connection time and creates an endpoint per returned IP. Uses async DNS with health checking per IP.&lt;/td&gt;&lt;td&gt;External APIs behind load balancers, managed databases with DNS endpoints (RDS, CloudSQL).&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;&lt;code&gt;DNS_ROUND_ROBIN&lt;/code&gt;&lt;/td&gt;&lt;td&gt;Envoy resolves the hostname and uses a single logical endpoint, rotating across returned IPs. No per-IP health checking.&lt;/td&gt;&lt;td&gt;Simple external services, services where you do not need per-endpoint circuit breaking.&lt;/td&gt;&lt;/tr&gt;&lt;/tbody&gt;&lt;/table&gt;&lt;/figure&gt;
&amp;lt;!-- /wp:table --&amp;gt;

&amp;lt;!-- wp:heading {&quot;level&quot;:3} --&amp;gt;
&lt;h3&gt;When to Use NONE&lt;/h3&gt;
&amp;lt;!-- /wp:heading --&amp;gt;

&amp;lt;!-- wp:paragraph --&amp;gt;
&lt;p&gt;Use &lt;code&gt;NONE&lt;/code&gt; when you want to register a range of external IPs or wildcard hosts without Envoy performing any address resolution. This is common for broad egress policies: &amp;ldquo;allow traffic to &lt;code&gt;*.googleapis.com&lt;/code&gt; on port 443.&amp;rdquo; Envoy will simply forward traffic to whatever IP the application resolved via kube-dns. The downside: Envoy has limited ability to apply per-endpoint policies.&lt;/p&gt;
&amp;lt;!-- /wp:paragraph --&amp;gt;

&amp;lt;!-- wp:heading {&quot;level&quot;:3} --&amp;gt;
&lt;h3&gt;When to Use STATIC&lt;/h3&gt;
&amp;lt;!-- /wp:heading --&amp;gt;

&amp;lt;!-- wp:paragraph --&amp;gt;
&lt;p&gt;Use &lt;code&gt;STATIC&lt;/code&gt; when the external service has known, stable IP addresses that rarely change. This avoids DNS dependencies entirely. You define the IPs in the &lt;code&gt;endpoints&lt;/code&gt; list. Classic use case: a legacy Oracle database on a fixed IP in your data center.&lt;/p&gt;
&amp;lt;!-- /wp:paragraph --&amp;gt;

&amp;lt;!-- wp:heading {&quot;level&quot;:3} --&amp;gt;
&lt;h3&gt;When to Use DNS&lt;/h3&gt;
&amp;lt;!-- /wp:heading --&amp;gt;

&amp;lt;!-- wp:paragraph --&amp;gt;
&lt;p&gt;Use &lt;code&gt;DNS&lt;/code&gt; for most external API integrations. Envoy performs asynchronous DNS resolution and creates a cluster endpoint for each returned IP address. This enables per-endpoint health checking and circuit breaking &amp;mdash; critical for production reliability. This is the mode you want for services like &lt;code&gt;api.stripe.com&lt;/code&gt; or your RDS instance endpoint.&lt;/p&gt;
&amp;lt;!-- /wp:paragraph --&amp;gt;

&amp;lt;!-- wp:heading {&quot;level&quot;:3} --&amp;gt;
&lt;h3&gt;When to Use DNS_ROUND_ROBIN&lt;/h3&gt;
&amp;lt;!-- /wp:heading --&amp;gt;

&amp;lt;!-- wp:paragraph --&amp;gt;
&lt;p&gt;Use &lt;code&gt;DNS_ROUND_ROBIN&lt;/code&gt; when the external hostname returns many IPs and you do not need per-IP circuit breaking. Envoy treats all resolved IPs as a single logical endpoint and round-robins across them. This is lighter weight than &lt;code&gt;DNS&lt;/code&gt; mode and avoids creating a large number of endpoints in Envoy&amp;rsquo;s cluster configuration.&lt;/p&gt;
&amp;lt;!-- /wp:paragraph --&amp;gt;

&amp;lt;!-- wp:heading --&amp;gt;
&lt;h2&gt;Practical Patterns&lt;/h2&gt;
&amp;lt;!-- /wp:heading --&amp;gt;

&amp;lt;!-- wp:heading {&quot;level&quot;:3} --&amp;gt;
&lt;h3&gt;Pattern 1: External HTTP API (api.stripe.com)&lt;/h3&gt;
&amp;lt;!-- /wp:heading --&amp;gt;

&amp;lt;!-- wp:paragraph --&amp;gt;
&lt;p&gt;The most common ServiceEntry pattern. Your application calls a third-party HTTPS API. You want Istio telemetry, and optionally retries and timeouts.&lt;/p&gt;
&amp;lt;!-- /wp:paragraph --&amp;gt;

&amp;lt;!-- wp:code --&amp;gt;
&lt;pre&gt;&lt;code class=&quot;language-yaml&quot;&gt;apiVersion: networking.istio.io/v1
kind: ServiceEntry
metadata:
  name: stripe-api
  namespace: payments
spec:
  hosts:
    - api.stripe.com
  location: MESH_EXTERNAL
  ports:
    - number: 443
      name: tls
      protocol: TLS
  resolution: DNS&lt;/code&gt;&lt;/pre&gt;
&amp;lt;!-- /wp:code --&amp;gt;

&amp;lt;!-- wp:paragraph --&amp;gt;
&lt;p&gt;Note the protocol is &lt;code&gt;TLS&lt;/code&gt;, not &lt;code&gt;HTTPS&lt;/code&gt;. Since your application initiates the TLS handshake directly, Envoy handles this as opaque TLS using SNI-based routing. If you were terminating TLS at the sidecar and doing TLS origination via a DestinationRule, you would set the protocol to &lt;code&gt;HTTP&lt;/code&gt; and handle the upgrade separately &amp;mdash; but for most external APIs, let the application manage its own TLS.&lt;/p&gt;
&amp;lt;!-- /wp:paragraph --&amp;gt;

&amp;lt;!-- wp:heading {&quot;level&quot;:3} --&amp;gt;
&lt;h3&gt;Pattern 2: External Managed Database (RDS / CloudSQL)&lt;/h3&gt;
&amp;lt;!-- /wp:heading --&amp;gt;

&amp;lt;!-- wp:paragraph --&amp;gt;
&lt;p&gt;Managed databases expose a DNS endpoint that resolves to one or more IPs. During failover, the DNS record changes. You need Envoy to respect DNS TTLs and route to the current primary.&lt;/p&gt;
&amp;lt;!-- /wp:paragraph --&amp;gt;

&amp;lt;!-- wp:code --&amp;gt;
&lt;pre&gt;&lt;code class=&quot;language-yaml&quot;&gt;apiVersion: networking.istio.io/v1
kind: ServiceEntry
metadata:
  name: orders-database
  namespace: orders
spec:
  hosts:
    - orders-db.abc123.us-east-1.rds.amazonaws.com
  location: MESH_EXTERNAL
  ports:
    - number: 5432
      name: postgres
      protocol: TCP
  resolution: DNS&lt;/code&gt;&lt;/pre&gt;
&amp;lt;!-- /wp:code --&amp;gt;

&amp;lt;!-- wp:paragraph --&amp;gt;
&lt;p&gt;For TCP services, Envoy cannot use HTTP headers to route, so it relies on IP-based matching. The &lt;code&gt;DNS&lt;/code&gt; resolution mode ensures Envoy periodically re-resolves the hostname and updates its endpoint list. This is critical for RDS multi-AZ failover scenarios where the DNS endpoint flips to a new IP.&lt;/p&gt;
&amp;lt;!-- /wp:paragraph --&amp;gt;

&amp;lt;!-- wp:heading {&quot;level&quot;:3} --&amp;gt;
&lt;h3&gt;Pattern 3: Legacy Internal Service Not in the Mesh&lt;/h3&gt;
&amp;lt;!-- /wp:heading --&amp;gt;

&amp;lt;!-- wp:paragraph --&amp;gt;
&lt;p&gt;You have a monitoring service running on a set of VMs at known IP addresses inside your VPC. It is not part of the mesh, but your meshed services need to talk to it.&lt;/p&gt;
&amp;lt;!-- /wp:paragraph --&amp;gt;

&amp;lt;!-- wp:code --&amp;gt;
&lt;pre&gt;&lt;code class=&quot;language-yaml&quot;&gt;apiVersion: networking.istio.io/v1
kind: ServiceEntry
metadata:
  name: legacy-monitoring
  namespace: observability
spec:
  hosts:
    - legacy-monitoring.internal
  location: MESH_INTERNAL
  ports:
    - number: 8080
      name: http
      protocol: HTTP
  resolution: STATIC
  endpoints:
    - address: 10.0.5.10
    - address: 10.0.5.11
    - address: 10.0.5.12&lt;/code&gt;&lt;/pre&gt;
&amp;lt;!-- /wp:code --&amp;gt;

&amp;lt;!-- wp:paragraph --&amp;gt;
&lt;p&gt;Key differences: &lt;code&gt;location&lt;/code&gt; is &lt;code&gt;MESH_INTERNAL&lt;/code&gt; because the service lives inside your network, and &lt;code&gt;resolution&lt;/code&gt; is &lt;code&gt;STATIC&lt;/code&gt; because we know the IPs. The hostname &lt;code&gt;legacy-monitoring.internal&lt;/code&gt; is synthetic &amp;mdash; your application uses it, and Istio&amp;rsquo;s DNS proxy (or a CoreDNS entry) resolves it to one of the listed endpoints.&lt;/p&gt;
&amp;lt;!-- /wp:paragraph --&amp;gt;

&amp;lt;!-- wp:heading {&quot;level&quot;:3} --&amp;gt;
&lt;h3&gt;Pattern 4: TCP Services with Multiple Ports&lt;/h3&gt;
&amp;lt;!-- /wp:heading --&amp;gt;

&amp;lt;!-- wp:paragraph --&amp;gt;
&lt;p&gt;Some external services expose multiple TCP ports &amp;mdash; for example, an Elasticsearch cluster with both data (9200) and transport (9300) ports.&lt;/p&gt;
&amp;lt;!-- /wp:paragraph --&amp;gt;

&amp;lt;!-- wp:code --&amp;gt;
&lt;pre&gt;&lt;code class=&quot;language-yaml&quot;&gt;apiVersion: networking.istio.io/v1
kind: ServiceEntry
metadata:
  name: external-elasticsearch
  namespace: search
spec:
  hosts:
    - es.example.com
  location: MESH_EXTERNAL
  ports:
    - number: 9200
      name: http
      protocol: HTTP
    - number: 9300
      name: transport
      protocol: TCP
  resolution: DNS&lt;/code&gt;&lt;/pre&gt;
&amp;lt;!-- /wp:code --&amp;gt;

&amp;lt;!-- wp:paragraph --&amp;gt;
&lt;p&gt;Each port gets its own Envoy listener configuration. The HTTP port benefits from full Layer 7 telemetry and traffic management. The TCP port gets Layer 4 metrics and connection-level policies.&lt;/p&gt;
&amp;lt;!-- /wp:paragraph --&amp;gt;

&amp;lt;!-- wp:heading --&amp;gt;
&lt;h2&gt;Combining ServiceEntry with DestinationRule&lt;/h2&gt;
&amp;lt;!-- /wp:heading --&amp;gt;

&amp;lt;!-- wp:paragraph --&amp;gt;
&lt;p&gt;A ServiceEntry alone registers the external service. To apply traffic policies &amp;mdash; connection pooling, circuit breaking, TLS origination, load balancing &amp;mdash; you pair it with a &lt;strong&gt;DestinationRule&lt;/strong&gt;. This is where things get powerful.&lt;/p&gt;
&amp;lt;!-- /wp:paragraph --&amp;gt;

&amp;lt;!-- wp:heading {&quot;level&quot;:3} --&amp;gt;
&lt;h3&gt;Connection Pooling and Circuit Breaking&lt;/h3&gt;
&amp;lt;!-- /wp:heading --&amp;gt;

&amp;lt;!-- wp:paragraph --&amp;gt;
&lt;p&gt;External APIs have rate limits. Your managed database has a maximum connection count. Protecting these dependencies at the mesh level prevents cascading failures.&lt;/p&gt;
&amp;lt;!-- /wp:paragraph --&amp;gt;

&amp;lt;!-- wp:code --&amp;gt;
&lt;pre&gt;&lt;code class=&quot;language-yaml&quot;&gt;apiVersion: networking.istio.io/v1
kind: ServiceEntry
metadata:
  name: stripe-api
  namespace: payments
spec:
  hosts:
    - api.stripe.com
  location: MESH_EXTERNAL
  ports:
    - number: 443
      name: tls
      protocol: TLS
  resolution: DNS
---
apiVersion: networking.istio.io/v1
kind: DestinationRule
metadata:
  name: stripe-api-dr
  namespace: payments
spec:
  host: api.stripe.com
  trafficPolicy:
    connectionPool:
      tcp:
        maxConnections: 50
        connectTimeout: 5s
      http:
        h2UpgradePolicy: DO_NOT_UPGRADE
        maxRequestsPerConnection: 100
    outlierDetection:
      consecutive5xxErrors: 3
      interval: 30s
      baseEjectionTime: 60s
      maxEjectionPercent: 100&lt;/code&gt;&lt;/pre&gt;
&amp;lt;!-- /wp:code --&amp;gt;

&amp;lt;!-- wp:paragraph --&amp;gt;
&lt;p&gt;This configuration caps outbound connections to Stripe at 50, sets a 5-second connection timeout, and ejects endpoints that return 3 consecutive 5xx errors. In production, this prevents a degraded third-party API from consuming all your connection slots and causing a domino effect across your services.&lt;/p&gt;
&amp;lt;!-- /wp:paragraph --&amp;gt;

&amp;lt;!-- wp:heading {&quot;level&quot;:3} --&amp;gt;
&lt;h3&gt;TLS Origination&lt;/h3&gt;
&amp;lt;!-- /wp:heading --&amp;gt;

&amp;lt;!-- wp:paragraph --&amp;gt;
&lt;p&gt;Sometimes your application speaks plain HTTP, but the external service requires HTTPS. Instead of modifying application code, you can offload TLS origination to the sidecar.&lt;/p&gt;
&amp;lt;!-- /wp:paragraph --&amp;gt;

&amp;lt;!-- wp:code --&amp;gt;
&lt;pre&gt;&lt;code class=&quot;language-yaml&quot;&gt;apiVersion: networking.istio.io/v1
kind: ServiceEntry
metadata:
  name: external-api
  namespace: default
spec:
  hosts:
    - api.external-service.com
  location: MESH_EXTERNAL
  ports:
    - number: 80
      name: http
      protocol: HTTP
    - number: 443
      name: https
      protocol: TLS
  resolution: DNS
---
apiVersion: networking.istio.io/v1
kind: DestinationRule
metadata:
  name: external-api-tls
  namespace: default
spec:
  host: api.external-service.com
  trafficPolicy:
    portLevelSettings:
      - port:
          number: 443
        tls:
          mode: SIMPLE&lt;/code&gt;&lt;/pre&gt;
&amp;lt;!-- /wp:code --&amp;gt;

&amp;lt;!-- wp:paragraph --&amp;gt;
&lt;p&gt;Your application sends HTTP to port 80. A VirtualService (shown in the next section) redirects that to port 443. The DestinationRule initiates TLS to the external endpoint. The application never knows TLS happened.&lt;/p&gt;
&amp;lt;!-- /wp:paragraph --&amp;gt;

&amp;lt;!-- wp:heading --&amp;gt;
&lt;h2&gt;Combining ServiceEntry with VirtualService&lt;/h2&gt;
&amp;lt;!-- /wp:heading --&amp;gt;

&amp;lt;!-- wp:paragraph --&amp;gt;
&lt;p&gt;VirtualService gives you Layer 7 traffic management for external services: retries, timeouts, fault injection, header-based routing, and traffic shifting. This is invaluable when you are migrating between API providers or need resilience policies for unreliable external dependencies.&lt;/p&gt;
&amp;lt;!-- /wp:paragraph --&amp;gt;

&amp;lt;!-- wp:heading {&quot;level&quot;:3} --&amp;gt;
&lt;h3&gt;Retries and Timeouts&lt;/h3&gt;
&amp;lt;!-- /wp:heading --&amp;gt;

&amp;lt;!-- wp:code --&amp;gt;
&lt;pre&gt;&lt;code class=&quot;language-yaml&quot;&gt;apiVersion: networking.istio.io/v1
kind: VirtualService
metadata:
  name: stripe-api-vs
  namespace: payments
spec:
  hosts:
    - api.stripe.com
  http:
    - route:
        - destination:
            host: api.stripe.com
            port:
              number: 443
      timeout: 10s
      retries:
        attempts: 3
        perTryTimeout: 3s
        retryOn: connect-failure,refused-stream,unavailable,cancelled,retriable&lt;/code&gt;&lt;/pre&gt;
</description>
<category>Other</category>
<guid isPermaLink="true">https://coderlegion.com/14650/istio-serviceentry-explained-external-services-dns-and-traffic-control</guid>
<pubDate>Tue, 14 Apr 2026 20:49:24 +0000</pubDate>
</item>
<item>
<title>Flowplane - A control plane for envoy</title>
<link>https://coderlegion.com/10766/flowplane-a-control-plane-for-envoy</link>
<description>&lt;p&gt;&lt;img src=&quot;https://coderlegion.com/?qa=blob&amp;amp;qa_blobid=18250612332956805035&quot; alt=&quot;&quot;&gt;&lt;/p&gt;
&lt;h3 id=&quot;what-is-flowplane-&quot;&gt;What is Flowplane?&lt;/h3&gt;
&lt;p&gt;Flowplane is a dynamic Envoy control plane that provides REST APIs for managing proxy configuration without writing raw protobuf. It translates high-level JSON resource definitions into Envoy's xDS protocol, enabling teams to configure clusters, routes, listeners, and filters through a standard API.&lt;/p&gt;
&lt;p&gt;The platform addresses three challenges faced by teams operating Envoy proxies:&lt;/p&gt;
&lt;p&gt;Configuration Complexity: Envoy requires protobuf-based xDS configuration that demands deep protocol knowledge. Flowplane exposes REST endpoints for clusters, routes, listeners, and filters, translating JSON payloads into the underlying xDS resources (LDS, RDS, CDS, EDS, SDS) that Envoy consumes via gRPC.&lt;/p&gt;
&lt;p&gt;Undocumented APIs: Services in production often lack accurate schema documentation. Flowplane's learning sessions capture traffic samples through Envoy's Access Log Service and External Processor, then infer JSON schemas from observed request/response patterns—extracting type information without persisting actual&lt;br&gt;
payload data.&lt;/p&gt;
&lt;p&gt;Multi-tenant Isolation: Shared proxy infrastructure needs proper team boundaries. Flowplane scopes all resources to teams, enforces authorisation through token-based access control with fine-grained scopes, and provides audit logging for security compliance.&lt;/p&gt;
&lt;p&gt;The system provides three core capabilities: Configure (REST API and Web UI for managing xDS resources), Import (OpenAPI specifications materialised directly into routes and clusters), and Learn (traffic-based schema inference through learning sessions). These translate into Envoy configuration delivered via a Tonic-based gRPC xDS server supporting ADS, LDS, RDS, CDS, EDS, and SDS protocols.&lt;/p&gt;
&lt;p&gt;Flowplane supports 15 HTTP filter types including JWT authentication, OAuth2, CORS, local and distributed rate limiting, header mutation, custom response handling, external authorization, RBAC, and health checks—all configurable through structured JSON rather than protobuf.&lt;/p&gt;
&lt;h3 id=&quot;features&quot;&gt;Features&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;xDS Server&lt;/strong&gt; - gRPC-based configuration server for Envoy proxies (ADS, LDS, RDS, CDS, EDS, SDS)&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;REST API&lt;/strong&gt; - Management API for clusters, listeners, routes, filters, and secrets&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Web UI&lt;/strong&gt; - SvelteKit dashboard for resource management and monitoring&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Multi-tenant&lt;/strong&gt; - Team-based resource isolation with RBAC&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;HTTP Filters&lt;/strong&gt; - 15 filters including JWT Auth, OAuth2, Rate Limit, CORS, Header Mutation&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;API Learning&lt;/strong&gt; - Infer API schemas from traffic via ExtProc and Access Logs&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Observability&lt;/strong&gt; - OpenTelemetry tracing, Prometheus metrics&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Security&lt;/strong&gt; - OAuth2, JWT, mTLS with Vault PKI integration&lt;/li&gt;
&lt;/ul&gt;
&lt;h4 id=&quot;import-openapi&quot;&gt;Import OpenApi&lt;/h4&gt;
&lt;p&gt;&lt;img src=&quot;docs/images/import.gif&quot; alt=&quot;Import OpenAppi&quot;&gt;&lt;/p&gt;
&lt;h4 id=&quot;api-learning&quot;&gt;API Learning&lt;/h4&gt;
&lt;p&gt;&lt;img src=&quot;docs/images/learning-session.gif&quot; alt=&quot;Learning API shape through requests&quot;&gt;&lt;/p&gt;
&lt;h3 id=&quot;requirements&quot;&gt;Requirements&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;Rust (edition 2021)&lt;/li&gt;
&lt;li&gt;Node.js 18+ (for UI)&lt;/li&gt;
&lt;li&gt;SQLite (default) or PostgreSQL&lt;/li&gt;
&lt;li&gt;protoc (Protocol Buffers compiler)&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 id=&quot;quick-start&quot;&gt;Quick Start&lt;/h3&gt;
&lt;h4 id=&quot;docker-recommended-&quot;&gt;Docker (Recommended)&lt;/h4&gt;
&lt;pre&gt;&lt;code class=&quot;language-bash&quot;&gt;docker run -d \
  --name flowplane \
  -p 8080:8080 \
  -p 50051:50051 \
  -v flowplane_data:/app/data \
  -e FLOWPLANE_DATABASE_URL=sqlite:///app/data/flowplane.db \
  ghcr.io/rajeevramani/flowplane:latest
&lt;/code&gt;&lt;/pre&gt;
&lt;h5 id=&quot;access-points&quot;&gt;Access Points&lt;/h5&gt;
&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;&lt;th&gt;Service    &lt;/th&gt;&lt;th&gt;URL&lt;/th&gt;&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;&lt;td&gt;API        &lt;/td&gt;&lt;td&gt;&lt;a rel=&quot;nofollow&quot; href=&quot;http://localhost:8080/api/v1/&quot;&gt;http://localhost:8080/api/v1/&lt;/a&gt;&lt;/td&gt;&lt;/tr&gt;
&lt;tr&gt;&lt;td&gt;UI         &lt;/td&gt;&lt;td&gt;&lt;a rel=&quot;nofollow&quot; href=&quot;http://localhost:8080/&quot;&gt;http://localhost:8080/&lt;/a&gt;&lt;/td&gt;&lt;/tr&gt;
&lt;tr&gt;&lt;td&gt;Swagger UI &lt;/td&gt;&lt;td&gt;&lt;a rel=&quot;nofollow&quot; href=&quot;http://localhost:8080/swagger-ui/&quot;&gt;http://localhost:8080/swagger-ui/&lt;/a&gt;&lt;/td&gt;&lt;/tr&gt;
&lt;tr&gt;&lt;td&gt;xDS (gRPC) &lt;/td&gt;&lt;td&gt;localhost:50051&lt;/td&gt;&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;p&gt;&lt;strong&gt;Ports are configurable&lt;/strong&gt;: &lt;code&gt;FLOWPLANE_API_PORT&lt;/code&gt; (default: 8080), &lt;code&gt;FLOWPLANE_XDS_PORT&lt;/code&gt; (default: 50051 in Docker, 18000 for local dev). See &lt;a rel=&quot;nofollow&quot; href=&quot;docs/configuration.md&quot;&gt;Configuration&lt;/a&gt;.&lt;/p&gt;
&lt;h4 id=&quot;development-with-docker-compose&quot;&gt;Development with Docker Compose&lt;/h4&gt;
&lt;p&gt;If you've cloned the repository, use &lt;code&gt;make&lt;/code&gt; for easier management:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-bash&quot;&gt;# Start backend + UI
make up

# Start with tracing (Jaeger)
make up-tracing

# Start with mTLS (Vault)
make up-mtls

# Add httpbin test service
make up HTTPBIN=1

# Stop all services
make down

# View all options
make help
&lt;/code&gt;&lt;/pre&gt;
&lt;h4 id=&quot;binary&quot;&gt;Binary&lt;/h4&gt;
&lt;p&gt;Download from &lt;a rel=&quot;nofollow&quot; href=&quot;https://github.com/flowplane-ai/flowplane/releases&quot;&gt;GitHub Releases&lt;/a&gt;:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-bash&quot;&gt;# Linux (x86_64)
curl -LO https://github.com/flowplane-ai/flowplane/releases/latest/download/flowplane-x86_64-unknown-linux-gnu.tar.gz
tar xzf flowplane-x86_64-unknown-linux-gnu.tar.gz

# macOS (Apple Silicon)
curl -LO https://github.com/flowplane-ai/flowplane/releases/latest/download/flowplane-aarch64-apple-darwin.tar.gz
tar xzf flowplane-aarch64-apple-darwin.tar.gz

# Run
./flowplane-*/flowplane
&lt;/code&gt;&lt;/pre&gt;
&lt;h4 id=&quot;platform-setup&quot;&gt;Platform Setup&lt;/h4&gt;
&lt;p&gt;On first startup, a setup token appears in the logs. Use it to create your first API token:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-bash&quot;&gt;# Initialize with setup token
curl --request POST \
  --url http://localhost:8080/api/v1/bootstrap/initialize \
  --header 'content-type: application/json' \
  --data '{
  &quot;email&quot;: &quot;*Emails are not allowed*&quot;,
  &quot;password&quot;: &quot;YOUR_PASSW0RD!&quot;,
  &quot;name&quot;: &quot;Admin User&quot;
}'

# Login
curl --request POST \
  --url http://localhost:8080/api/v1/auth/login \
  --header 'content-type: application/json' \
  --data '{
  &quot;email&quot;: &quot;*Emails are not allowed*&quot;,
  &quot;password&quot;: &quot;YOUR_PASSW0RD!&quot;
}'

# Token
curl --request POST \
  --url http://localhost:8080/api/v1/tokens \
  --header 'authorization: Bearer TOKEN_FROM_PREVIOUS_STEP' \
  --header 'content-type: application/json' \
  --header 'x-csrf-token: CSRF_TOKEN_FROM_PREVIOUS_STEP' \
  --data '{
  &quot;name&quot;: &quot;admin-api-token&quot;,
  &quot;description&quot;: &quot;Token with admin:all scope for testing&quot;,
  &quot;scopes&quot;: [
    &quot;admin:all&quot;
  ]
}'

&lt;/code&gt;&lt;/pre&gt;
&lt;h4 id=&quot;create-dataplane-and-boot-envoy&quot;&gt;Create Dataplane and Boot Envoy&lt;/h4&gt;
&lt;p&gt;A &lt;strong&gt;Dataplane&lt;/strong&gt; represents an Envoy proxy instance. Create one first, then generate bootstrap config.&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-bash&quot;&gt;# Create a dataplane
curl --request POST \
  --url http://localhost:8080/api/v1/teams/platform-admin/dataplanes \
  --header 'authorization: Bearer API_TOKEN_FROM_PREVIOUS_STEP' \
  --header 'content-type: application/json' \
  --data '{
  &quot;team&quot;: &quot;platform-admin&quot;,
  &quot;name&quot;: &quot;default&quot;,
  &quot;gatewayHost&quot;: &quot;127.0.0.1&quot;,
  &quot;description&quot;: &quot;Default dataplane for local development&quot;
}'

# Generate Envoy bootstrap config from the dataplane
curl --request GET \
  --url 'http://localhost:8080/api/v1/teams/platform-admin/dataplanes/default/bootstrap' \
  --header 'authorization: Bearer API_TOKEN_FROM_PREVIOUS_STEP' \
  &amp;gt; envoy.yaml

# Run Envoy
envoy -c envoy.yaml
&lt;/code&gt;&lt;/pre&gt;
&lt;h5 id=&quot;config-from-ui&quot;&gt;Config from UI&lt;/h5&gt;
&lt;p&gt;&lt;img src=&quot;docs/images/export-envoy-config.gif&quot; alt=&quot;Envoy Config&quot;&gt;&lt;/p&gt;
&lt;h4 id=&quot;create-the-first-api&quot;&gt;Create the first API&lt;/h4&gt;
&lt;pre&gt;&lt;code class=&quot;language-bash&quot;&gt;# Create a cluster
curl --request POST \
  --url http://localhost:8080/api/v1/clusters \
  --header 'authorization: Bearer API_TOKEN_FROM_PREVIOUS_STEP' \
  --header 'content-type: application/json' \
  --data '{
  &quot;team&quot;: &quot;platform-admin&quot;,
  &quot;name&quot;: &quot;httpbin-cluster&quot;,
  &quot;serviceName&quot;: &quot;httpbin-service&quot;,
  &quot;endpoints&quot;: [
    {
      &quot;host&quot;: &quot;httpbin.org&quot;,
      &quot;port&quot;: 443
    }
  ],
  &quot;connectTimeoutSeconds&quot;: 5,
  &quot;useTls&quot;: true,
  &quot;lbPolicy&quot;: &quot;ROUND_ROBIN&quot;
}'

# Create a route-config
curl --request POST \
  --url http://localhost:8080/api/v1/route-configs \
  --header 'authorization: Bearer API_TOKEN_FROM_PREVIOUS_STEP' \
  --header 'content-type: application/json' \
  --data '{
  &quot;name&quot;: &quot;httpbin-route&quot;,
  &quot;team&quot;: &quot;platform-admin&quot;,
  &quot;virtualHosts&quot;: [
    {
      &quot;name&quot;: &quot;httpbin-vhost&quot;,
      &quot;domains&quot;: [&quot;*&quot;],
      &quot;routes&quot;: [
        {
          &quot;name&quot;: &quot;httpbin-route&quot;,
          &quot;match&quot;: {
            &quot;path&quot;: {
              &quot;type&quot;: &quot;prefix&quot;,
              &quot;value&quot;: &quot;/&quot;
            }
          },
          &quot;action&quot;: {
            &quot;type&quot;: &quot;forward&quot;,
            &quot;cluster&quot;: &quot;httpbin-cluster&quot;,
            &quot;timeoutSeconds&quot;: 30
          }
        }
      ]
    }
  ]
}'

# Create a listener (associated with the dataplane)
curl --request POST \
  --url http://localhost:8080/api/v1/listeners \
  --header 'authorization: Bearer API_TOKEN_FROM_PREVIOUS_STEP' \
  --header 'content-type: application/json' \
  --data '{
  &quot;name&quot;: &quot;httpbin-listener&quot;,
  &quot;address&quot;: &quot;0.0.0.0&quot;,
  &quot;port&quot;: 10000,
  &quot;team&quot;: &quot;platform-admin&quot;,
  &quot;dataplaneId&quot;: &quot;DATAPLANE_ID_FROM_CREATE_RESPONSE&quot;,
  &quot;protocol&quot;: &quot;HTTP&quot;,
  &quot;filterChains&quot;: [
    {
      &quot;name&quot;: &quot;default&quot;,
      &quot;filters&quot;: [
        {
          &quot;name&quot;: &quot;envoy.filters.network.http_connection_manager&quot;,
          &quot;type&quot;: &quot;httpConnectionManager&quot;,
          &quot;routeConfigName&quot;: &quot;httpbin-route&quot;,
          &quot;httpFilters&quot;: [
            {
              &quot;filter&quot;: {
                &quot;type&quot;: &quot;router&quot;
              }
            }
          ]
        }
      ]
    }
  ]
}'

# Test the proxy
curl http://localhost:10000/get
&lt;/code&gt;&lt;/pre&gt;
&lt;h3 id=&quot;architecture&quot;&gt;Architecture&lt;/h3&gt;
&lt;pre&gt;&lt;code class=&quot;language-mermaid&quot;&gt;graph TD
    User[User / CI/CD] --&amp;gt;|REST / UI| API[API Server]

    subgraph &quot;Flowplane Control Plane&quot;
        direction TB

        API

        subgraph &quot;Core xDS Engine&quot;
            XDS[xDS Server]
            State[xDS State Cache]
            SecretReg[Secret Backend Registry]
            FilterReg[Filter Registry]
        end

        subgraph &quot;Learning Engine&quot;
            ALS[Access Log Service]
            ExtProc[External Processor]
            Worker[Access Log Processor]
            SchemaAgg[Schema Aggregator]
            SessionMgr[Learning Session Service]
        end

        API --&amp;gt;|Read/Write| DB[(Database)]

        %% State Sync Flow
        State --&amp;gt;|Poll/Refresh| DB
        XDS --&amp;gt;|Read| State

        %% Secret Flow
        SecretReg --&amp;gt;|Fetch| ExtSecrets
        State -.-&amp;gt;|Resolve| SecretReg

        %% Learning Flow
        ALS --&amp;gt;|Stream| SessionMgr
        ExtProc --&amp;gt;|Stream| SessionMgr
        SessionMgr --&amp;gt;|Batch| Worker
        Worker --&amp;gt;|Infer| SchemaAgg
        SchemaAgg --&amp;gt;|Persist| DB
    end

    subgraph &quot;Data Plane&quot;
        Envoy[Envoy Proxy]
    end

    %% External Dependencies
    ExtSecrets[External Secrets\n Vault / AWS / GCP]
    PKI[Vault PKI]

    %% Flows
    Envoy --&amp;gt;|gRPC ADS/xDS| XDS
    Envoy --&amp;gt;|gRPC ALS| ALS
    Envoy --&amp;gt;|gRPC ExtProc| ExtProc

    %% Security
    XDS -.-&amp;gt;|mTLS Auth| PKI
    Envoy -.-&amp;gt;|mTLS Identity| PKI

    %% Traffic
    Envoy --&amp;gt;|Traffic| Upstream[Upstream Services]
&lt;/code&gt;&lt;/pre&gt;
</description>
<category>Launches</category>
<guid isPermaLink="true">https://coderlegion.com/10766/flowplane-a-control-plane-for-envoy</guid>
<pubDate>Fri, 30 Jan 2026 11:41:18 +0000</pubDate>
</item>
</channel>
</rss>