Deploy NGINX on multiple Environments

Mai Chi Bao - Nov 23 '24 - - Dev Community

Table of Contents:

Introduction

This post will walk you through deploying a web application with NGINX on both local and production environments using a single docker-compose command.


Main Takeaways

  • Deploy a web application using NGINX in both development and production environments.
  • Simplify deployment by automating configurations with Docker Compose.

The Norm Way: Deploying on an EC2 Server

If you want to deploy a web application on an EC2 server, here’s a step-by-step example. Below is the docker-compose configuration I used:

Docker Compose File

version: '3.8'

services:
  mongodb:
    image: mongo:latest
    container_name: mongodb
    volumes:
      - /data/test-change-streams:/data/db
    ports:
      - "27017:27017"
    networks:
      - app-network
    command: mongod --replSet test-change-streams --logpath /data/db/mongodb.log --dbpath /data/db --port 27017

  mongodb-setup:
    image: mongo:latest
    depends_on:
      - mongodb
    networks:
      - app-network
    command: >
      bash -c "
      sleep 10 &&
      mongosh --host mongodb:27017 --eval \"
      rs.initiate({
        _id: 'test-change-streams',
        members: [
          {_id: 0, host: 'mongodb:27017'}
        ]
      })
      \"
      "

  fastapi-app:
    image: multilanguage_invoice_ocr-fastapi-app
    build:
      context: .
      dockerfile: Dockerfile
    container_name: fastapi-app
    environment:
      - OPENAI_API_KEY=${OPENAI_API_KEY}
      - EMAIL_USER=${EMAIL_USER}
      - EMAIL_PASSWORD=${EMAIL_PASSWORD}
      - SECRET_KEY=${SECRET_KEY}
      - ALGORITHM=${ALGORITHM}
      - ACCESS_TOKEN_EXPIRE_MINUTES=${ACCESS_TOKEN_EXPIRE_MINUTES}
    volumes:
      - ./config:/app/config
      - ./src:/app/src
    ports:
      - "8149:8149"
    networks:
      - app-network
    depends_on:
      - mongodb
      - mongodb-setup

  jwt-frontend:
    image: multilanguage_invoice_ocr-jwt-frontend
    build:
      context: ./jwt-auth-frontend
      dockerfile: Dockerfile
    container_name: jwt-frontend
    volumes:
      - ./jwt-auth-frontend/src:/app/src
    ports:
      - "3000:3000"
    networks:
      - app-network
    depends_on:
      - fastapi-app

  nginx:
    build:
      context: ./nginx
    container_name: nginx
    volumes:
      - ./nginx/nginx.conf.template:/etc/nginx/nginx.conf.template:ro
    ports:
      - "80:80"
    networks:
      - app-network
    depends_on:
      - fastapi-app
      - jwt-frontend
      - mongodb
    environment:
      - CLIENT_MAX_BODY_SIZE=${CLIENT_MAX_BODY_SIZE}
      - SERVER_IP=${SERVER_IP}

networks:
  app-network:
    driver: bridge
Enter fullscreen mode Exit fullscreen mode

Explanation

The nginx service in the above configuration acts as a reverse proxy connecting to:

  • Frontend (jwt-frontend)
  • Backend (fastapi-app)
  • Database (mongodb)

Let’s focus on the NGINX service:

Key Features of NGINX in Docker Compose:

  • Service Name: nginx
  • Build Context: Builds the Docker image from ./nginx.
  • Container Name: nginx
  • Volumes: Mounts a read-only nginx.conf.template for configuration.
  • Ports: Exposes port 80.
  • Networks: Connects to a custom app-network.
  • Environment Variables: Passes variables such as CLIENT_MAX_BODY_SIZE and SERVER_IP.

Folder Structure for NGINX Configuration

Create a folder named nginx for all related configurations:

  • Dockerfile:
   FROM nginx:latest

   COPY start-nginx.sh /start-nginx.sh
   RUN chmod +x /start-nginx.sh
   ENTRYPOINT ["/start-Nginx.sh"]
Enter fullscreen mode Exit fullscreen mode
  • Startup Script (start-nginx.sh):
   #!/bin/bash
   envsubst '${CLIENT_MAX_BODY_SIZE} ${SERVER_IP}' < /etc/nginx/nginx.conf.template > /etc/nginx/nginx.conf
   nginx -g 'daemon off;'
Enter fullscreen mode Exit fullscreen mode
  • Nginx Config Template (nginx.conf.template):
   events {
       worker_connections 1024;
   }

   http {
       client_max_body_size ${CLIENT_MAX_BODY_SIZE};

       server {
           listen 80;
           server_name localhost ${SERVER_IP};

           location / {
               proxy_pass http://jwt-frontend:3000/;
               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/ {
               proxy_pass http://fastapi-app:8149/;
               proxy_read_timeout 300s;
               proxy_connect_timeout 75s;
           }

           location /mongo/ {
               proxy_pass http://mongodb:27017/;
           }
       }
   }
Enter fullscreen mode Exit fullscreen mode

The Advanced Way: Handling Multiple Environments

When deploying in production, you might need different configurations compared to local development. For example:

  • Production: Requires HTTPS on port 443.
  • Development: Simpler HTTP configuration on port 80.

Environment Differences

Feature Production Development
Port 443 (HTTPS), 80 (HTTP fallback) Only 80 (HTTP)
Protocol HTTPS (secure) HTTP (non-secure)
Server IP Domain name (e.g., xyz.com) Local or private IP (e.g., xx.xx.xx.xx)
SSL/TLS Enabled, with valid certificates Typically disabled
Environment Variables Loaded with production settings Uses development-specific configurations

New Dockerfile

FROM nginx:latest

RUN mkdir -p /etc/nginx/dev /etc/nginx/prod
COPY dev/nginx.conf.template /etc/nginx/dev/
COPY prod/nginx.conf.template /etc/nginx/prod/
COPY start-nginx.sh /start-nginx.sh
RUN chmod +x /start-nginx.sh
ENTRYPOINT ["/start-nginx.sh"]
Enter fullscreen mode Exit fullscreen mode

Updated start-nginx.sh

#!/bin/bash

if [ "$DEBUG" = "True" ]; then
    CONFIG_PATH="/etc/nginx/dev/nginx.conf.template"
else
    CONFIG_PATH="/etc/nginx/prod/nginx.conf.template"
fi

envsubst '${CLIENT_MAX_BODY_SIZE} ${SERVER_IP}' < $CONFIG_PATH > /etc/nginx/nginx.conf
exec nginx -g 'daemon off;'
Enter fullscreen mode Exit fullscreen mode

.env Example

DEBUG=True
CLIENT_MAX_BODY_SIZE=5
SERVER_IP=12.13.13.14
Enter fullscreen mode Exit fullscreen mode

Conclusion

With this setup:

  • You can deploy NGINX with just one command:
  docker compose up -d
Enter fullscreen mode Exit fullscreen mode
  • Your application will work seamlessly in both development and production environments.

Reference

Multilanguage Invoice OCR - Mrzaizai2k

More

If you’d like to learn more, be sure to check out my other posts and give me a like! It would mean a lot to me. Thank you.

. . . . . . . . . . . . . . . . . .