How to Install and Configure Envoy Proxy on RHEL 7

Envoy Proxy has become a cornerstone of modern service mesh architectures, used as the data plane in Istio, the sidecar in Consul Connect, and the edge proxy behind many large-scale APIs. Written in C++ and designed for cloud-native workloads, Envoy provides L4/L7 load balancing, health checking, circuit breaking, retries, observability, and gRPC support in a single binary. While Envoy is typically discussed in the context of Kubernetes and containers, it runs perfectly well on bare-metal RHEL 7 hosts as a forward proxy, reverse proxy, or API gateway. This tutorial installs Envoy on RHEL 7 using a pre-built binary, configures it as a reverse proxy via static bootstrap YAML, and wraps it in a systemd service.

Prerequisites

  • RHEL 7 server with glibc 2.17 or later (the default on RHEL 7 is sufficient).
  • Root or sudo access.
  • At least one upstream application running locally (e.g., a web server on port 8080) to proxy to.
  • Internet access or a local mirror to download the Envoy binary (~140 MB).
  • curl installed (sudo yum install -y curl).

Step 1: Download the Envoy Binary

Envoy does not ship in standard RHEL 7 repositories. The easiest distribution mechanism for bare-metal Linux is the func-e installer from Tetrate or a pre-built static binary from the Envoy GitHub releases. For RHEL 7, we use func-e, which manages Envoy versions and is itself a single static binary:

# Download the func-e installer
curl -L https://func-e.io/install.sh | sudo bash -s -- -b /usr/local/bin

If your server has no internet access, download the Envoy binary directly from Tetrate builds (RHEL 7 compatible, built against glibc 2.17):

# Find the latest Linux x86_64 release from:
# https://github.com/tetratelabs/envoy-builds/releases
# Example for Envoy 1.29.x:
curl -Lo /tmp/envoy 
  https://github.com/tetratelabs/envoy-builds/releases/download/v1.29.2/envoy-1.29.2-linux-amd64

sudo install -m 755 /tmp/envoy /usr/local/bin/envoy
envoy --version

Expected output:

envoy  version: 8ec5db97-1ad5-4f54-9e27-3c/RELEASE/BoringSSL

Step 2: Create the Envoy Bootstrap Configuration

Envoy is configured entirely through a bootstrap YAML or JSON file passed at startup. Unlike Nginx or HAProxy, there are no include directives — the entire configuration lives in one structured file. Create the directory and configuration:

sudo mkdir -p /etc/envoy
sudo vi /etc/envoy/envoy.yaml

The following configuration sets up a reverse proxy that listens on port 10000 and routes all traffic to an upstream application on localhost:8080, with an admin interface on port 9901:

admin:
  address:
    socket_address:
      address: 127.0.0.1
      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
                codec_type: AUTO
                access_log:
                  - name: envoy.access_loggers.stdout
                    typed_config:
                      "@type": type.googleapis.com/envoy.extensions.access_loggers.stream.v3.StdoutAccessLog
                route_config:
                  name: local_route
                  virtual_hosts:
                    - name: local_service
                      domains:
                        - "*"
                      routes:
                        - match:
                            prefix: "/"
                          route:
                            cluster: upstream_app
                            timeout: 30s
                http_filters:
                  - name: envoy.filters.http.router
                    typed_config:
                      "@type": type.googleapis.com/envoy.extensions.filters.http.router.v3.Router

  clusters:
    - name: upstream_app
      connect_timeout: 5s
      type: LOGICAL_DNS
      dns_lookup_family: V4_ONLY
      lb_policy: ROUND_ROBIN
      load_assignment:
        cluster_name: upstream_app
        endpoints:
          - lb_endpoints:
              - endpoint:
                  address:
                    socket_address:
                      address: 127.0.0.1
                      port_value: 8080
      health_checks:
        - timeout: 5s
          interval: 10s
          unhealthy_threshold: 3
          healthy_threshold: 2
          http_health_check:
            path: "/health"

Step 3: Validate the Configuration

Envoy can validate a configuration file without starting:

envoy --mode validate -c /etc/envoy/envoy.yaml

Expected output: configuration '/etc/envoy/envoy.yaml' OK. Fix any YAML syntax errors before proceeding.

Step 4: Create a systemd Service Unit

Create a dedicated system user for Envoy and a systemd unit file:

sudo useradd -r -s /sbin/nologin envoy

