Docker Nginx Reverse Proxy
Are you looking to set up a Nginx container using Docker on your MacOS machine? Look no further! In this article, we'll walk you through the process of creating and configuring a Docker Nginx container, including setting nginx configuration as Reverse Proxy, API Gateway, http redirection & ssl/tls termination.
Create Self Signed Certificate
Pre-requisite: openssl must be installed in MacOS
openssl req -x509 -nodes -days 365 \
-subj "/C=DE/ST=Berlin/L=Berlin/O=appdev24/OU=dev/CN=nginx.demo" \
-newkey rsa:4096 -keyout selfsigned.key \
-out selfsigned.crt
-subj switch option:
- Country Name (2 letter code): DE
- State or Province Name (full name): Berlin
- Locality Name (city): Berlin
- Organization Name (company): appdev24
- Organizational Unit Name (department): dev
- Common Name (server FQDN): nginx.demo
Update hosts file
sudo vi /etc/hosts
# Append the server entry
127.0.0.1 nginx.demo
Create Nginx configuration
nginx-reverseproxy.conf
user nginx;
worker_processes auto;
error_log /var/log/nginx/error.log notice;
pid /var/run/nginx.pid;
events {
worker_connections 1024;
}
http {
include /etc/nginx/mime.types;
default_type application/octet-stream;
log_format main '$remote_addr - $remote_user [$time_local] "$request" '
'$status $body_bytes_sent "$http_referer" '
'"$http_user_agent" "$http_x_forwarded_for"';
access_log /var/log/nginx/access.log main;
sendfile on;
keepalive_timeout 65;
# HTTP Redirect
server {
listen 80 default_server;
listen [::]:80 default_server;
server_name _;
return 301 https://$host$request_uri;
}
# Reverse Proxy
upstream products {
server host.docker.internal:5000;
}
upstream customers {
server host.docker.internal:3000;
}
# SSL/TLS Termination
server {
listen 443 ssl;
listen [::]:443 ssl;
server_name nginx.test;
root /usr/share/nginx/html;
index index.html;
ssl_session_cache shared:SSL:10m;
ssl_session_timeout 4h;
ssl_protocols TLSv1 TLSv1.1 TLSv1.2 TLSv1.3;
ssl_ciphers HIGH:!aNULL:!MD5;
ssl_prefer_server_ciphers off;
ssl_certificate /etc/ssl/certs/selfsigned.crt;
ssl_certificate_key /etc/ssl/private/selfsigned.key;
# Proxy Microservices
proxy_http_version 1.1;
proxy_cache_bypass $http_upgrade;
proxy_set_header Upgrade $http_upgrade;
proxy_set_header Connection 'upgrade';
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;
location /api/products {
proxy_pass http://products;
}
location /api/customers {
proxy_pass http://customers;
}
# Basic Status Info
location /stub_status {
stub_status on;
}
}
}
Create Demo Python API Microservice
requirements.txt
flask
app.py
import json
from flask import Flask, jsonify
app = Flask(__name__)
products = [
{ 'id': 1, 'name': 'Cakes' },
{ 'id': 2, 'name': 'Cookies' },
{ 'id': 3, 'name': 'Chocolates' },
{ 'id': 4, 'name': 'Milk' },
{ 'id': 5, 'name': 'Coffee' },
{ 'id': 6, 'name': 'Soft Drinks' },
{ 'id': 7, 'name': 'Ice cream' }
]
@app.route('/api/products', methods=['GET'])
def get_products():
return jsonify(products)
if __name__ == '__main__':
app.run(debug=True)
Python-Dockerfile
# syntax=docker/dockerfile:1
FROM python:3.12.4-slim
WORKDIR /usr/src/app
COPY requirements.txt ./
RUN pip3 install --no-cache-dir -r requirements.txt
COPY app.py .
CMD [ "python", "-m" , "flask", "run", "--host=0.0.0.0"]
Create Demo Nodejs API Microservice
package.json
{
"name": "nginx-demo",
"version": "1.0.0",
"description": "nginx reverse proxy & API gateway demo",
"main": "app.js",
"scripts": {
"start": "node app.js",
"test": "echo \"Error: no test specified\" && exit 1"
},
"repository": {
"type": "git",
"url": "git+https://github.com/sarubhai/nginx-demo.git"
},
"keywords": [
"Node.js",
"Nginx Reverse Proxy",
"Nginx API Gateway",
"REST API"
],
"author": "Saurav Mitra <saurav.karate@gmail.com>",
"license": "ISC",
"homepage": "https://appdev24.com/pages/44/docker-nginx-reverse-proxy",
"dependencies": {
"express": "^4.19.2"
}
}
Also download the package-lock.json file from https://github.com/sarubhai/nginx-demo/blob/main/package-lock.json
app.js
const express = require('express');
const app = express();
app.get("/api/customers", (req, res, next) => {
const customers = [
{ id: 1, name: "John Doe", age: 30 },
{ id: 2, name: "Jane Smith", age: 25 },
{ id: 3, name: "Mike Johnson", age: 40 },
{ id: 4, name: "Jane Doe", age: 28 },
{ id: 5, name: "Micheal Page", age: 36 },
{ id: 6, name: "Peter Alter", age: 39 }
];
res.json(customers);
});
app.listen(3000, () => {
console.log("Server running on port 3000");
});
Nodejs-Dockerfile
# syntax=docker/dockerfile:1
FROM node:20.15.0-slim
WORKDIR /usr/src/app
COPY package*.json ./
RUN npm install
COPY app.js .
EXPOSE 3000
CMD [ "node", "app.js" ]
Create Docker Compose file
reverseproxy-docker-compose.yml
version: '3.9'
services:
nginx-reverseproxy:
# Apple M1 Chip
platform: linux/amd64
image: nginx:latest
container_name: nginx-reverseproxy
restart: always
ports:
- 80:80
- 443:443
volumes:
- ./nginx-reverseproxy.conf:/etc/nginx/nginx.conf:ro
- ./selfsigned.crt:/etc/ssl/certs/selfsigned.crt:ro
- ./selfsigned.key:/etc/ssl/private/selfsigned.key:ro
networks:
- backend-network
ms1-python-app:
# Apple M1 Chip
platform: linux/amd64
build:
context: .
dockerfile: Python-Dockerfile
container_name: ms1-python-app
restart: always
ports:
- 5000:5000
networks:
- backend-network
ms2-nodejs-app:
# Apple M1 Chip
platform: linux/amd64
build:
context: .
dockerfile: Nodejs-Dockerfile
container_name: ms2-nodejs-app
restart: always
ports:
- 3000:3000
networks:
- backend-network
networks:
backend-network:
driver: bridge
Start the Container
Now that we have our configuration and Docker compose file set up, it's time to start the containers! Run the following command:
docker-compose -f reverseproxy-docker-compose.yml up -d --build
This will start the containers in detached mode, meaning they will run in the background.
Test Nginx Reverse Proxy Container
Once the container is running, we can test our webserver by browsing to https://nginx.demo/api/products & https://nginx.demo/api/customers
Optionally you can download the self signed certificate from
Chrome > Developer Tools > Security Tab > View Certificate
and import into MacOS System Keychain to Always Trust the self signed certificate and avoid the certificate warning.
Stop the Container
Finally, we can stop the containers by running the following command:
docker-compose -f loadbalancer-docker-compose.yml down
This will stop the containers and remove it from memory.
That's it! We've successfully created and configured a Docker Nginx container as a Reverse Proxy along with SSL/TLS Termination and HTTP Redirect. It also acts as an API Gateway for two backend API Services.