Skip to main content

NGINX HTTP/2 Docker Ingress

Guangzhou, China

This article is an update to NGINX Docker Ingress for your Gatsby Build.

Github Repository

Setup

Get the repository from Github and save it e.g. in /opt - Note if you choose a different location you will have to adjust the docker run commands below accordingly:

cd /opt
git pull https://github.com/mpolinowski/nginx_docker_ingress.git
cd /nginx_docker_ingress

Check out the default configuration file that is included in the repository:

nano /conf.d/default.conf

This contains 2 server locations - I am using the ports 8080 and 8081 here so that they don't interfere with your other applications (e.g. if you already have a web server running) - but feel free to change them to the default ports 80 and 443:

server {
    listen 8080;
    listen [::]:8080;

...

server {
    listen 8081 ssl http2 default_server;
    listen [::]:8081 ssl;

Both of them are configured to listen for traffic on localhost - replace this address with your server domain. Or just leave it be for testing:

server_name localhost;

The first server location expects HTTP traffic on port 8080 and redirects this traffic to port 8081 expecting there to be the HTTP/2 endpoint:

return 301 https://$server_name:8081$request_uri;

The repository contains a self-signed TLS certificate that you can use for testing. For production run Certbot to generate a CA cert and replace the ssl/self-signed.conf include with it (Make sure to mount the Let's Encrypt certificate into the NGINX container!):

# ssl_certificate /opt/letsencrypt/live/my.domain.com/fullchain.pem;
# ssl_certificate_key /opt/letsencrypt/live/my.domain.com/privkey.pem;

include ssl/self-signed.conf; # Replace with the 2 lines above when using CA Cert
include ssl/ssl-params.conf;
include /etc/nginx/conf.d/header.conf;

Last but not least - define the location that should be proxied by NGINX. The default location is the NGINX welcome page:

location / {
    root   /usr/share/nginx/html;
    index  index.html index.htm;
}

Jump to running the container to do a test run - this path in /opt/nginx_docker_ingress the command below has to point to the repository:

docker run -p 8080:8080 -p 8081:8081 -v /opt/nginx_docker_ingress:/etc/nginx --name ingress nginx:1.25.0-alpine3.17

Once the container is up you should be able to access http://localhost:8080 and be redirected to https://localhost:8081 to see the NGINX welcome page:

NGINX HTTP/2 Docker Ingress

Networking

If your application is not running in a Docker container start the NGINX Ingress with --network host so that it has access to your host network:

docker run --rm --network host -v /opt/nginx_docker_ingress:/etc/nginx --name ingress nginx:1.25.0-alpine3.17

E.g. if you have an application running on http://localhost:3000 you can replace the NGINX location block with:

location / {
    proxy_pass http://127.0.0.1:3000/;
  }

Restart the container and revisit http://localhost:8080. Again, you will be redirected to https://localhost:8081 - but this time you will be seeing the web application beeing proxied by NGINX (If your browser still shows the NGINX welcome page do a hard refresh with CTRL + F5 to bypass the browser cache).

The problem here is that your app is now directly reachable over port 3000 and proxied on port 8081. Of course you can just firewall port 3000. But there is a better way.

Container Network

To have the NGINX container work as an ingress we first need to create a virtual network - I choose the name wikinet as this network will host our company wiki:

docker network create wikinet

The wiki frontend container - the app that I want to proxy - is called wiki_en. I can add this container to the virtual network with the following command:

docker network connect wikinet wiki_en

Let’s see if the container has been added:

docker network inspect wikinet

The output show us that our container has been added successfully:

[
  {
    "Name": "wikinet",
    "Id": "725dfcde3015d752f8d0c4bbfe2027d9a4cb1cb3c6cc9b4f4094fb33d5a1d6bc",
    "Created": "2021-05-10T15:07:38.420998455+08:00",
    "Scope": "local",
    "Driver": "bridge",
    "EnableIPv6": false,
    "IPAM": {
      "Driver": "default",
      "Options": {},
      "Config": [
        {
          "Subnet": "172.18.0.0/16",
          "Gateway": "172.18.0.1"
        }
      ]
    },
    "Internal": false,
    "Attachable": false,
    "Ingress": false,
    "ConfigFrom": {
      "Network": ""
    },
    "ConfigOnly": false,
    "Containers": {
      "e903e22b56f21973ade9bab9aa58aa8994b0a2399eadff36a4801fbc26fb82d4": {
        "Name": "wiki_en",
        "EndpointID": "82090263f6853a6219989828ade375a4db5aa655561293bf5bce0d7dfeb4640a",
        "MacAddress": "02:42:ac:12:00:02",
        "IPv4Address": "172.18.0.2/16",
        "IPv6Address": ""
      }
    },
    "Options": {},
    "Labels": {}
  }
]

The next step is to add the container to my ingresses proxy pass. Note that we can now use the Docker DNS service to connect the container by it's name:

location / {
    proxy_pass http://wiki_en:3000/;
  }

And to restart the NGINX ingress into the same virtual network:

docker run -d -p 8080:8080 -p 8081:8081 -v /opt/nginx_docker_ingress:/etc/nginx --network=wikinet --name nginx:1.25.0-alpine3.17

The application is now locked inside the virtual network and the NGINX proxy works as an ingress directing traffic in.