DEV Community

Cover image for Deploy NGINX on multiple Environments
Mai Chi Bao
Mai Chi Bao

Posted on

Deploy NGINX on multiple Environments

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.

Top comments (2)

Collapse
 
mrzaizai2k profile image
Mai Chi Bao

πŸ’‘ There are significant differences between development and production environments. Striving to make them as similar as possible ensures faster deployments with fewer errors. πŸš€

Collapse
 
aniruddhadak profile image
ANIRUDDHA

Absolutely incredible! 😻.