Deploy Django Project on EC2 using Nginx and uWSGI

Nginx is a high-performance web server that can handle a large number of requests efficiently. uWSGI is a fast and efficient application server that is well-suited for running Django applications. EC2 allows for easy scalability by enabling to scale application horizontally by adding or removing instances. This guide demonstrates how to deploy a Django project on EC2 using Nginx and uWSGI.

I. Create EC2 Ubuntu on AWS

  1. On AWS, create EC2 name “sudoku-3033”, connect to EC2 through terminal by this below command. Make sure open port 8000 on security group for local test.
    	ssh -i ~/sudoku-3033.pem

    If the connection fail due to Permission, run below command for grant read and write the publickey.

    	chmod 600 /Users/nguyenvanan2730/Projects/Sudoku-AWS/sudoku-info/pro-sudoku/sudoku-3033.pem
  2. After login in EC2, run below command for update package list.
    	sudo apt update
    	sudo apt upgrade

II. Create Virtual Environment, and install necessary library

Create Virtual Environment

  1. Install pip3:
    	sudo apt install python3-pip
  2. Install virtualenv for create virtual environment
    	sudo apt install python3-virtualenv
  3. Create virtual environment in root direction home/ubuntu
    	virtualenv venv
  4. Activate environment
    	source venv/bin/activate

    After the virtual environment is activated, the format on terminal will be displayed like: (venv) ubuntu@ip-10-0-4-232:

Clone project from GitHub

  1. Update git using version 2.34.1
    	sudo apt install git
  2. Clone source code from GitHub using "git clone and token"
    	git clone https://nguyenvanan2730:<token>

Install necessary library

  1. Django
    	pip3 install django==4.1.4
    	pip3 show Django
  2. Numpy
    	pip3 install numpy==1.23.5
    	pip3 show numpy
  3. opencv
    	pip3 install opencv-python==
    	pip3 show opencv-python
    	sudo apt-get install libgl1-mesa-glx
    	--> Install this packages for successful import OpenCV
  4. boto3
    	pip3 install boto3==1.26.30
    	pip3 show boto3
    	--> 1.26.30
  5. Pillow
    	python3 -m pip install Pillow
  6. django-environ
    	python3 -m pip install django-environ
  7. aws-cli
    	sudo apt install awscli

III. Setting Variable Environment

  1. Create a .env file in the /home/ubuntu/venv/sudoku/sudoku_project
    	sudo nano /home/ubuntu/venv/sudoku/sudoku_project/.env
  2. Setting below content in the .env file
    The static_root setting is the absolute filesystem path to the directory where all collected static files will be stored after running the collectstatic command. The collectstatic command is used to gather static files from various locations and place them in a single location for easier deployment and improved performance.
  3. Create a .gitignore file and add .env into it, ensure that do not push these configure to the GitHub.
    Run below command, the screen should be display as http://18.XX.XX.147:8000
    	python runserver

IV. Deploy application on EC2 using Nginx and uWSGI

Install uWSGI into virtualenv

  1. Activate the virtual environment and run below command to install uWSGI.
    	pip install uwsgi
    uWSGI will be located at /home/ubuntu/venv/lib/python3.10/site-packages. Can check if the uWSGI is installed by running "pip freeze" or "pip3 show uWSGI"
  2. After uWSGI is successful installed, Django can run the following command. Access sever through the browser, application should be executed as run python3 runserver command.
    	uwsgi --http :8000 --module sudoku_project.wsgi
    sudoku_project.wsgi is not a file but a reference to the application instance located in the file. As uWSGI was not yet configured to serve static files, the screen design may appear broken at this stage. In the next step, static files will be served using Nginx.

