Skip to content

Migrate from Envoy

Run an existing Envoy bootstrap under Arion, read the refusals it produces, and reconnect to your xDS control plane.

Request path

What you will build

ARION 1your listeners2your filters3router client your clusters

Outcome

This guide takes a configuration written for Envoy and runs it under Arion — first as a static file, then attached to the xDS control plane you already operate. Use it when you have working Envoy config and want to move it, not rewrite it.

The compatibility promise

If Arion accepts a configuration meant for Envoy, it behaves essentially like Envoy: filters run in the expected order and requests are routed or rejected as Envoy would. If Arion cannot honour part of a config the same way, it refuses to start when loading from a file, or rejects the update when it arrives over xDS — it never silently ignores what you wrote. The reasoning behind this is laid out in Envoy compatibility.

Prerequisites

  • Your Envoy bootstrap YAML.
  • An arion binary — see install.

Run your bootstrap under Arion

There are two ways in, and neither changes the Envoy dialect itself.

The first is to wrap the bootstrap in an Arion file: everything that was top-level in Envoy moves under an envoy_bootstrap: key, leaving room for Arion-native keys such as logging beside it. A complete minimal example:

envoy_bootstrap:
  static_resources:
    listeners:
      - name: http_8080
        address:
          socket_address: { address: 0.0.0.0, port_value: 8080 }
        filter_chains:
          - name: http_8080_chain
            filters:
              - name: http_gateway
                typed_config:
                  "@type": type.googleapis.com/envoy.extensions.filters.network.http_connection_manager.v3.HttpConnectionManager
                  http_filters:
                    - name: envoy.filters.http.router
                      typed_config:
                        "@type": type.googleapis.com/envoy.extensions.filters.http.router.v3.Router
                  route_config:
                    name: http_8080_routes
                    virtual_hosts:
                      - name: backend
                        domains: ["*"]
                        routes:
                          - match:
                              prefix: /
                            route:
                              cluster: backend
    clusters:
      - name: backend
        connect_timeout: 5s
        type: STATIC
        lb_policy: ROUND_ROBIN
        load_assignment:
          cluster_name: backend
          endpoints:
            - lb_endpoints:
                - endpoint:
                    address:
                      socket_address: { address: 127.0.0.1, port_value: 5678 }
arion --config arion.yaml

The second way skips the wrapper entirely: point Arion straight at the unmodified Envoy file with --with-envoy-bootstrap.

arion --with-envoy-bootstrap envoy.yaml

The two flags combine — --config supplies Arion-native settings while --with-envoy-bootstrap overrides whatever bootstrap the config file carried. Inside a config file the same exclusivity holds: a file sets envoy_bootstrap or Arion's native bootstrap key, never both.

Nothing inside the bootstrap is translated by hand. Filter names, @type URLs, route match shapes, cluster fields — everything keeps its Envoy spelling, which is what makes the move reversible while you evaluate. Two dialect notes from real migrations: Arion requires name on resources Envoy lets you leave anonymous, filter chains especially, so name them before converting; and the connection manager's stat_prefix, mandatory in Envoy, is accepted but ignored with a startup warning.

Read the refusals

The first run of a real-world bootstrap usually fails, deliberately. Suppose a cluster named web carries outlier_detection. Arion exits with an error that names the field and where it sits:

Error parsing field static_resources / clusters ["web"]
Unsupported field: outlier_detection

The reaction is always the same: delete the field or replace it with a supported equivalent, consult the supported configuration reference for what the surrounding resource accepts, and run again until Arion starts. An unsupported filter or extension fails as Unsupported enum variant naming its @type URL; a key Envoy itself does not know fails earlier, as a YAML error naming the position. Each refusal is a statement that Arion could not have honoured that line the way Envoy does — not a parser bug to work around.

Once it starts, treat that as the halfway point, not the finish. Run Arion beside the Envoy it replaces — on a different port or host — send the same requests through both, and compare status codes, routing decisions and access logs before cutting traffic over. Refusals catch what Arion will not do; a side-by-side soak is how you confirm what it does.

Under your control plane

Dynamic configuration is the same story: Envoy's bootstrap shape, consumed unchanged, and to the control plane Arion looks like one more Envoy client identified by its node.id and node.cluster. Arion connects through ads_config, with a static cluster carrying the xDS server address:

envoy_bootstrap:
  node:
    id: arion-1
    cluster: edge
  dynamic_resources:
    ads_config:
      grpc_services:
        - envoy_grpc:
            cluster_name: xds_cluster
  static_resources:
    clusters:
      - name: xds_cluster
        connect_timeout: 0.25s
        type: STATIC
        typed_extension_protocol_options:
          envoy.extensions.upstreams.http.v3.HttpProtocolOptions:
            "@type": type.googleapis.com/envoy.extensions.upstreams.http.v3.HttpProtocolOptions
            explicit_http_config:
              http2_protocol_options: {}
        load_assignment:
          cluster_name: xds_cluster
          endpoints:
            - lb_endpoints:
                - endpoint:
                    address:
                      socket_address: { address: 127.0.0.1, port_value: 50051 }

Arion speaks the incremental (delta) variant of ADS, so the control plane must serve delta streams. Over that stream it subscribes to listeners, clusters, routes, endpoints and secrets — LDS, CDS, RDS, EDS and SDS — so a listener delivered dynamically may use rds with config_source: { ads: {} } and a route_config_name exactly as under Envoy. Everything must flow through ADS: separate lds_config or cds_config sources are refused at startup. When the control plane pushes a resource Arion cannot honour, the compatibility promise switches from refusing to start to rejecting the update: Arion NACKs, carrying the same error text back over the stream for the control plane to log, and the invalid resource is not applied. Setup detail is in xDS control planes.

Commonly hit unsupported areas

Fields that make working Envoy configs fail their first Arion run, all refused at load rather than ignored:

  • ext_authz, as an HTTP or network filter — no external authorization callout exists.
  • Cluster outlier_detection — active health checks are the ejection mechanism.
  • Cluster type: LOGICAL_DNS — use STRICT_DNS or STATIC.
  • Access log json_format — write a text_format whose template is a JSON object instead.
  • Bootstrap-level layered_runtime, overload_manager, stats_config, watchdog, cluster_manager and hds_config.
  • lds_config / cds_config in dynamic_resources — ADS only.

The list is not exhaustive; the supported configuration reference is the authoritative inventory.

Next

Read Envoy compatibility for what the guarantee promises and why, then move real traffic with the L7 load balancer pattern under Arion.

Next

Take the configuration further

Continue

Start typing to search the site.

↑ ↓ to move Enter to open Esc to close