How to Host a Custom Python Web App on Ubuntu Server Overview of the Stack We will use: Ubuntu Server – Operating system
Python 3 – Application runtime
Virtualenv – Dependency isolation Gunicorn – Python WSGI application server Nginx – Reverse proxy & static file server systemd – Service management
UFW – Firewall
This setup is secure, scalable, and production-ready. 1. Initial Server Setup 1.1 Update the System Always start by updating your server:
sudo apt update && sudo apt upgrade -y
Reboot if required:
sudo reboot
1.2 Create a Non-Root User (Recommended)
sudo adduser webuser
sudo usermod -aG sudo webuser
su - webuser This improves security by avoiding root access. 1.3 Install Required Packages
sudo apt install -y python3 python3-pip python3-venv \
nginx git ufw Verify versions:
python3 --version
pip3 --version nginx -v 2. Firewall Configuration (UFW) Enable firewall and allow essential services:
sudo ufw allow OpenSSH
sudo ufw allow 'Nginx Full'
sudo ufw enable
Check status:
sudo ufw status
3. Prepare Your Python Web Application 3.1 Create Project Directory
mkdir -p ~/apps/myapp
cd ~/apps/myapp
Example structure: myapp/ │ ├── app.py ├── requirements.txt ├── wsgi.py └── venv/ 3.2 Create a Virtual Environment
python3 -m venv venv
source venv/bin/activate Upgrade pip:
pip install --upgrade pip
3.3 Install Application Dependencies Example for Flask:
pip install flask gunicorn
Save dependencies:
pip freeze > requirements.txt
3.4 Sample Flask App (app.py) from flask import Flask app = Flask(__name__) @app.route("/") def home(): return "Hello from Ubuntu Server!" 3.5 WSGI Entry Point (wsgi.py) from app import app if __name__ == "__main__": app.run() Test locally: gunicorn --bind 0.0.0.0:8000 wsgi:app Visit: http://SERVER_IP:8000 If it works, stop Gunicorn: CTRL + C 4. Configure Gunicorn as a systemd Service 4.1 Create Service File
sudo nano /etc/systemd/system/myapp.service
Paste: [Unit] Description=Gunicorn instance for myapp After=network.target [Service] User=webuser Group=www-data WorkingDirectory=/home/webuser/apps/myapp Environment="PATH=/home/webuser/apps/myapp/venv/bin" ExecStart=/home/webuser/apps/myapp/venv/bin/gunicorn \ --workers 3 \ --bind unix:/home/webuser/apps/myapp/myapp.sock \ wsgi:app [Install] WantedBy=multi-user.target Save and exit. 4.2 Start and Enable Service
sudo systemctl daemon-reload
sudo systemctl start myapp
sudo systemctl enable myapp
Check status:
sudo systemctl status myapp
5. Configure Nginx Reverse Proxy 5.1 Create Nginx Site Config
sudo nano /etc/nginx/sites-available/myapp
Paste: server { listen 80; server_name your_domain_or_server_ip; location / { include proxy_params; proxy_pass http://unix:/home/webuser/apps/myapp/myapp.sock; } } 5.2 Enable the Site
sudo ln -s /etc/nginx/sites-available/myapp /etc/nginx/sites-enabled
sudo nginx -t
sudo systemctl restart nginx
Visit: http://SERVER_IP Your Python app should now be live 🎉 6. Useful Management Commands Restart Services
sudo systemctl restart myapp
sudo systemctl restart nginx
View Logs journalctl -u myapp
sudo tail -f /var/log/nginx/error.log
Reload Nginx Without Downtime
sudo systemctl reload nginx
7. Common Troubleshooting Issue: 502 Bad Gateway Causes: Gunicorn not running Socket permissions wrong Wrong path in Nginx config Fix:
sudo systemctl status myapp
ls -l ~/apps/myapp/myapp.sock
Ensure group is www-data. Issue: ModuleNotFoundError Cause: Wrong virtualenv path or missing dependency Fix: source venv/bin/activate
pip install -r requirements.txt
Restart service. Issue: Permission Denied on Socket
sudo chown webuser:www-data myapp.sock
sudo chmod 660 myapp.sock
Issue: App Works with Gunicorn but Not Nginx Check Nginx error log:
sudo tail -n 50 /var/log/nginx/error.log
8. Security Best Practices Disable root login via SSH Use HTTPS (Let’s Encrypt + Certbot) Use environment variables for secrets Regularly update the server Install Certbot:
sudo apt install certbot python3-certbot-nginx
sudo certbot --nginx
9. Useful Ubuntu Server Commands htop # Resource usage df -h # Disk space free -m # Memory usage netstat -tulpn # Open ports ps aux | grep gunicorn 10. Summary You now have: A Python web app running in a virtual environment Gunicorn serving the app Nginx handling HTTP requests systemd managing uptime Firewall protecting your server This setup is production-ready and scalable for real-world applications.