Configure Nginx as Web Server

  1. Install and start Nginx using below command.
    	sudo apt-get install nginx.     # install nginx
    	sudo service nginx start   # start nginx
    After Nginx is started, the message “Welcome to nginx” should be displayed at the address: http://18.XX.XX.147:80
  2. Configure Nginx for project

    Create a uwsgi_params file in ~/venv/ contains below configures.

    	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;
    Create a mysite_nginx.conf in the /etc/nginx/sites-available/ directory.
    	# mysite_nginx.conf
    	# the upstream component nginx needs to connect to
    	upstream django {
    		server unix:///home/ubuntu/venv/sudoku/sudoku_project/mysite.sock; # for a file socket
    	# configuration of the server
    	server {
    		# the port your site will be served on
    		listen      8000;
    		# the domain name it will serve for
    		server_name 18.XX.XX.147; # substitute your machine's IP address or FQDN
    		charset     utf-8;
    		# max upload size
    		client_max_body_size 75M;   # adjust to taste
    		# Django media
    		location /media  {
    			alias /home/ubuntu/venv/sudoku/sudoku_project/media;  # your Django project>
    		location /static {
    			alias /home/ubuntu/venv/sudoku/sudoku_project/static; # your Django project>
    		# Finally, send all non-media requests to the Django server.
    		location / {
    			uwsgi_pass  django;
    			include     /home/ubuntu/venv/uwsgi_params; # the uwsgi_params file you ins>
  3. Run below command, uWSGI and Nginx should be serving as expected.
    	uwsgi --socket mysite.sock --module sudoku_project.wsgi --chmod-socket=664

Configuring uWSGI to run with a .ini file

  1. Create a file call mysite_uwsgi.ini in the ~/venv/ direction
    	# mysite_uwsgi.ini file
    	# Django-related settings
    	# the base directory (full path)
    	chdir           = /home/ubuntu/venv/sudoku/sudoku_project
    	# Django's wsgi file
    	module          = sudoku_project.wsgi
    	# the virtualenv (full path)
    	home            = /home/ubuntu/venv
    	# process-related settings
    	# master
    	master          = true
    	# maximum number of worker processes
    	processes       = 10
    	# the socket (use the full path to be safe
    	socket          = /home/ubuntu/venv/sudoku/sudoku_project/mysite.sock
    	# ... with appropriate permissions - may be needed
    	chmod-socket    = 664
    	# clear environment on exit
    	vacuum          = true
  2. And Run uwsgi using this file, the same result will be displayed on the screen.
    	uwsgi --ini /home/ubuntu/venv/mysite_uwsgi.ini

Install uWSGI system-wide

Emperor mode

"Emperor mode" in Nginx and uWSGI refers to a feature that allows Nginx to run as a master process and manage multiple uWSGI worker processes. In this mode, Nginx acts as a reverse proxy and load balancer for the uWSGI worker processes, and can automatically start, stop, and respawn the worker processes as needed.

  1. Create a directory name vassals, -p option means the parents directories will be created if it do not already exist.
    	sudo mkdir -p /etc/uwsgi/vassals
  2. Create a symlink from the default config directory to your config file
    	sudo ln -s /home/ubuntu/venv/mysite_uwsgi.ini /etc/uwsgi/vassals/	
  3. Run uWSGI again with sudo
    	sudo uwsgi --emperor /etc/uwsgi/vassals --uid www-data --gid www-data

Make uWSGI startup when the system boots

  1. Create "emperor.uwsgi.service" file at: /etc/systemd/system/emperor.uwsgi.service
    	Description=uwsgi emperor for micro domains website
    	ExecStart=sudo uwsgi --emperor /etc/uwsgi/vassals --uid www-data --gid www-data
  2. After setting emperor.uwsgi.service file, enable and start service. It is should show the results like run Emperor mode.
    	sudo systemctl enable emperor.uwsgi.service
    	sudo systemctl start emperor.uwsgi.service
  3. Now, reboot server and the application should be startup. Below command can be used in emperor.uwsgi.service
    	sudo systemctl enable emperor.uwsgi.service
    	sudo systemctl start emperor.uwsgi.service
    	sudo systemctl status emperor.uwsgi.service
    	sudo systemctl stop emperor.uwsgi.service
    	sudo systemctl restart emperor.uwsgi.service

This guide has demonstrated the steps for deploying a Django application to EC2 using Nginx and uWSGI. However, in other to make the project accessible via, additional configuration on the AWS is required. For more informations about this topic, consult the documentation at System Architect.