In this tutorial, you will learn how to configure an Nginx server on Ubuntu with the Django web framework and WSGI as the web server gateway interface. The server configuration will be production-ready and set up for public release.
Throughout the tutorial, concepts will be introduced and demonstrated so you can gain an understanding of how each piece works.
Before you begin:
- I will be using the domain name micro.domains throughout this tutorial.
- It is recommended that you configure your domain name’s DNS A records to point to the IP address of your server. Here’s a tutorial on how to do that.
- Don’t have a server? I’ll be using Linode to deploy a server running Ubuntu 20.04.
- It is recommended that you use a user other than root. Here’s a tutorial about how to add users to Ubuntu.
1. Update Your Server
It’s always a good idea to start by updating your server.
sudo apt-get update sudo apt-get upgrade
Now we have the latest and greatest packages installed on the server.
2. Use a Virtual Environment for Python
The venv module allows you to create virtual Python environments. This allows you to isolate installed packages from the system.
After installing venv, make a directory to house your virtual environments, and then create a virtual environment named md
using venv. You can call your virtual environment whatever you prefer.
apt-get install python3-venv mkdir /home/udoms/env/ python3 -m venv /home/udoms/env/md
Now activate your virtual environment.
source /home/udoms/env/md/bin/activate
You will see the name of your virtual environment in parenthesis as a prefix to your username and server name in your terminal window.
You can also verify that you are working from within your virtual environment by taking a look at where the Python binary is located.
which python
In this case, we are using Python version 3.8.5 which is located in the /home/udoms/env/md/bin/python
directory.
3. Create a Django Project
Now that our Python environment is set up, we can install the Django web framework using the pip package installer.
pip install Django
Next let’s create a Django project with django-admin.py which should be on your path by default. Feel free to choose a project name that suites you.
django-admin.py startproject microdomains cd microdomains
Test out your project by spinning up the built-in Django server with the following command:
python manage.py runserver 0.0.0.0:8000
In a browser, you should be able to visit micro.domains:8000
and see the default Django landing page. If not, you might you see a Django error message like this:
If you see this error message, add your domain name to the microdomains/settings.py
file.
... # SECURITY WARNING: don't run with debug turned on in production! DEBUG = True ALLOWED_HOSTS = ['micro.domains', 'www.micro.domains'] # Application definition INSTALLED_APPS = [ 'django.contrib.admin', ...
Try to visit micro.domains:8000
again and this time you will see the default Django landing page.
4. Get Started With uWSGI
First we need to install the web server gateway interface (WSGI). In this case, we will be using uWSGI. You will also need the development packages for your version of Python to be installed.
sudo apt-get install python3.8-dev sudo apt-get install gcc pip install uwsgi
The ultimate goal in this tutorial is to send requests from the client to Nginx which will pass them to a socket that will hand them off to uWSGI before finally being given to Django.
That’s a lot to configure right off the bat, so we will start with a much simpler test just to make sure all the individual puzzle pieces are in place.
Create a file called test.py
with the following content:
def application(env, start_response): start_response('200 OK', [('Content-Type','text/html')]) return [b"Hello World!"]
Let’s test out uWSGI directly talking to Python with the following command:
uwsgi --http :8000 --wsgi-file test.py
In a browser, you should be able to visit micro.domains:8000
and see the text “Hello World!”
If this works for you, we have demonstrated that uWSGI is able to pass requests to Python.
Now we can similarly serve the Django project with uWSGI with the following command:
uwsgi --http :8000 --module microdomains.wsgi
Note that this works because the path microdomains/wsgi.py
exists. Test it out in a browser and you should see the default Django landing page again.
5. Configure the Nginx Web Server
Install Nginx with apt-get
as follows:
sudo apt-get install nginx
Now with Nginx installed, you will be able to visit the default “Welcome to nginx!” page in your browser at http://micro.domains
.
Let’s tell Nginx about our Django project by creating a configuration file at /etc/nginx/sites-available/microdomains.conf
. Change the highlighted lines to suite your needs.
# the upstream component nginx needs to connect to upstream django { server unix:///home/udoms/microdomains/microdomains.sock; } # configuration of the server server { listen 80; server_name micro.domains www.micro.domains; charset utf-8; # max upload size client_max_body_size 75M; # Django media and static files location /media { alias /home/udoms/microdomains/media; } location /static { alias /home/udoms/microdomains/static; } # Send all non-media requests to the Django server. location / { uwsgi_pass django; include /home/udoms/microdomains/uwsgi_params; } }
We need to create the /home/udoms/microdomains/uwsgi_params
file highlighted above on line 26 as well.
uwsgi_param QUERY_STRING $query_string; uwsgi_param REQUEST_METHOD $request_method; uwsgi_param CONTENT_TYPE $content_type; uwsgi_param CONTENT_LENGTH $content_length; uwsgi_param REQUEST_URI $request_uri; uwsgi_param PATH_INFO $document_uri; uwsgi_param DOCUMENT_ROOT $document_root; uwsgi_param SERVER_PROTOCOL $server_protocol; uwsgi_param REQUEST_SCHEME $scheme; uwsgi_param HTTPS $https if_not_empty; uwsgi_param REMOTE_ADDR $remote_addr; uwsgi_param REMOTE_PORT $remote_port; uwsgi_param SERVER_PORT $server_port; uwsgi_param SERVER_NAME $server_name;
Next we can publish our changes by creating a symbolic link from sites-available to sites-enabled like so:
sudo ln -s /etc/nginx/sites-available/microdomains.conf /etc/nginx/sites-enabled/
We must edit the microdomains/settings.py
file to explicitly tell Nginx where our static files reside.
First add import os
at the very beginning:
""" Django settings for microdomains project. Generated by 'django-admin startproject' using Django 3.1.2. For more information on this file, see https://docs.djangoproject.com/en/3.1/topics/settings/ For the full list of settings and their values, see https://docs.djangoproject.com/en/3.1/ref/settings/ """ import os from pathlib import Path # Build paths inside the project like this: BASE_DIR / 'subdir'. BASE_DIR = Path(__file__).resolve().parent.parent ...
and STATIC_ROOT = os.path.join(BASE_DIR, "static/")
at the very end:
... # Static files (CSS, JavaScript, Images) # https://docs.djangoproject.com/en/3.1/howto/static-files/ STATIC_URL = '/static/' STATIC_ROOT = os.path.join(BASE_DIR, "static/")
With these changes in place, we can now tell Django to put all static files in the static folder.
python manage.py collectstatic
Restart the Nginx server to apply changes.
sudo /etc/init.d/nginx restart
At this point if you visit http://micro.domains
in a browser, you will probably see an Nginx 502 Bad Gateway error, but at this point we just want to test if media files are being properly served. So to test this out, let’s put an image in our media directory.
mkdir media wget https://upload.wikimedia.org/wikipedia/commons/b/b9/First-google-logo.gif -O media/media.gif
Finally visit http://micro.domains/media/media.gif
in a browser and you should see Google’s first logo from back in 1998.
6. Get Nginx, uWSGI, and Django to Work Together
Let’s take this one step further and have Nginx, uWSGI, and Django work together with the help of the UNIX socket.
uwsgi --socket microdomains.sock --module microdomains.wsgi --chmod-socket=666
You can actually try the above command without the --chmod-socket=666
argument and/or with a --chmod-socket=664
argument instead. If either of those work for you, just keep that in mind going forward.
Once again, visit http://micro.domains
in a browser, and this time you should see the default Django landing page!
7. Configure uWSGI for Production
Rather than passing arguments to uWSGI like we did above, we can put these options in a configuration file at the root of you Django project called microdomains_uwsgi.ini
.
[uwsgi] # full path to Django project's root directory chdir = /home/udoms/microdomains/ # Django's wsgi file module = microdomains.wsgi # full path to python virtual env home = /home/udoms/env/md # enable uwsgi master process master = true # maximum number of worker processes processes = 10 # the socket (use the full path to be safe socket = /home/udoms/microdomains/microdomains.sock # socket permissions chmod-socket = 666 # clear environment on exit vacuum = true # daemonize uwsgi and write messages into given log daemonize = /home/udoms/uwsgi-emperor.log
You can then proceed to start up uwsgi and specify the ini file:
uwsgi --ini microdomains_uwsgi.ini
Visit http://micro.domains
in a browser and you will see the default Django landing page if everything works correctly.
As one last configuration option for uWSGI, let’s run uWSGI in emperor mode. This will monitor the uWSGI config file directory for changes and will spawn vassals (i.e. instances) for each one it finds.
cd /home/udoms/env/md/ mkdir vassals sudo ln -s /home/udoms/microdomains/microdomains_uwsgi.ini /home/udoms/env/md/vassals/
Now you can run uWSGI in emperor mode as a test.
uwsgi --emperor /home/udoms/env/md/vassals --uid www-data --gid www-data
Visit http://micro.domains
in a browser and you will see the default Django landing page if everything works correctly.
Finally, we want to start up uWSGI when the system boots. Create a systemd service file at /etc/systemd/system/emperor.uwsgi.service
with the following content:
[Unit] Description=uwsgi emperor for micro domains website After=network.target [Service] User=udoms Restart=always ExecStart=/home/udoms/env/md/bin/uwsgi --emperor /home/udoms/env/md/vassals --uid www-data --gid www-data [Install] WantedBy=multi-user.target
Enable the service to allow it to execute on system boot and start it so you can test it without a reboot.
systemctl enable emperor.uwsgi.service systemctl start emperor.uwsgi.service
Visit http://micro.domains
in a browser and you will see the default Django landing page if everything works correctly.
Check the status of the service and stop it as follows:
systemctl status emperor.uwsgi.service systemctl stop emperor.uwsgi.service
For good measure, reboot your system to make sure that your website is accessible at startup.
Before going public with your website, it is highly recommended that you configure a few additional Django settings.
Please let me know if you have any questions about setting up Django with Nginx and uWSGI.
41 Responses
hey man, my url just apear nginx page i dont know whats whrong, can you helpe me?
Seems like the default Nginx landing page is still being served. This tells me that one of the following is true:
1.) You have multiple Nginx config files and the default is overriding your new one
2.) You need to reload Nginx for your config changes to take effect
3.) You haven’t symlinked your config in sites-available to sites-enabled
hello i’ve gone through all the steps though im encountring an error when i start the nginx server :
pam_unix(sudo:auth): Couldn’t open /etc/securetty: No such file or directory
please help
I’m not familiar with securetty. Are you? Is this a typo for security?
what must to write in /etc/nginx/nginx.conf file. first it listens :80 in nginx.conf, not in sites-available/microdomains.conf. is cause of linux server?
I’m sorry, can you rephrase your question?
I don’t have a setting I skipped, but I see the nginx page. django is not shown. Which part should I check again?
Is your Django server running on port 80 and/or is your Nginx config file set up to listen on port 80?
/etc/nginx/sites-available/microdomains.conf
the problem starts after this step
Your Nginx configuration file will be named something other than microdomains.conf
If you are using CentOS system, make *.conf files inside /etc/nginx/conf.d/ directory.
Refer this if needed :
https://www.youtube.com/watch?v=DzXCHAuHf0I
For ubuntu based systems mentions steps in the video works flawless.
Thanks for sharing this info Tony, helped a lot in server config.
Great post, everything is working, thank you very much for this!
Good morning!
This was a great post. It helped me a lot and I thank you so much.
However, from step 5 onwards the domain name stopped redirecting to my website and I could only access it from the VPS’s IP address. Do you know why this problem might be happening? I have checked my spelling of the domain inside the nginx conf files and everything appears to be ok.
Sorry Felipe, I’m not sure why that would be happening
Ok, I kind of have figured out what has happened. It seems I have to clear the cache and flush the DNS configuration of my local machine after I have accessed the site using the IP address.
Tony, once again, thank you for this tutorial.
There is one more error that is coming up to me when I make my Django application open a text file inside Python. I use the Python command ‘open’ inside views.py and, in order to avoid path related issues, I also use os and pathlib. When I run the application through the Django server at port 8000, everything works fine. However, when I run the server with the Nginx and uWSGI server, this error occurs.
I think it might have to do with the server user permissions, but I have tried to chmod and chown the file folder with no success. I would be very glad if you could give me an insight on how to fix this problem.
What is the error message?
I could not find the error message. Which file should I tail in order to get the error messages as I would get running the Django server directly?
I know that the code stops at this part because I used the “print” command several times until I had found the statement that was causing the error.
Hey Tony. This lesson has been a life saver. Thankyou very much! I was wondering if you have some advice for using multiple domain names, and pointing them to specific apps in my Django project. I am missing something.
hey tony thanks for the great and simplified tutorial I’ve followed your tutorial on ubuntu server 20.04 and everything worked fine, I created the service and it worked and the status was running after i rebooted the machine I received “502 Bad Gateway nginx/1.18.0 (Ubuntu)” I checked the status of the service it’s running, am I missing something
Hi Fady, please see if the suggestions in this video help you fix the 502 error https://youtu.be/hYVriHb43wU
When I run sudo nginx -t -c /etc/nginx/sites-available/na_backend.conf it says upstream is not allowed here
Hey, any idea how to enable –enable-threads
I had the same doubt. I was trying to shedule function using APSheduler. But it fails because django unable to create multiple thread because of uwsgi. Any Suggestions?
Hello Tony,
I used you tutorial to deploy Odoo ERP System, and I succeeded to load the project under uWSGI. But I have a problem with “uwsgi request is too big:XXX”.
My Deployment stack for Odoo ERP 12 :
#Python 3.7.10 and Werkzeug 0.16.1
#Nginx 1.20.0 #uWSGI 2.0.19.1 #FreeBSD 13.0-REL
Nginx throw an alert : “uwsgi request is too big: 81492 GET /……..”
I increased the “uwsgi_buffer_size ” in both uwsgi and nginx, but without any success
How to do a large buffer size (> 64k) uWSGI requests with Nginx proxy.
This is the issue that I openned on uWSGI github
https://github.com/unbit/uwsgi/issues/2315
Can you please take a look and help me to solve this problem
Thanks.
hello Mr.Tony,
first of all i really appreciate your effort of making this video but i got 502 bad gatway error,
i followed all steps as you mentioned in this blog
i can see my site only if i run this
“uwsgi –socket microdomains.sock –module microdomains.wsgi –chmod-socket=666″
i think nginx service was not running
and i used this command
sudo tail -f /var/log/nginx/error.log
to see the error logs
and i got
”
2021/06/07 18:51:49 [error] 9612#9612: *1 connect() to unix:///home/developer/bharathwajan/bharathwajan.sock failed (111: Connection refused) while connecting to upstream, client: 157.49.92.156, server: apothecary-re.com, request: “GET / HTTP/1.1”, upstream: “uwsgi://unix:///home/developer/bharathwajan/bharathwajan
”
i saw your video “https://www.youtube.com/watch?v=hYVriHb43wU&t=231”
but that doesn’t work for me
if you are ok to fix this error on teamviewer i can pay you around 10 to 14$ ,i know that was a less amount but mine is not a big budget project it was my personal project , if you are ok then mail me
i forgot to say that i cloned my project from my github repo,and used postgresql with it
Did you get a solution? I have the same error
Hi Tony, i m Fernando from Argentina, i did all steps and all looks good.
The server is running fine with django-nginx-uwsgi in a service (emperor mode working)
I’m having another problem, i want to host a site from home, and when i register mi syte with a dns server, i dont know what ip to put in the A record, the modem of my service has an ip like 126.100.66.xx, but when i search internet for my public ip i find another ip like 186.22.19.xx
I did the port forwarding in the modem for the ports 80 y 22 to the local ip of my webserver…
Sorry about my english, my native language is spanish.
Your channel is the best, so plz still going on it!!!
You will want to use the public IP address that is reported by the website 186.22.19.xx
Hi there! Great tutorial, however I’m encountering an error regarding the media file that has to be shown on /static/path/to/media/file. I’ve gone into the nginx logs and am getting the following error: 2021/08/19 10:31:34 [crit] 30839#30839: *1 connect() to unix:///home/ubuntu/web/src/project.sock failed (13: Permission denied) while connecting to upstream, client: (CLIENT IP), server: (SERVER IP), request: “GET /favicon.ico HTTP/1.1”, upstream: “uwsgi://unix:///home/ubuntu/web/src/project.sock:”, host: (SERVER IP), referrer: “http://(SERVER IP)/”. I believe it is an error with user permissions, how can I go about fixing it?
I had a similiar problem when running unicorn: https://stackoverflow.com/questions/68817517/nginx-502-bad-gateway-error-when-deploying-django-13-permission-denied-while
Feel free to answer either question, I really just can’t get it to work! It seems that when I run uwsgi –socket microdomains.sock –module microdomains.wsgi –chmod-socket=666, the server starts but never receives any requests… weird. Than you
Hola Fernando, tenés que poner la IP Publica… pero depende también de qué proveedor de internet estés usando.
How can I serve WebSockets ( Django-channels) using the above configuration?
hi iam sudhakar can you please make a tutorial for same django project with aws code pipeline and code deplyod on ec2 instance of ubuntu os
Everything worked perfectly in my ec2 ubuntu 18.04 instance until I rebooted it. Now I am getting 502 Bad gateway and the following error
2021/12/19 08:12:19 [error] 890#890: *1 connect() to unix:///home/ubuntu/rasabot/rasabot.sock failed (111: Connection refused) while connecting to upstream, client: 168.92.26.64, server: abc.com, request: “GET / HTTP/1.1”, upstream: “uwsgi://unix:///home/ubuntu/rasabot/rasabot.sock:”, host: “www.abc.com”
I made up the domain name for security reasons.
I am receiving these error
“`
— no python application found, check your startup logs for errors —
[pid: 1811|app: -1|req: -1/1] 157.119.86.188 () {48 vars in 936 bytes} [Sun Dec 26 08:38:11 2021] GET / => generated 21 bytes in 0 msecs (HTTP/1.1 500) 2 headers in 83 bytes (1 switches on core 0)
“`
But when I tried to run this command uwsgi –socket microdomains.sock –module microdomains.wsgi –chmod-socket=666
It is working maybe some issue with the ini configuration?
I had to comment out the home variable from the ini configuration and its work fine. Thank you for such a good video.
I configured the nginx server and restarted it and typed my domain name in web browser. I am getting timeout error not getting any message from nginx
Sorry, this happened due to the ufw was blocking the http traffic
Hi there! Great tutorial, however I’m encountering an error regarding the media file that has to be shown on /static/path/to/media/file. I’ve gone into the nginx logs and am getting the following error: 2021/08/19 10:31:34 [crit] 30839#30839: *1 connect() to unix:///home/ubuntu/web/src/project.sock failed (13: Permission denied) while connecting to upstream, client: (CLIENT IP), server: (SERVER IP), request: “GET /favicon.ico HTTP/1.1”, upstream: “uwsgi://unix:///home/ubuntu/web/src/project.sock:”, host: (SERVER IP), referrer: “http://(SERVER IP)/”. I believe it is an error with user permissions, how can I go about fixing it?
can we deploy frontend application also with this uwsgi emperor