Extensions
Envoy's architecture is built around extensions — pluggable components that handle
specific concerns like HTTP filtering, transport sockets, and config sources. Each
extension is identified by a name (e.g. envoy.filters.http.router) and/or a
type URL (e.g. type.googleapis.com/envoy.extensions.filters.http.router.v3.Router).
Armeria follows the same model. Extensions are resolved from a registry by type URL
(primary) or name (fallback). Built-in extensions cover the core xDS functionality,
and custom extensions can be added via the Java ServiceLoader mechanism.
HTTP filter extensions
HTTP filters are the most common extension point. They intercept requests on both the client side (see Client — Request flow) and the server side (see Server).
Built-in filters
The following HTTP filters are bundled with Armeria:
| Name | Description |
|---|---|
envoy.filters.http.router | Route matching and endpoint selection (client), route gating (server). Always required as the last filter in http_filters. |
envoy.filters.http.credential_injector | Injects credentials (e.g. from SDS secrets) into outgoing requests. |
Any other filter name referenced in http_filters or upstream_http_filters
must be registered as a custom extension (see below), or marked as optional
in the xDS config to be silently skipped. An unregistered, non-optional filter
causes configuration resolution to fail.
Custom filters
To create a custom HTTP filter, implement
HttpFilterFactory and
XdsHttpFilter.
HttpFilterFactory
A factory that creates an XdsHttpFilter from an xDS HttpFilter config.
Factories are responsible for all config parsing, including unwrapping per-route
overrides. Returning null from create causes the filter to be silently skipped.
import com.linecorp.armeria.xds.filter.FactoryContext;
import com.linecorp.armeria.xds.filter.HttpFilterFactory;
import com.linecorp.armeria.xds.filter.XdsHttpFilter;
public class MyFilterFactory implements HttpFilterFactory {
@Override
public String name() {
return "my.custom.filter";
}
@Override
public List<String> typeUrls() {
return List.of("type.googleapis.com/my.custom.filter.v1.Config");
}
@Override
@Nullable
public XdsHttpFilter create(HttpFilter httpFilter, Any config,
FactoryContext context) {
MyConfig myConfig = context.validator().unpack(config, MyConfig.class);
return new MyFilter(myConfig);
}
}
XdsHttpFilter
The resolved filter returned by the factory. Override the methods relevant to your use case:
| Method | Used for |
|---|---|
httpDecorator() | Client-side filtering (both downstream http_filters and upstream upstream_http_filters). Also applies to RPC clients (Thrift, gRPC) at the HTTP layer. |
serviceDecorator() | Server-side filtering |
import com.linecorp.armeria.xds.filter.XdsHttpFilter;
public class MyFilter implements XdsHttpFilter {
@Override
public DecoratingHttpClientFunction httpDecorator() {
return (delegate, ctx, req) -> {
// client-side filter logic
return delegate.execute(ctx, req);
};
}
@Override
public DecoratingHttpServiceFunction serviceDecorator() {
return (delegate, ctx, req) -> {
// server-side filter logic
return delegate.serve(ctx, req);
};
}
}
Registration
Register your factory using Java's ServiceLoader via
XdsExtensionFactoryProvider. Create a provider class:
package com.example;
import com.linecorp.armeria.xds.XdsExtensionFactory;
import com.linecorp.armeria.xds.XdsExtensionFactoryProvider;
public class MyFilterFactoryProvider implements XdsExtensionFactoryProvider {
@Override
public XdsExtensionFactory newFactory() {
return new MyFilterFactory();
}
}
Then create the file:
META-INF/services/com.linecorp.armeria.xds.XdsExtensionFactoryProvider
With the fully qualified class name of your provider:
com.example.MyFilterFactoryProvider
The factory will be discovered automatically when XdsBootstrap is created.
Usage
Once registered, reference your filter by name and type URL in either the
http_filters list (downstream) or the Router's upstream_http_filters list
(upstream):
listeners:
- name: my-listener
api_listener:
api_listener:
"@type": type.googleapis.com/envoy.extensions.filters.network.http_connection_manager.v3.HttpConnectionManager
http_filters:
- name: my.custom.filter # ← downstream filter
typed_config:
"@type": type.googleapis.com/my.custom.filter.v1.Config
some_field: some_value
- name: envoy.filters.http.router
typed_config:
"@type": type.googleapis.com/envoy.extensions.filters.http.router.v3.Router
upstream_http_filters:
- name: my.custom.filter # ← upstream filter
typed_config:
"@type": type.googleapis.com/my.custom.filter.v1.Config
some_field: some_value
route_config:
name: route
virtual_hosts:
- name: my-vhost
domains: ["*"]
routes:
- match: { prefix: "/" }
route: { cluster: my-cluster }
Downstream filters wrap the entire retry loop and are invoked once per request. Upstream filters run inside each retry attempt. Both are executed in the order they are declared.
Cluster type extensions
Custom cluster types allow xDS clusters to resolve endpoints from sources other
than static addresses, DNS, or EDS. Implement ClusterTypeFactory and
register it via XdsExtensionFactoryProvider.
Kubernetes endpoint discovery
The armeria-xds-kubernetes module provides a built-in cluster type that resolves
endpoints from Kubernetes services. Add the dependency:
dependencies {
implementation 'com.linecorp.armeria:armeria-xds-kubernetes'
}
Then reference it in your cluster configuration:
clusters:
- name: my-service
cluster_type:
name: armeria.cluster.kubernetes
typed_config:
"@type": type.googleapis.com/armeria.xds.kubernetes.KubernetesClusterConfig
service_name: my-service
namespace: production
port_name: http
mode: POD
| Field | Description |
|---|---|
service_name | The Kubernetes Service to watch (required) |
namespace | Kubernetes namespace; defaults to the client's default namespace |
port_name | Port name to select; uses the first port if empty |
mode | POD (default) for direct pod IPs, or NODE_PORT for NodePort/LoadBalancer access |
api_server_url | Custom Kubernetes API server URL; uses kubeconfig/in-cluster default if unset |
credential | SDS secret config for bearer token authentication |
Config source extensions
Custom config sources allow xDS resources to be loaded from sources other than
ADS or file paths. Implement SotwConfigSourceSubscriptionFactory and
register it via XdsExtensionFactoryProvider.
Spring Boot integration
The armeria-spring-boot4-xds module uses Spring properties as a control plane
for xDS resources. Instead of connecting to an external control plane, listeners
and clusters are defined as Spring Environment properties and served to the
xDS module via a built-in custom_config_source. Add the dependency:
dependencies {
implementation 'com.linecorp.armeria:armeria-spring-boot4-xds'
}
Defining resources
Spring properties can act as a config source for any xDS resource type. The
SpringConfigSourceFactory reads properties matching a configured prefix
and parses each value as the corresponding xDS resource. For example, the
default bootstrap fetches listeners from armeria.xds.listener.* and clusters
from armeria.xds.cluster.*:
armeria:
xds:
listener:
my-listener: |
name: my-listener
api_listener:
api_listener:
"@type": ...HttpConnectionManager
# ...
cluster:
my-cluster: |
name: my-cluster
type: STATIC
# ...
These are ordinary Spring properties — they can come from application.yml,
environment variables, Spring Cloud Config Server, or any other property source.
When the Spring environment changes (e.g. via EnvironmentChangeEvent),
resources are automatically refreshed.
Customizing the bootstrap
The bootstrap itself is read from the armeria.xds.bootstrap property. A
default bootstrap is provided that configures both LDS and CDS to use
SpringConfigSourceFactory. Override it to mix config sources — for
example, defining listeners in Spring properties while fetching clusters and
endpoints from a remote control plane:
armeria:
xds:
bootstrap: |
dynamic_resources:
lds_config:
custom_config_source:
name: armeria.config_source.spring
typed_config:
"@type": type.googleapis.com/armeria.xds.spring.SpringConfigSource
prefix: "armeria.xds.listener."
cds_config:
ads: {}
ads_config:
api_type: GRPC
grpc_services:
- envoy_grpc:
cluster_name: xds-cluster
static_resources:
clusters:
- name: xds-cluster
type: STATIC
load_assignment:
cluster_name: xds-cluster
endpoints:
- lb_endpoints:
- endpoint:
address:
socket_address:
address: control-plane.example.com
port_value: 15010
In this setup, listeners are managed as Spring properties and can be updated via Spring Cloud Config Server, while clusters and endpoints are discovered dynamically from the control plane.