Envoy is a high-performance, cloud-native L4/L7 proxy designed for service mesh architectures and API gateway deployments. Originally built by Lyft, it is the data plane of Istio and is used by organisations running microservices on Kubernetes and bare metal alike. Unlike Nginx or HAProxy, Envoy is designed to run as a sidecar alongside each service, providing uniform observability, traffic management, and security policy enforcement. This tutorial walks through installing Envoy on RHEL 9, creating a working proxy configuration, and exploring its administration interface.
Prerequisites
- RHEL 9 server with root or sudo access
- Internet access to pull packages or container images
- Docker or Podman installed (for the container-based install option)
- A backend HTTP service to proxy (e.g., a simple Python HTTP server on port 8080)
- Basic familiarity with YAML and proxy concepts
Step 1 — Install Envoy on RHEL 9
The recommended method for RHEL 9 is the Tetrate GetEnvoy package repository, which provides a native RPM binary:
# Add the GetEnvoy repository
curl -sL https://getenvoy.io/gpg | gpg --dearmor -o /usr/share/keyrings/getenvoy.gpg
cat > /etc/yum.repos.d/getenvoy.repo << 'EOF'
[getenvoy]
name=GetEnvoy
baseurl=https://tetrate.bintray.com/getenvoy-rpm/rhel/9/x86_64
enabled=1
gpgcheck=1
gpgkey=file:///usr/share/keyrings/getenvoy.gpg
EOF
dnf install -y getenvoy-envoy
envoy --version
Alternatively, run Envoy via Podman without installing a package:
podman pull docker.io/envoyproxy/envoy:v1.30-latest
podman run --rm envoyproxy/envoy:v1.30-latest --version
Step 2 — Create a Basic Envoy Configuration
Envoy is configured entirely through a YAML bootstrap file. The following configuration creates a listener on port 10000 that proxies HTTP traffic to a backend on 127.0.0.1:8080:
cat > /etc/envoy/envoy.yaml << 'EOF'
static_resources:
listeners:
- name: listener_0
address:
socket_address:
address: 0.0.0.0
port_value: 10000
filter_chains:
- filters:
- name: envoy.filters.network.http_connection_manager
typed_config:
"@type": type.googleapis.com/envoy.extensions.filters.network.http_connection_manager.v3.HttpConnectionManager
stat_prefix: ingress_http
access_log:
- name: envoy.access_loggers.stdout
typed_config:
"@type": type.googleapis.com/envoy.extensions.access_loggers.stream.v3.StdoutAccessLog
http_filters:
- name: envoy.filters.http.router
typed_config:
"@type": type.googleapis.com/envoy.extensions.filters.http.router.v3.Router
route_config:
name: local_route
virtual_hosts:
- name: backend
domains: ["*"]
routes:
- match:
prefix: "/"
route:
cluster: backend_service
timeout: 10s
clusters:
- name: backend_service
connect_timeout: 5s
type: STATIC
lb_policy: ROUND_ROBIN
load_assignment:
cluster_name: backend_service
endpoints:
- lb_endpoints:
- endpoint:
address:
socket_address:
address: 127.0.0.1
port_value: 8080
health_checks:
- timeout: 1s
interval: 5s
unhealthy_threshold: 2
healthy_threshold: 2
http_health_check:
path: "/healthz"
admin:
address:
socket_address:
address: 127.0.0.1
port_value: 9901
EOF
mkdir -p /etc/envoy
Step 3 — Start a Backend Service and Run Envoy
Start a simple backend to proxy to:
# Start a minimal Python HTTP server on port 8080 in the background
python3 -m http.server 8080 &
# Run Envoy with the configuration file
envoy -c /etc/envoy/envoy.yaml
# Or with Podman, mounting the config file
podman run --rm --network host
-v /etc/envoy/envoy.yaml:/etc/envoy/envoy.yaml:Z
envoyproxy/envoy:v1.30-latest
-c /etc/envoy/envoy.yaml
Test that the proxy is forwarding requests:
curl -s http://localhost:10000/
Step 4 — Explore the Admin Interface
The admin interface on port 9901 exposes rich diagnostic information without requiring any authentication configuration in the bootstrap file (restrict it to localhost in production, as it has no auth by default):
# View all registered listeners
curl -s http://localhost:9901/listeners
# View cluster membership and health status
curl -s http://localhost:9901/clusters
# View Prometheus-compatible metrics
curl -s http://localhost:9901/stats/prometheus
# View the currently loaded configuration as JSON
curl -s http://localhost:9901/config_dump | python3 -m json.tool | head -80
# Gracefully drain connections (for rolling restarts)
curl -X POST http://localhost:9901/drain_listeners
Step 5 — Create a systemd Service Unit
To run Envoy as a managed system service:
cat > /etc/systemd/system/envoy.service << 'EOF'
[Unit]
Description=Envoy Proxy
After=network.target
[Service]
ExecStart=/usr/local/bin/envoy -c /etc/envoy/envoy.yaml
Restart=on-failure
RestartSec=5
User=envoy
NoNewPrivileges=true
ProtectSystem=full
PrivateTmp=true
[Install]
WantedBy=multi-user.target
EOF
useradd --system --no-create-home envoy
systemctl daemon-reload
systemctl enable --now envoy
systemctl status envoy
Conclusion
You have installed Envoy on RHEL 9, created a static bootstrap configuration that proxies HTTP requests with health checking, started Envoy as a systemd service, and navigated the admin API to inspect listeners, clusters, and live metrics. Unlike traditional reverse proxies, Envoy is designed to be configured dynamically at scale via xDS APIs in a service mesh, with this static configuration serving as a solid foundation for understanding its architecture.
Next steps: How to Deploy Istio Service Mesh on Kubernetes, How to Configure TLS Termination in Envoy, and How to Set Up a Complete DevSecOps Pipeline on RHEL 9.