Envoy Proxy is a high-performance, cloud-native Layer 7 proxy originally built at Lyft and now a CNCF graduated project. Unlike Nginx or HAProxy, Envoy is designed with dynamic configuration via xDS APIs and deep observability baked in, making it the proxy of choice for service meshes such as Istio. On RHEL 8 you can run Envoy as a standalone edge proxy, a sidecar for a specific application, or as the data plane for a full service mesh. This guide installs Envoy using the GetEnvoy repository, writes a minimal static YAML configuration, and demonstrates HTTP proxying, upstream cluster definitions, and health checking.

Prerequisites

  • RHEL 8 server with root or sudo access and internet connectivity
  • Basic familiarity with YAML syntax
  • An upstream application to proxy (the examples use a local httpd on port 8080 as the backend)
  • Podman or Docker available if you prefer the container-based installation path
  • Ports 10000 (listener) and 9901 (admin UI) available

Step 1 — Install Envoy via the GetEnvoy Repository

The GetEnvoy project (now maintained by Tetrate) provides pre-built Envoy binaries packaged as an RPM for RHEL/CentOS.

# Add the GetEnvoy (Tetrate) repository
curl -sL https://rpm.tetratelabs.io/getenvoy/release/rpm/el8/getenvoy.repo 
  -o /etc/yum.repos.d/getenvoy.repo

dnf install -y getenvoy-envoy

# Verify the installation
envoy --version

If you prefer not to add a third-party repository, you can run Envoy as a container instead. Pull the official image and use podman run with a volume mount for the configuration file — see the alternative below.

# Alternative: container-based Envoy (no RPM needed)
dnf install -y podman
podman pull docker.io/envoyproxy/envoy:v1.29-latest
# Run: podman run --rm -p 10000:10000 -p 9901:9901 
#        -v /etc/envoy/envoy.yaml:/etc/envoy/envoy.yaml:ro 
#        envoyproxy/envoy:v1.29-latest

Step 2 — Write a Minimal Envoy Configuration File

Create /etc/envoy/envoy.yaml. This configuration defines an admin interface, a single listener on port 10000 with an HTTP connection manager filter, and a static cluster pointing to the upstream backend on port 8080.

mkdir -p /etc/envoy

cat > /etc/envoy/envoy.yaml << 'EOF'
admin:
  address:
    socket_address:
      address: 0.0.0.0
      port_value: 9901

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

  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: 3s
          interval: 10s
          unhealthy_threshold: 2
          healthy_threshold: 1
          http_health_check:
            path: /health
EOF

Step 3 — Validate the Configuration and Start Envoy

Use the --mode validate flag to catch YAML and schema errors before launching the process.

envoy --config-path /etc/envoy/envoy.yaml --mode validate
echo "Config OK: $?"

# Start Envoy in the foreground (Ctrl-C to stop)
envoy --config-path /etc/envoy/envoy.yaml --log-level info

To run Envoy as a persistent background service, create a systemd unit file at /etc/systemd/system/envoy.service:

cat > /etc/systemd/system/envoy.service << 'EOF'
[Unit]
Description=Envoy Proxy
After=network.target

[Service]
User=root
ExecStart=/usr/bin/envoy --config-path /etc/envoy/envoy.yaml --log-level info
Restart=on-failure
RestartSec=5

[Install]
WantedBy=multi-user.target
EOF

systemctl daemon-reload
systemctl enable --now envoy
systemctl status envoy

Step 4 — Open the Firewall and Test Proxying

firewall-cmd --add-port=10000/tcp --permanent
firewall-cmd --add-port=9901/tcp  --permanent
firewall-cmd --reload

# Send a request through Envoy to the backend
curl -v http://localhost:10000/

# Check the Envoy admin UI for live stats
curl http://localhost:9901/stats | grep backend_service

# View active clusters and health check status
curl http://localhost:9901/clusters

The /stats endpoint exposes hundreds of counters including upstream_cx_total (total connections to the backend) and upstream_rq_2xx (successful responses). These metrics can be scraped directly by Prometheus using the /stats/prometheus endpoint.

Step 5 — Add a Second Upstream Endpoint for Load Balancing

Envoy’s static cluster definition accepts multiple endpoints. Add a second backend host to demonstrate round-robin load balancing without restarting Envoy (use xDS in production for zero-downtime updates).

# Edit /etc/envoy/envoy.yaml — extend lb_endpoints under backend_service:
#   - endpoint:
#       address:
#         socket_address:
#           address: 127.0.0.1
#           port_value: 8081

systemctl restart envoy

# Watch the round-robin in action
for i in $(seq 1 6); do curl -s http://localhost:10000/ | grep -o "server:[^<]*"; done

Step 6 — Envoy as a Sidecar Proxy (Service Mesh Overview)

In a service mesh pattern, an Envoy sidecar runs alongside every application instance in the same network namespace, intercepting all inbound and outbound traffic via iptables rules injected by a control plane (e.g., Istio’s istio-init container). The sidecar enforces mutual TLS, applies traffic policies, and emits telemetry to a central collector — all transparently to the application code. The same envoy.yaml structure used in this tutorial is generated dynamically by the control plane via the xDS (Discovery Service) API, allowing configuration updates without any restart.

# Inspect Envoy's xDS config dump from the admin API
curl http://localhost:9901/config_dump | python3 -m json.tool | head -80

Conclusion

You have installed Envoy Proxy on RHEL 8, written a static YAML configuration with an HTTP connection manager, defined an upstream cluster with health checking, and tested round-robin load balancing across two backend endpoints. The admin API at port 9901 provides rich runtime observability and integrates with Prometheus out of the box, making Envoy a powerful building block for both standalone proxy use cases and full service-mesh deployments.

Next steps: How to Deploy Istio Service Mesh on Kubernetes from RHEL 8, How to Collect Envoy Metrics with Prometheus on RHEL 8, and How to Set Up a Complete DevSecOps Pipeline on RHEL 8.