Deploying Python Applications with Nginx and systemd: A Production-Ready Approach
When deploying a backend application (FastAPI, Flask, Django, or any Python service) to a cloud server like AWS EC2, simply running:
python app.py
is not enough.
In real-world production environments, we need:
- ✔ Reliability
- ✔ Auto-restart on crash
- ✔ Proper web server handling
- ✔ Secure HTTPS
- ✔ Clean configuration
This is where Nginx and systemd come into play.
In this article, we’ll walk through:
- What Nginx does
- What systemd does
- Why you should use both together
- Best configuration practices
- Reload vs restart explained
What is systemd (systemctl)?
systemd is the service manager in modern Linux systems. It allows you to run your backend application as a background service.
Instead of running:
python app.py
You create a service file:
[Unit]
Description=ABC
After=network.target
[Service]
User=ec2-user
WorkingDirectory=/var/www/ABC
ExecStart=/home/ec2-user/.local/bin/uv run python app.py
Restart=always
Environment="PATH=/home/ec2-user/.local/bin:/usr/bin"
[Install]
WantedBy=multi-user.target
Benefits of systemd:
- ✅ Auto-start on server boot
- ✅ Restart on crash
- ✅ No terminal needed
- ✅ Production standard
Common commands:
systemctl start ABC.service
systemctl stop ABC.service
systemctl restart ABC.service
systemctl status ABC.service
What is Nginx?
Nginx is a high-performance web server and reverse proxy.
It handles:
- HTTP/HTTPS traffic
- Static files (HTML, CSS, images)
- Routing to backend services
Python servers alone are not designed for:
- ❌ SSL handling
- ❌ Heavy traffic
- ❌ Static file performance
Nginx solves all these problems.
Production Architecture
User Browser
↓
Nginx
↓
Python App (systemd service)
Flow:
- User makes request
- Nginx receives it
- Static files served directly
- API forwarded to backend
Why use sites-available instead of nginx.conf?
Many beginners put everything in:
/etc/nginx/nginx.conf
This works but is not scalable.
Best practice:
/etc/nginx/
├── nginx.conf
├── sites-available/
│ └── ABC
└── sites-enabled/
└── ABC
nginx.conf should contain only global settings:
http {
include /etc/nginx/mime.types;
include /etc/nginx/sites-enabled/*;
}
Actual site config goes in:
/etc/nginx/sites-available/ABC
- ✔ Clean
- ✔ Organized
- ✔ Scalable
Understanding Nginx proxy_pass
One of the most powerful features of Nginx is its ability to act as a reverse proxy using the proxy_pass directive.
Trailing Slash Behavior
With trailing slash
proxy_pass http://localhost:8080/;
Request:
/images/pic.png
Forwarded as:
http://localhost:8080/pic.png
👉 The /images/ part is replaced.
Without trailing slash
proxy_pass http://localhost:8080;
Forwarded as:
http://localhost:8080/images/pic.png
👉 The full URI is preserved.
Rule of Thumb
| proxy_pass format | Backend receives |
|---|---|
| with slash / | Replaces location path |
| without slash | Keeps full URI |
Basic Reverse Proxy Configuration
server {
listen 80;
server_name your_domain.com www.your_domain.com;
location / {
proxy_pass http://localhost:8080;
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;
}
}
Why These Headers Matter
- ✔ Correct client info
- ✔ Proper logging
- ✔ Secure application logic
Reload vs Restart
Reload
systemctl reload nginx
- ✔ Applies config changes
- ✔ No downtime
- ✔ Best for updates
Restart
systemctl restart nginx
- ⛔ Stops service
- ⛔ Starts again
- ⛔ Brief downtime
Use only if reload fails.
Typical Production Workflow
git pull origin main
systemctl restart ABC.service
Nginx config update:
nginx -t
systemctl reload nginx
Conclusion
For deploying Python applications on Linux:
- ✔ Use systemd for backend services
- ✔ Use Nginx as reverse proxy
- ✔ Use sites-available structure
This avoids:
- ❌ Downtime
- ❌ Security risks
- ❌ Messy configs
And prepares your app for real-world scale.
Happy deploying 🚀