Install NGINX with Docker: Web Server, Reverse Proxy and HTTPS

· 1 min read · Docker & Rancher Desktop Tutorials

What is NGINX #

NGINX is a fast web server that is also used as a reverse proxy and load balancer. A reverse proxy receives requests from the internet and forwards them to your applications, so you can add TLS, caching and routing in one place. This tutorial runs it in Docker in three roles.

Serve a static site #

Create a folder with a page:

mkdir -p site && echo "<h1>Hello from NGINX</h1>" > site/index.html
docker run -d --name web -p 8080:80 -v "$PWD/site:/usr/share/nginx/html:ro" nginx:stable

Open http://localhost:8080. The image serves /usr/share/nginx/html and reads its configuration from /etc/nginx/conf.d/.

Use your own configuration #

nginx.conf
server {
    listen 80;
    server_name _;

    root /usr/share/nginx/html;
    index index.html;

    location / {
        try_files $uri $uri/ =404;
    }

    location ~* \.(css|js|png|jpg|svg)$ {
        expires 7d;
        add_header Cache-Control "public";
    }
}

Mount it as a server block and test the syntax before reloading:

docker run -d --name web -p 8080:80 \
  -v "$PWD/site:/usr/share/nginx/html:ro" \
  -v "$PWD/nginx.conf:/etc/nginx/conf.d/default.conf:ro" nginx:stable
docker exec web nginx -t
docker exec web nginx -s reload

Reverse proxy and load balancing #

Put NGINX in front of two replicas of an application. The upstream block lists them and NGINX distributes the requests round robin.

proxy.conf
upstream app {
    server app1:3000;
    server app2:3000;
}

server {
    listen 80;

    location / {
        proxy_pass http://app;
        proxy_set_header Host $host;
        proxy_set_header X-Real-IP $remote_addr;
        proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
        proxy_set_header X-Forwarded-Proto $scheme;
    }
}
compose.yaml
services:
  proxy:
    image: nginx:stable
    ports:
      - "80:80"
    volumes:
      - ./proxy.conf:/etc/nginx/conf.d/default.conf:ro
    depends_on: [app1, app2]
  app1:
    image: myapp:1.0
  app2:
    image: myapp:1.0

Only the proxy publishes a port. The applications are reachable just through the Compose network (see Docker networking). On AWS the equivalent managed service is the Elastic Load Balancing.

HTTPS #

Add a second server block with your certificate and key mounted into the container:

tls.conf
server {
    listen 443 ssl;
    http2 on;
    server_name example.com;

    ssl_certificate     /etc/nginx/certs/fullchain.pem;
    ssl_certificate_key /etc/nginx/certs/privkey.pem;
    ssl_protocols       TLSv1.2 TLSv1.3;

    location / {
        proxy_pass http://app;
    }
}

server {
    listen 80;
    server_name example.com;
    return 301 https://$host$request_uri;
}

For free certificates use Let's Encrypt with certbot, or an AWS Certificate Manager certificate on a load balancer. For local development create a self-signed one with openssl req -x509 -nodes -newkey rsa:2048 -days 365 -keyout privkey.pem -out fullchain.pem -subj "/CN=localhost".

Logs and troubleshooting #

docker logs -f web          # access and error logs go to stdout and stderr
docker exec web nginx -T    # print the whole effective configuration

A 502 Bad Gateway means NGINX reached no healthy upstream: check the names in upstream and that the application listens on 0.0.0.0, not 127.0.0.1.

Alternatives #

In Kubernetes, ingress controllers such as Traefik or the NGINX ingress controller play this role.

#Docker #Nginx #Web Server