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.