{"id":7456,"date":"2024-06-03T09:54:00","date_gmt":"2024-06-03T09:54:00","guid":{"rendered":"https:\/\/blog.ssdnodes.com\/blog\/?p=7456"},"modified":"2025-05-18T13:19:45","modified_gmt":"2025-05-18T13:19:45","slug":"how-to-create-and-deploy-a-django-application-using-gunicorn-and-nginx-on-ubuntu-22-04","status":"publish","type":"post","link":"https:\/\/www.ssdnodes.com\/blog\/how-to-create-and-deploy-a-django-application-using-gunicorn-and-nginx-on-ubuntu-22-04\/","title":{"rendered":"How to Create and Deploy a Django Application Using Gunicorn and Nginx on Ubuntu 22.04"},"content":{"rendered":"<h2>Introduction<\/h2>\n<p>Django is a web framework that allows you to create web applications using the Python language. Gunicorn is a Python Web Server Gateway Interface HTTP server, allowing you to serve your Python web application in an efficient, production-ready environment. NGINX is an open-source web server delivering web content through the internet, as well as reverse proxying, caching, load balancing, media streaming, and more.<\/p>\n<\/p>\n<p>This tutorial will show you how to create a small Django application, serve it using Gunicorn, and configure Nginx as a reverse proxy that sits in front of Gunicorn and forwards client requests to it, which improves scalability, performance, resilience, and security.<\/p>\n<h2>Prerequisites<\/h2>\n<p>To follow this tutorial you need:<\/p>\n<ul>\n<li>Basic knowledge of the Linux command line.<\/li>\n<li>An Ubuntu 22.04 server with a non-root user with <code>sudo<\/code> privileges. You can get affordable, and powerful Ubuntu servers from <a href=\"https:\/\/www.ssdnodes.com\/\" target=\"external\">our website<\/a>, and you can check out our <a href=\"https:\/\/www.ssdnodes.com\/blog\/tutorial-setting-up-and-securing-ssh-based-authentication\/\">How to access your server using SSH<\/a> guide to learn how to access your server and create a <code>sudo<\/code> user.<\/li>\n<li>Nginx installed on your Ubuntu server, follow our <a href=\"https:\/\/www.ssdnodes.com\/blog\/installing-lemp-on-ubuntu-22-04-lts-jammy-jellyfish\/\">Installing LEMP on Ubuntu 22.04<\/a> guide to install it on your server.<\/li>\n<\/ul>\n<h1>Update The Package Cache<\/h1>\n<p>Start by updating the packages in the package manager cache to the latest available versions using the following command:<\/p>\n<pre><code>sudo apt update<\/code><\/pre>\n<h1>Installing Core Dependencies<\/h1>\n<p>Before you start writing your Django application, you need to install some core dependencies for your Python programming environment. To do this, use the following command:<\/p>\n<pre><code>sudo apt install python3-pip python3-dev build-essential libssl-dev libffi-dev python3-setuptools python3-venv<\/code><\/pre>\n<p>With this, you can now create a Python virtual environment for your project.<\/p>\n<h1>Creating a Django Application Inside a Virtual Environment<\/h1>\n<p>Before you install Django and Gunicorn, you will first create a project folder and then create a Python virtual environment inside it to isolate your project code and dependencies from the rest of the system.<\/p>\n<p>Python virtual environments allow you to have multiple isolated Python environments on one computer. This is useful for projects that require different versions of Python, or for projects that you don't want to contaminate with code from other projects. This ensures that each Python project is isolated from your other projects, so that conflict between dependencies does not occur.<\/p>\n<p>First, create the project folder, we'll call it <code>myapp<\/code>:<\/p>\n<pre><code>mkdir myapp<\/code><\/pre>\n<p>Navigate to it:<\/p>\n<pre><code>cd myapp<\/code><\/pre>\n<p>Create a Python virtual environment inside your <code>myapp<\/code> project folder:<\/p>\n<pre><code>python3 -m venv env<\/code><\/pre>\n<p>Activate it:<\/p>\n<pre><code>source env\/bin\/activate<\/code><\/pre>\n<p>Your command line should now have an <code>(env)<\/code> prefix indicating that your virtual environment is activated.<\/p>\n<p>Install Django and Gunicorn:<\/p>\n<pre><code>pip install django gunicorn<\/code><\/pre>\n<p>With Django and Gunicorn now installed, you can now create a small application.<\/p>\n<h1>Building a Small Django Application<\/h1>\n<p>We'll create a small Django application that displays a <code>Hello World!<\/code> web page.<\/p>\n<p>First, in your <code>myapp<\/code> folder, create a new Django project using the Django admin:<\/p>\n<pre><code>django-admin startproject mysite<\/code><\/pre>\n<p>This creates a new <code>mysite<\/code> directory containing the Django files that are needed for our demo application.<\/p>\n<p>Navigate to this new <code>mysite<\/code> folder:<\/p>\n<pre><code>cd mysite<\/code><\/pre>\n<p>Next, create a new application inside this Django project with the following command:<\/p>\n<pre><code>python manage.py startapp app<\/code><\/pre>\n<p>This will create a new django application and a new directory called <code>app<\/code>.<\/p>\n<p>Before you create your application, you need to adjust your project settings to disable debugging and allow your server IP and domain names as hosts to serve your application. Open the <code>mysite\/settings.py<\/code> file:<\/p>\n<pre><code>nano mysite\/settings.py<\/code><\/pre>\n<p>Set the <code>DEBUG = True<\/code> line to this:<\/p>\n<pre><code>DEBUG = False<\/code><\/pre>\n<p>Then, set the <code>ALLOWED_HOSTS<\/code> line to the following:<\/p>\n<pre><code>ALLOWED_HOSTS = [&#039;your_domain&#039;, &#039;your_IP_address&#039;, &#039;localhost&#039;]<\/code><\/pre>\n<p>Save and close the file.<\/p>\n<p><strong>Note:<\/strong> Remember to replace <code>your_domain<\/code> and <code>your_IP_address<\/code> with your own appropriate values.<\/p>\n<p>Next, open <code>app\/views.py<\/code> to add a new view:<\/p>\n<pre><code>nano app\/views.py<\/code><\/pre>\n<p>Add the following code to it:<\/p>\n<pre><code>from django.http import HttpResponse\n\ndef index(request):\n    return HttpResponse(&quot;Hello Wrold! This is my Django app!&quot;)<\/code><\/pre>\n<p>Save and close the file.<\/p>\n<p>Next, create a file called <code>urls.py<\/code> to reference this new view:<\/p>\n<pre><code>nano app\/urls.py<\/code><\/pre>\n<p>Add the following to it:<\/p>\n<pre><code>from django.urls import path\n\nfrom . import views\n\nurlpatterns = [\n    path(&#039;&#039;, views.index, name=&#039;index&#039;),\n]<\/code><\/pre>\n<p>Save and close the file.<\/p>\n<p>Next, open the main <code>mysite\/urls.py<\/code>:<\/p>\n<pre><code>nano mysite\/urls.py<\/code><\/pre>\n<p>Then include the <code>app\/urls.py<\/code> file in it by importing the <code>include<\/code> function from <code>django.urls<\/code> and calling the <code>path()<\/code> function and passing it the <code>app.urls<\/code> module like so:<\/p>\n<pre><code>from django.contrib import admin\nfrom django.urls import include, path\n\nurlpatterns = [\n    path(&#039;&#039;, include(&#039;app.urls&#039;)),\n    path(&#039;admin\/&#039;, admin.site.urls),\n]<\/code><\/pre>\n<p>Now, allow port <code>8000<\/code> on your firewall and then run the Django development server:<\/p>\n<pre><code>sudo ufw allow 8000\npython manage.py runserver 0.0.0.0:8000<\/code><\/pre>\n<p>You should receive the following as part of the output:<\/p>\n<pre><code>Django version 4.1.3, using settings &#039;mysite.settings&#039;\nStarting development server at http:\/\/0.0.0.0:8000\/\nQuit the server with CONTROL-C.<\/code><\/pre>\n<p>This confirms that our Django application is running. Navigate to it with the following URL, replacing <code>your_IP_address<\/code> with your server's IP address:<\/p>\n<pre><code>your_IP_address:8000<\/code><\/pre>\n<p>You should see the <code>Hello Wrold! This is my Django app!<\/code> text.<\/p>\n<h1>Configure Gunicorn<\/h1>\n<p>Now that you've set up your sample Django application, you can set up Gunicorn.<\/p>\n<p>Inside your <code>mysite<\/code> project, activate the environment if you haven't already.<\/p>\n<p>Run the application with Gunicorn by giving it the <code>0.0.0.0:8000<\/code> interface and the project's WSGI module.<\/p>\n<pre><code>gunicorn --bind 0.0.0.0:8000 mysite.wsgi<\/code><\/pre>\n<p>You should see output like the following:<\/p>\n<pre><code>[2022-11-20 16:49:59 +0100] [6994] [INFO] Starting gunicorn 20.1.0\n[2022-11-20 16:49:59 +0100] [6994] [INFO] Listening at: http:\/\/0.0.0.0:8000 (6994)\n[2022-11-20 16:49:59 +0100] [6994] [INFO] Using worker: sync\n[2022-11-20 16:49:59 +0100] [6995] [INFO] Booting worker with pid: 6995<\/code><\/pre>\n<p>Now use your browser to visit your applications URL again:<\/p>\n<pre><code>http:\/\/your_server_ip:8000<\/code><\/pre>\n<p>You'll see the <code>Hello Wrold! This is my Django app!<\/code> text on your browser.<\/p>\n<p>With this, you have ensured the Gunicorn server is functioning correctly.<\/p>\n<h1>Automatically Start Gunicorn on Boot<\/h1>\n<p>Now that the application and Gunicorn are ready, create a systemd service unit file to automatically start Gunicorn and serve the Django application at boot.<\/p>\n<p>First, make sure to deactivate the virtual environment in your <code>mysite<\/code> directory:<\/p>\n<pre><code>deactivate<\/code><\/pre>\n<p>Create a <code>myapp.service<\/code> file inside the <code>\/etc\/systemd\/system<\/code> directory:<\/p>\n<pre><code>sudo nano \/etc\/systemd\/system\/myapp.service<\/code><\/pre>\n<p>Add the following to it:<\/p>\n<pre><code>[Unit]\nDescription=Gunicorn instance to serve myapp\nAfter=network.target\n\n[Service]\nUser=your_user\nGroup=www-data\nWorkingDirectory=\/home\/your_user\/myapp\/mysite\nEnvironment=&quot;PATH=\/home\/your_user\/myapp\/env\/bin&quot;\nExecStart=\/home\/your_user\/myapp\/env\/bin\/gunicorn \\\n          --workers 3\\\n          --bind unix:mysite.sock -m 007\\\n          mysite.wsgi:application\n\n[Install]\nWantedBy=multi-user.target<\/code><\/pre>\n<p>Save and close the file.<\/p>\n<p>The <code>After=network.target<\/code> line tells systemd to start this service after the network starts.<\/p>\n<p>In the <code>[Service]<\/code> block, you specify the user and group that will run this service, replace <code>your_user<\/code> with the user who owns the files of the Django application. You give  group ownership to the <code>www-data<\/code> group to allow Nginx to access and communicate with Gunicorn.<\/p>\n<p>You set <code>WorkingDirectory<\/code> to the full path of the application's project folder, and you specify your python environment's <code>bin<\/code> directory with <code>Environment<\/code>.<\/p>\n<p>The <code>ExecStart<\/code> runs Gunicorn with 3 workers (adjust this as you see fit), and creates a <code>mysite.sock<\/code> socket file. You also pass in the Django project name and the WSGI module (<code>mysite.wsgi<\/code>).<\/p>\n<p>In the <code>WantedBy=multi-user.target<\/code> line, you tell systemd to start this service when the regular multi-user system is started.<\/p>\n<p><strong>Note:<\/strong> Systemd requires the full path to the Gunicorn executable, which is installed within your virtual environment inside the <code>bin<\/code> directory. Remember to replace the username and the paths with your own.<\/p>\n<p>You can now start your <code>myapp.service<\/code> you just created and enable it to start at boot:<\/p>\n<pre><code>sudo systemctl start myapp.service\nsudo systemctl enable myapp.service<\/code><\/pre>\n<p>Check the status:<\/p>\n<pre><code>sudo systemctl status myapp<\/code><\/pre>\n<p>You should receive the following output indicating that your <code>myapp.service<\/code> is active and running:<\/p>\n<pre><code>\u25cf myapp.service - Gunicorn instance to serve myapp\n     Loaded: loaded (\/etc\/systemd\/system\/myapp.service; enabled; vendor preset: enabled)\n     Active: active (running) since Fri 2022-11-18 14:17:31 UTC; 7s ago\n   Main PID: 59209 (gunicorn)\n      Tasks: 4 (limit: 19072)\n     Memory: 55.5M\n        CPU: 821ms\n     CGroup: \/system.slice\/myapp.service\n             \u251c\u250059209 \/home\/abd\/myapp\/env\/bin\/python3 \/home\/abd\/myapp\/env\/bin\/gunicorn --work&gt;\n             \u251c\u250059210 \/home\/abd\/myapp\/env\/bin\/python3 \/home\/abd\/myapp\/env\/bin\/gunicorn --work&gt;\n             \u251c\u250059211 \/home\/abd\/myapp\/env\/bin\/python3 \/home\/abd\/myapp\/env\/bin\/gunicorn --work&gt;\n             \u2514\u250059212 \/home\/abd\/myapp\/env\/bin\/python3 \/home\/abd\/myapp\/env\/bin\/gunicorn --work&gt;<\/code><\/pre>\n<h1>Configuring Nginx as a Reverse Proxy<\/h1>\n<p>Now that Gunicorn is running and serving your Django application, you can configure Nginx to pass web requests to it.<\/p>\n<p>Create a new server block configuration file called <code>myapp<\/code> in your Nginx\u2019s <code>sites-available<\/code> directory:<\/p>\n<pre><code>sudo nano \/etc\/nginx\/sites-available\/myapp<\/code><\/pre>\n<p>Add the following configuration to this new server block:<\/p>\n<pre><code>server {\n    listen 8080;\n    server_name your_IP_or_domain;\n\n    location \/ {\n        include proxy_params;\n        proxy_pass http:\/\/unix:\/home\/your_user\/myapp\/myapp.sock;\n    }\n}\n<\/code><\/pre>\n<p>Save and close the file.<\/p>\n<p>Here, you tell Nginx to listen on port <code>8080<\/code> and specify your IP address or domain name. Then in the <code>location<\/code> block, you include the <code>proxy_params<\/code> file for proxying, and then you use the <code>proxy_pass<\/code> directive to pass web requests to the socket you created earlier in <code>myapp.service<\/code>. Remember to change <code>your_user<\/code> to your username.<\/p>\n<p>Link this new Nginx configuration to the <code>sites-enabled<\/code> directory:<\/p>\n<pre><code>sudo ln -s \/etc\/nginx\/sites-available\/myapp \/etc\/nginx\/sites-enabled<\/code><\/pre>\n<p>Unlink Nginx's default configuration file:<\/p>\n<pre><code>sudo unlink \/etc\/nginx\/sites-enabled\/default<\/code><\/pre>\n<p>Test the configuration for errors:<\/p>\n<pre><code>sudo nginx -t<\/code><\/pre>\n<p>You should receive an output that informs you that the test is successful.<\/p>\n<p>Restart Nginx:<\/p>\n<pre><code>sudo systemctl restart nginx<\/code><\/pre>\n<p>If you haven't already, give Nginx full access in <code>ufw<\/code>:<\/p>\n<pre><code>sudo ufw allow &#039;Nginx Full&#039;<\/code><\/pre>\n<p>Next, make sure that Nginx can access the Gunicorn socket file by giving your home directory <code>0755<\/code> permissions:<\/p>\n<pre><code>sudo chmod 755 \/home\/your_user<\/code><\/pre>\n<p>With this, navigate to your website with your browser:<\/p>\n<pre><code>http:\/\/your_server_ip_or_domain:8080<\/code><\/pre>\n<p>And you should see that your Django application was successfully served.<\/p>\n<h2>Congrats<\/h2>\n<p>You have learned how to create a Django application and serve it using Gunicorn and Nginx. To learn more about Django, check out the <a href=\"https:\/\/www.djangoproject.com\/\" target=\"_blank\" rel=\"noopener\">official documentation<\/a>.<\/p>\n","protected":false},"excerpt":{"rendered":"<p>Master the art of deploying Django applications with Gunicorn and Nginx on Ubuntu 22.04! Learn how to create a robust web environment for your Python projects.<\/p>\n","protected":false},"author":19,"featured_media":9155,"comment_status":"open","ping_status":"closed","sticky":false,"template":"","format":"standard","meta":{"_acf_changed":false,"inline_featured_image":false,"footnotes":""},"categories":[18,30],"tags":[],"class_list":["post-7456","post","type-post","status-publish","format-standard","has-post-thumbnail","hentry","category-devops","category-tutorials"],"acf":[],"_links":{"self":[{"href":"https:\/\/www.ssdnodes.com\/wp-json\/wp\/v2\/posts\/7456","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/www.ssdnodes.com\/wp-json\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/www.ssdnodes.com\/wp-json\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/www.ssdnodes.com\/wp-json\/wp\/v2\/users\/19"}],"replies":[{"embeddable":true,"href":"https:\/\/www.ssdnodes.com\/wp-json\/wp\/v2\/comments?post=7456"}],"version-history":[{"count":6,"href":"https:\/\/www.ssdnodes.com\/wp-json\/wp\/v2\/posts\/7456\/revisions"}],"predecessor-version":[{"id":13020,"href":"https:\/\/www.ssdnodes.com\/wp-json\/wp\/v2\/posts\/7456\/revisions\/13020"}],"wp:featuredmedia":[{"embeddable":true,"href":"https:\/\/www.ssdnodes.com\/wp-json\/wp\/v2\/media\/9155"}],"wp:attachment":[{"href":"https:\/\/www.ssdnodes.com\/wp-json\/wp\/v2\/media?parent=7456"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/www.ssdnodes.com\/wp-json\/wp\/v2\/categories?post=7456"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/www.ssdnodes.com\/wp-json\/wp\/v2\/tags?post=7456"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}