Why and what
Nginx accepts public HTTP/TLS traffic and forwards application requests to a local process. Gunicorn is a Python WSGI server; Flask's development server is not the intended production process manager. Express runs inside Node.js and needs a managed lifecycle. A reverse proxy does not automatically make application code secure or highly available.
Flask lab on Ubuntu
sudo apt install -y python3-venv
mkdir -p ~/academy/flask-demo
cd ~/academy/flask-demo
python3 -m venv .venv
. .venv/bin/activate
pip install flask gunicorn
cat > app.py <<'PY'
from flask import Flask
app = Flask(__name__)
@app.get('/')
def home():
return {'message': 'Flask behind Nginx'}
PY
gunicorn --bind 127.0.0.1:8000 app:appIn another terminal run curl http://127.0.0.1:8000/. Pin reviewed dependency versions in a lock/requirements workflow for repeatability. On AL2023 install the corresponding Python/pip packages available in the selected repository and create the same venv.
Express equivalent
Using a supported Node.js LTS installation, create a separate project:
mkdir -p ~/academy/express-demo
cd ~/academy/express-demo
npm init -y
npm install express
cat > app.js <<'JS'
const express = require('express');
const app = express();
app.get('/', (_req, res) => res.json({message:'Express behind Nginx'}));
app.listen(3000, '127.0.0.1');
JS
node app.jsReverse-proxy block
location / {
proxy_pass http://127.0.0.1:8000;
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;
}Use 3000 for Express. Configure application proxy trust only for known proxies; trusting arbitrary forwarded headers can let clients spoof scheme/IP information.
Service operation
Create a dedicated OS user and a systemd unit with WorkingDirectory, absolute ExecStart path, limited permissions and Restart=on-failure. Keep secrets outside the repository. Test syntax/reload Nginx, then test a reboot to prove the service starts without your SSH session.
Troubleshooting
A 502 may mean the app died or listens on a different port. Python import failures often mean wrong working directory/venv. Node failures may be dependency or syntax errors. Read application journal output before changing security groups.
Official references
Ravindra’s Tip
Browser में page दिखा इसलिए backend healthy है ऐसा मत मानो। API response और application logs भी check करो।
Interview and revision check
Why keep Gunicorn or Node behind a managed service?
A systemd-managed process can restart, log and survive SSH disconnects/reboots. An interactive shell process does not provide the same lifecycle.
Ravindra Bagale · Cloud & DevOps Academy · Handbook and project downloads