sudo vi /etc/systemd/system/envoy.service
[Unit]
Description=Envoy Proxy
Documentation=https://www.envoyproxy.io/docs
After=network-online.target
Wants=network-online.target

[Service]
Type=simple
User=envoy
Group=envoy
ExecStart=/usr/local/bin/envoy 
  --config-path /etc/envoy/envoy.yaml 
  --log-level warn 
  --log-format "[%Y-%m-%d %T.%e][%t][%l][%n] %v"
ExecReload=/bin/kill -HUP $MAINPID
Restart=on-failure
RestartSec=5s
LimitNOFILE=65536
LimitNPROC=65536

[Install]
WantedBy=multi-user.target

Grant the envoy user read access to the configuration:

sudo chown -R root:envoy /etc/envoy
sudo chmod 640 /etc/envoy/envoy.yaml

Reload systemd and start the service:

sudo systemctl daemon-reload
sudo systemctl enable envoy
sudo systemctl start envoy
sudo systemctl status envoy

Step 5: Configure Load Balancing Across Multiple Upstream Instances

To distribute traffic across multiple backend instances (e.g., three application servers), expand the cluster’s lb_endpoints section:

  clusters:
    - name: upstream_app
      connect_timeout: 5s
      type: STATIC
      lb_policy: LEAST_REQUEST   # or ROUND_ROBIN, RANDOM, RING_HASH
      load_assignment:
        cluster_name: upstream_app
        endpoints:
          - lb_endpoints:
              - endpoint:
                  address:
                    socket_address:
                      address: 192.168.10.10
                      port_value: 8080
              - endpoint:
                  address:
                    socket_address:
                      address: 192.168.10.11
                      port_value: 8080
              - endpoint:
                  address:
                    socket_address:
                      address: 192.168.10.12
                      port_value: 8080

Available lb_policy values: ROUND_ROBIN, LEAST_REQUEST, RANDOM, RING_HASH, MAGLEV. RING_HASH and MAGLEV provide consistent hashing suitable for session-aware backends.

Step 6: Access Logging and the Admin Interface

The admin interface (port 9901) exposes runtime statistics, cluster health, and configuration dumps. Access it from localhost:

# View all statistics
curl http://127.0.0.1:9901/stats

# View cluster health
curl http://127.0.0.1:9901/clusters

# Dump the current config as JSON
curl http://127.0.0.1:9901/config_dump | python -m json.tool | head -60

# View live access logs
sudo journalctl -u envoy -f

Step 7: Configure gRPC Proxying

Envoy understands HTTP/2 and gRPC natively. To proxy gRPC traffic, set codec_type: HTTP2 on the listener and http2_protocol_options: {} on the cluster:

  clusters:
    - name: grpc_upstream
      connect_timeout: 5s
      type: LOGICAL_DNS
      lb_policy: ROUND_ROBIN
      http2_protocol_options: {}     # Force HTTP/2 (required for gRPC)
      load_assignment:
        cluster_name: grpc_upstream
        endpoints:
          - lb_endpoints:
              - endpoint:
                  address:
                    socket_address:
                      address: 127.0.0.1
                      port_value: 9090

Step 8: Open the Firewall

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

Do not open port 9901 externally — the admin interface has no authentication and must remain on localhost only.

Comparing Envoy with Nginx and HAProxy

All three are capable reverse proxies, but they serve different use cases:

  • Nginx: Mature, file-based configuration, excellent static file serving, low learning curve. No native gRPC, no dynamic xDS configuration.
  • HAProxy: Best-in-class L4 TCP load balancing, excellent health checking. Limited HTTP/2 and no native gRPC proxying. Configuration is not hot-reloadable without process replacement.
  • Envoy: Designed for dynamic service-to-service communication. Supports xDS APIs for live configuration updates, native gRPC, HTTP/3 (QUIC), distributed tracing, and circuit breaking. Higher initial complexity but unmatched extensibility for microservice environments.

Conclusion

You have installed Envoy Proxy on RHEL 7 using a pre-built binary, written a static bootstrap YAML configuring a listener, an upstream cluster with health checks, and access logging to stdout, and managed it as a systemd service under a dedicated user account. You explored multiple load balancing policies, the admin API, gRPC proxying requirements, and compared Envoy with Nginx and HAProxy. For production deployments, consider adding TLS termination on the listener using a transport_socket with DownstreamTlsContext, and integrating with your secrets management system to rotate certificates without restarting the proxy.