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
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
andSERVER_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"]
-
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;'
-
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/;
}
}
}
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"]
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;'
.env
Example
DEBUG=True
CLIENT_MAX_BODY_SIZE=5
SERVER_IP=12.13.13.14
Conclusion
With this setup:
- You can deploy NGINX with just one command:
docker compose up -d
- 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)
π‘ There are significant differences between development and production environments. Striving to make them as similar as possible ensures faster deployments with fewer errors. π
Absolutely incredible! π».