NGINX ingress proxy for Consul Service Mesh

Disclaimer: This blog post is an abridged (NGINX specific) version of the original blog by John Eikenberry at HashiCorp. 

Consul service mesh provides service-to-service connection authorization and encryption using mutual Transport Layer Security (mTLS). Secure mesh networks require ingress points to act as gateways to enable external traffic to communicate with the internal services. NGINX supports mTLS and can be configured as a robust ingress proxy for your mesh network. For Consul service mesh environments, you can use Consul-template to configure NGINX as a native ingress proxy. This can be beneficial when performance is of utmost importance.

Below you can walk through a complete example setup. The examples use consul-template to dynamically generate the proxy configuration and the required certificates for the NGINX to communicate directly with the services inside the mesh network.

This blog post is only meant to demonstrate features and may not be a production ready or secure deployment. Each of the services are configured for demonstration purposes and will run in the foreground and output its logs to that console. They are all designed to run from files in the same directory. In this blog post, we use the term ingress proxy to describe Nginx. When we use the term sidecar proxy, we mean the Consul Connect proxy for the internal service.

Common Infrastructure

The example setup below requires Consul, NGINX, and Python. The former is available at the link provided, NGINX and Python should be easily installable with the package manager on any Linux system.

The Ingress proxy needs a service to proxy to, for that you need Consul, a service (E.g., a simple webserver), and the Connect sidecar proxy to connect it to the mesh. The ingress proxy will also need the certificates to make the mTLS connection.

Start Consul

Connect, Consul’s service mesh feature, requires Consul 1.2.0 or newer. First start your agent.

$ consul agent -dev

Start a Webserver

For the webserver you will use Python’s simple built-in server included in most Linux distributions and Macs.

$ python -m SimpleHTTPServer

Python’s webserver will listen on port 8000 and publish an index.html by default. For demo purposes, create an index.html for it to publish.

$ echo "Hello from inside the mesh." > index.html

Next, you’ll need to register your “webserver” with Consul. Note, the Connect option registers a sidecar proxy with the service.

$ echo '{
  "service": {
    "name": "webserver",
    "connect": { "sidecarservice": {} },
    "port": 8000
}' > webserver.json

$ consul services register webserver.json

You also need to start the sidecar.

$ consul connect proxy -sidecar-for webserver

Note, the service is using the built-in sidecar proxy. In production you would probably want to consider using Envoy or NGINX instead.

Create the Certificate File Templates

To establish a connection with the mesh network, you’ll need to use consul-template to fetch the CA root certificate from the Consul servers as well as the applications leaf certificates, which Consul will generate.

You need to create the templates that consul-template will use to generate the certificate files needed for NGINX ingress proxy. The template functions caRoots and caLeaf require consul-template version 0.23.0 or newer.

Note, the name, “nginx” used in the leaf certificate templates. It needs to match the name used to register the ingress proxy services with Consul below.


NGINX requires the CA certificate.

$ echo '{{range caRoots}}{{.RootCertPEM}}{{end}}' > ca.crt.tmpl

Nginx cert.pem and cert.key

NGINX requires the certificate and key to be in separate files.

$ echo '{{with caLeaf "nginx"}}{{.CertPEM}}{{end}}' > cert.pem.tmpl
$ echo '{{with caLeaf "nginx"}}{{.PrivateKeyPEM}}{{end}}' > cert.key.tmpl

Setup The Ingress Proxies

NGINX proxy service needs to be registered with Consul and have a templated configuration file. In each of the templated configuration files, the connect function is called and returns a list of the services with the passed name “webserver” (matching the registered service above). The list Consul creates is used to create the list of back-end servers to which the ingress proxy connections.

The ports are set to different values so you can have both proxies running at the same time.

First, register the NGINX ingress proxy service with the Consul servers.

$ echo '{
  "service": {
    "name": "nginx",
    "port": 8081
}' > nginx-service.json

$ consul services register nginx-service.json

Next, configure the NGINX configuration file template. Set it to listen on the registered port and route to the Connect-enabled servers retrieved by the Connect call.


$ cat > nginx-proxy.conf.tmpl << EOF
daemon off;
master_process off;
error_log /dev/stdout;

events {}

http {
  access_log /dev/stdout;

  server {
    listen 8081 defaultserver;

    location / {
{{range connect "webserver"}}
      proxy_pass https://{{.Address}}:{{.Port}};
      # these refer to files written by templates above
      proxy_ssl_certificate cert.pem;
      proxy_ssl_certificate_key cert.key;
      proxy_ssl_trusted_certificate ca.crt;

Consul Template

The final piece of the puzzle, tying things together are the consul-template configuration files! These are written in HCL, the Hashicorp Configuration Language, and lay out the commands used to run the proxy, the template files, and their destination files.


$ cat > nginx-ingress-config.hcl << EOF
exec {
  command = "/usr/sbin/nginx -p . -c nginx-proxy.conf"
template {
  source = "ca.crt.tmpl"
  destination = "ca.crt"
template {
  source = "cert.pem.tmpl"
  destination = "cert.pem"
template {
  source = "cert.key.tmpl"
  destination = "cert.key"
template {
  source = "nginx-proxy.conf.tmpl"
  destination = "nginx-proxy.conf"

Running and Testing

You are now ready to run the consul-template managed NGINX ingress proxy. When you run consul-template, it will process each of the templates, fetching the certificate and server information from consul as needed, and render them to their destination files on disk. Once all the templates have been successfully rendered it will run the command starting the proxy.

Run the NGINX managing consul-template instance.

$ consul-template -config nginx-ingress-config.hcl

Now with everything running, you are finally ready to test the proxies.

$ curl http://localhost:8081
Hello from inside the mesh!


F5 technologies work very well in your Consul environments. In this blog post you walked through setting up NGINX to work as a proxy to provide ingress to services contained in a Consul service mesh. Please reach out to me and the F5-HashiCorp alliance team here if you have any questions, feature requests, or any feedback to make this solution better.

Published Feb 22, 2021
Version 1.0

Was this article helpful?

No CommentsBe the first to comment