{"id":1749,"date":"2026-09-04T11:17:52","date_gmt":"2026-09-04T05:47:52","guid":{"rendered":"https:\/\/cpanelfree.com\/blog\/how-to-deploy-python-django-flask-app-gunicorn-nginx-ubuntu\/"},"modified":"2026-09-04T11:22:10","modified_gmt":"2026-09-04T05:52:10","slug":"how-to-deploy-python-django-flask-app-gunicorn-nginx-ubuntu","status":"publish","type":"post","link":"https:\/\/cpanelfree.com\/blog\/how-to-deploy-python-django-flask-app-gunicorn-nginx-ubuntu\/","title":{"rendered":"How to Deploy Python Django \/ Flask App with Gunicorn and Nginx on Ubuntu"},"content":{"rendered":"<div style=\"background-color: #f8fafc;border-left: 4px solid #a855f7;padding: 20px;border-radius: 6px;margin-bottom: 25px\">\n<p style=\"margin: 0;font-size: 16px;color: #1e293b\">\n        <strong>Quick Answer:<\/strong> To deploy a Python Django or Flask web application on Ubuntu: <strong>1)<\/strong> Create an isolated Python virtual environment (<code>python3 -m venv myenv<\/code>), <strong>2)<\/strong> Install <strong>Gunicorn<\/strong> as the WSGI server, <strong>3)<\/strong> Create a <strong>Systemd socket and service file<\/strong> to manage the Gunicorn daemon, and <strong>4)<\/strong> Configure <strong>Nginx<\/strong> to serve static\/media files directly and proxy dynamic requests to the Gunicorn UNIX socket.\n    <\/p>\n<\/div>\n<h2>Step 1: Set Up Python Virtual Environment &amp; Gunicorn<\/h2>\n<pre style=\"background-color: #1e293b;color: #38bdf8;padding: 14px;border-radius: 6px;font-size: 13px\">sudo apt update &amp;&amp; sudo apt install -y python3-pip python3-venv python3-dev libpq-dev nginx\ncd \/var\/www\/my-django-app\npython3 -m venv myenv\nsource myenv\/bin\/activate\npip install -r requirements.txt gunicorn<\/pre>\n<h2>Step 2: Create Systemd Service File for Gunicorn<\/h2>\n<p>Create <code>\/etc\/systemd\/system\/gunicorn.service<\/code>:<\/p>\n<pre style=\"background-color: #1e293b;color: #38bdf8;padding: 14px;border-radius: 6px;font-size: 13px\">[Unit]\nDescription=gunicorn daemon\nAfter=network.target\n\n[Service]\nUser=www-data\nGroup=www-data\nWorkingDirectory=\/var\/www\/my-django-app\nExecStart=\/var\/www\/my-django-app\/myenv\/bin\/gunicorn           --access-logfile -           --workers 3           --bind unix:\/run\/gunicorn.sock           myproject.wsgi:application\n\n[Install]\nWantedBy=multi-user.target<\/pre>\n<pre style=\"background-color: #1e293b;color: #38bdf8;padding: 14px;border-radius: 6px;font-size: 13px\"># Start and enable Gunicorn service\nsudo systemctl start gunicorn &amp;&amp; sudo systemctl enable gunicorn<\/pre>\n<h2>Step 3: Configure Nginx Reverse Proxy for Python<\/h2>\n<p>Create <code>\/etc\/nginx\/sites-available\/django.conf<\/code>:<\/p>\n<pre style=\"background-color: #1e293b;color: #38bdf8;padding: 14px;border-radius: 6px;font-size: 13px\">server {\n    listen 80;\n    server_name yourdomain.com www.yourdomain.com;\n\n    location = \/favicon.ico { access_log off; log_not_found off; }\n    \n    location \/static\/ {\n        root \/var\/www\/my-django-app;\n    }\n\n    location \/media\/ {\n        root \/var\/www\/my-django-app;\n    }\n\n    location \/ {\n        include proxy_params;\n        proxy_pass http:\/\/unix:\/run\/gunicorn.sock;\n    }\n}<\/pre>\n<pre style=\"background-color: #1e293b;color: #38bdf8;padding: 14px;border-radius: 6px;font-size: 13px\">sudo ln -s \/etc\/nginx\/sites-available\/django.conf \/etc\/nginx\/sites-enabled\/\nsudo nginx -t &amp;&amp; sudo systemctl reload nginx<\/pre>\n<div style=\"background-color: #f8fafc;border: 1px solid #e2e8f0;border-left: 4px solid #0ea5e9;padding: 20px;border-radius: 8px;margin: 30px 0\">\n<h3 style=\"margin-top: 0;color: #0f172a;font-size: 18px;display: flex;align-items: center\">\n        <span style=\"margin-right: 8px\">\ud83d\udd17<\/span> Recommended Related Technical Guides:<br \/>\n    <\/h3>\n<ul style=\"margin: 10px 0 0 0;padding-left: 20px;line-height: 1.8\">\n<li><a href=\"https:\/\/cpanelfree.com\/blog\/how-to-deploy-nodejs-express-app-linux-vps-nginx-pm2\/\" style=\"color: #0284c7;text-decoration: none;font-weight: 600\">How to Deploy Node.js Express on Linux VPS<\/a><\/li>\n<li><a href=\"https:\/\/cpanelfree.com\/blog\/how-to-setup-fresh-ubuntu-24-04-vps-scratch\/\" style=\"color: #0284c7;text-decoration: none;font-weight: 600\">How to Set Up a Fresh Ubuntu 24.04 VPS<\/a><\/li>\n<li><a href=\"https:\/\/cpanelfree.com\/blog\/how-to-install-lets-encrypt-ssl-certbot-linux\/\" style=\"color: #0284c7;text-decoration: none;font-weight: 600\">How to Install Let&#8217;s Encrypt SSL with Certbot<\/a><\/li>\n<li><a href=\"https:\/\/cpanelfree.com\/#plans\" style=\"color: #0284c7;text-decoration: none;font-weight: 600\">Explore $0 Free cPanel Web Hosting Plans<\/a><\/li>\n<\/ul>\n<\/div>\n<h2>Managing Static Files with WhiteNoise in Production Django<\/h2>\n<p>While Nginx can serve static assets directly, installing <strong>WhiteNoise<\/strong> allows Django to serve compressed, cache-busting static files directly through WSGI with zero external configuration:<\/p>\n<pre style=\"background-color: #1e293b;color: #38bdf8;padding: 14px;border-radius: 6px;font-size: 13px\"># In settings.py\nMIDDLEWARE = [\n    'django.middleware.security.SecurityMiddleware',\n    'whitenoise.middleware.WhiteNoiseMiddleware',\n    # ...\n]\nSTATICFILES_STORAGE = 'whitenoise.storage.CompressedManifestStaticFilesStorage'<\/pre>\n<h2>Automating Django Migrations and Static Collection via Bash Script<\/h2>\n<pre style=\"background-color: #1e293b;color: #38bdf8;padding: 14px;border-radius: 6px;font-size: 13px\">#!\/bin\/bash\n# deploy.sh\nsource myenv\/bin\/activate\ngit pull origin main\npip install -r requirements.txt\npython manage.py migrate --noinput\npython manage.py collectstatic --noinput\nsudo systemctl restart gunicorn<\/pre>\n<h2>Production Security Checklist for Django Deployments<\/h2>\n<ul style=\"padding-left: 20px;line-height: 1.8\">\n<li>\ud83d\udd12 <strong>Set DEBUG = False:<\/strong> Never run <code>DEBUG = True<\/code> in production, as error tracebacks reveal database schemas and secret API keys to visitors.<\/li>\n<li>\ud83d\udd12 <strong>Configure ALLOWED_HOSTS:<\/strong> Restrict <code>ALLOWED_HOSTS = ['yourdomain.com', 'www.yourdomain.com']<\/code> in <code>settings.py<\/code> to prevent HTTP Host header poisoning.<\/li>\n<li>\ud83d\udd12 <strong>Enforce Secure Cookies:<\/strong> Enable <code>CSRF_COOKIE_SECURE = True<\/code> and <code>SESSION_COOKIE_SECURE = True<\/code> to ensure cookies are only transmitted over encrypted HTTPS connections.<\/li>\n<li>\ud83d\udd12 <strong>Use Environment Variables for SECRETS:<\/strong> Store database passwords and <code>SECRET_KEY<\/code> inside a local <code>.env<\/code> file managed via <code>python-dotenv<\/code>.<\/li>\n<\/ul>\n<div style=\"border-bottom: 1px solid #e2e8f0;padding: 12px 0\">\n<h4 style=\"margin: 0 0 8px 0;color: #1e293b\">Why use UNIX sockets instead of TCP ports (127.0.0.1:8000) for Gunicorn?<\/h4>\n<p style=\"margin: 0;color: #475569;font-size: 14px\">UNIX domain sockets (<code>unix:\/run\/gunicorn.sock<\/code>) communicate directly through kernel memory buffers, bypassing the TCP network stack overhead and delivering up to <strong>15% higher request throughput<\/strong>.<\/p>\n<\/div>\n<div style=\"background: linear-gradient(135deg, #0f172a 0%, #1e293b 100%);border: 1px solid #334155;border-radius: 12px;padding: 25px;margin: 30px 0;text-align: center\">\n<h3 style=\"color: #38bdf8;margin-top: 0;font-size: 20px\">Deploy Python Apps with Ease on CpanelFree<\/h3>\n<p style=\"color: #94a3b8;font-size: 14px;line-height: 1.6;max-width: 600px;margin: 0 auto 15px\">\n        Deploy Python applications, WSGI microservices, and databases with root control on <strong>CpanelFree<\/strong> cloud servers.\n    <\/p>\n<p>    <a href=\"https:\/\/cpanelfree.com\/#plans\" style=\"display: inline-block;background-color: #a855f7;color: #ffffff;padding: 10px 22px;border-radius: 6px;text-decoration: none;font-weight: bold;font-size: 14px\">Explore Cloud Servers<\/a>\n<\/div>\n<h2>Frequently Asked Questions<\/h2>\n<div style=\"border-bottom: 1px solid #e2e8f0;padding: 12px 0\">\n<h4 style=\"margin: 0 0 8px 0;color: #1e293b\">How many Gunicorn workers should I configure?<\/h4>\n<p style=\"margin: 0;color: #475569;font-size: 14px\">The standard formula recommended by the Gunicorn team is <code>(2 x $num_cores) + 1<\/code>. On a 2 vCPU VPS, configure <strong>5 workers<\/strong> for optimal throughput.<\/p>\n<\/div>\n<h2>Configuring Celery Background Task Workers with Redis on Ubuntu<\/h2>\n<p>For long-running tasks like sending notification emails or generating PDF reports in Django, run <strong>Celery<\/strong> with a Redis message broker alongside Gunicorn:<\/p>\n<pre style=\"background-color: #1e293b;color: #38bdf8;padding: 14px;border-radius: 6px;font-size: 13px\"># Install Redis server and Celery\nsudo apt install -y redis-server\npip install celery redis\n\n# Launch Celery worker daemon\ncelery -A myproject worker --loglevel=info --concurrency=4<\/pre>\n<div style=\"background-color: #f1f5f9;padding: 15px;border-radius: 8px;margin: 20px 0\">\n<h4 style=\"margin-top: 0;color: #1e293b\">Pro Sysadmin Tip: Monitoring Gunicorn Socket Status<\/h4>\n<p style=\"margin: 0;color: #475569;font-size: 14px\">Verify UNIX socket communication health using <code>sudo systemctl status gunicorn.socket<\/code> and inspect real-time worker logs via <code>sudo journalctl -u gunicorn -f<\/code>.<\/p>\n<\/div>\n<p>Deploying Python Django and Flask applications with Gunicorn, Systemd, and Nginx ensures sub-second response times, robust security isolation, and effortless production scalability.<\/p>\n<h2>Configuring Automated PostgreSQL Database Backups for Django<\/h2>\n<p>Protect your production Django data by configuring a nightly cron job that exports compressed PostgreSQL database dumps directly to encrypted offsite cloud storage:<\/p>\n<pre style=\"background-color: #1e293b;color: #38bdf8;padding: 14px;border-radius: 6px;font-size: 13px\"># Automated PostgreSQL Nightly Dump\n0 3 * * * pg_dump -U djangouser djangodb | gzip &gt; \/var\/backups\/django_$(date +\\%F).sql.gz<\/pre>\n<div style=\"border-bottom: 1px solid #e2e8f0;padding: 12px 0\">\n<h4 style=\"margin: 0 0 8px 0;color: #1e293b\">How do I run Django database migrations during production updates?<\/h4>\n<p style=\"margin: 0;color: #475569;font-size: 14px\">Activate your virtual environment and run <code>python manage.py migrate --noinput<\/code> followed by <code>sudo systemctl restart gunicorn<\/code> to apply schema updates without downtime.<\/p>\n<\/div>\n<p>Additionally, configuring Gunicorn with Systemd socket activation ensures that incoming web traffic automatically spawns application worker threads with zero manual intervention.<\/p>\n<p>Following this production deployment architecture ensures your Django and Flask web applications operate with maximum security, reliability, and speed on Linux VPS servers.<\/p>\n<p>Setting up dedicated virtual environments and automated Gunicorn process monitoring guarantees maximum stability and performance for your mission-critical Python web applications.<\/p>\n<p>Following this production architecture ensures complete data isolation, high concurrency handling, and rapid response times for full-stack Python web development projects.<\/p>\n<p>Employing modern WSGI process management combined with Nginx edge proxy caching unlocks blazing performance for production web frameworks on Ubuntu.<\/p>\n","protected":false},"excerpt":{"rendered":"<p>Quick Answer: To deploy a Python Django or Flask web application on Ubuntu: 1) Create an isolated Python virtual environment (python3 -m venv myenv), 2) Install Gunicorn as the WSGI server, 3) Create a Systemd socket and service file to manage the Gunicorn daemon, and 4) Configure Nginx to serve static\/media files directly and proxy [&hellip;]<\/p>\n","protected":false},"author":1,"featured_media":1748,"comment_status":"open","ping_status":"open","sticky":false,"template":"","format":"standard","meta":{"footnotes":""},"categories":[51],"tags":[],"class_list":["post-1749","post","type-post","status-publish","format-standard","has-post-thumbnail","hentry","category-tutorials"],"_links":{"self":[{"href":"https:\/\/cpanelfree.com\/blog\/wp-json\/wp\/v2\/posts\/1749","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/cpanelfree.com\/blog\/wp-json\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/cpanelfree.com\/blog\/wp-json\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/cpanelfree.com\/blog\/wp-json\/wp\/v2\/users\/1"}],"replies":[{"embeddable":true,"href":"https:\/\/cpanelfree.com\/blog\/wp-json\/wp\/v2\/comments?post=1749"}],"version-history":[{"count":8,"href":"https:\/\/cpanelfree.com\/blog\/wp-json\/wp\/v2\/posts\/1749\/revisions"}],"predecessor-version":[{"id":1805,"href":"https:\/\/cpanelfree.com\/blog\/wp-json\/wp\/v2\/posts\/1749\/revisions\/1805"}],"wp:featuredmedia":[{"embeddable":true,"href":"https:\/\/cpanelfree.com\/blog\/wp-json\/wp\/v2\/media\/1748"}],"wp:attachment":[{"href":"https:\/\/cpanelfree.com\/blog\/wp-json\/wp\/v2\/media?parent=1749"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/cpanelfree.com\/blog\/wp-json\/wp\/v2\/categories?post=1749"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/cpanelfree.com\/blog\/wp-json\/wp\/v2\/tags?post=1749"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}