Approx 5 years back, I wrote a small article on how to deploy a Django application on the PythonAnyWhere server for free.
https://pythoncircle.com/post/18/how-to-host-django-app-on-pythonanywhere-for-free/
In this article, we are going to see how to deploy a Django application on a DigitalOcean droplet.
First, create an account on DigitalOcean. You will get $200 worth of credits which you can use to pay for server cost.
Once you have created the droplet, attach it to a floating IP address. A floating IP address is a non-changing IP4 address provided by DigitalOcean for each droplet.
Login to the server from your terminal using SSH.
Update the packages and install new packages.
Run the below commands.
apt-get update && apt-get upgrade
Check if the latest python is installed. If not, install python.
sudo apt-get install python3.10
Install essential Ubuntu Packages required to run the Django application. Like virtual environment, NGINX, MySQL, etc.
apt install virtualenv supervisor nginx mysql-server python3-dev libmysqlclient-dev build-essential default-libmysqlclient-dev net-tools plocate
If you are using MySQL DB in your Django application, setup the MySQL using mysql_secure_installation. Please refer to this article by DigitalOcean for the same.
Also, set the right timezone on the server.
- Clone the git repository.
- Create the virtual environment. Activate it.
- Install the Django packages and dependencies in it from the requirement.txt
file.
pip install -r requirement.txt
- Copy secret files manually.
- Open MySQL from the terminal and create the Database.
- Run migrate command, and create cache tables if any.
python manage.py migrate
python manage.py createcachetable
- Run collect static files command.
python manage.py collectstatic
- Install gunicorn
pip install gunicorn
Every Django application needs three things to work. A Web Server, an Application Server, and the application itself. You must know the difference between a web server and an application server.
A web server like NGINX takes care of HTTP requests and passes on only those requests which are meant for that application. Gunicorn is the application server here.
In the wsgi.py
file of the Django application, an object is defined which is used by WSGI Server or Application Server, Gunicorn in this case.
application = get_wsgi_application()
WSGI stands for web server gateway interface. Check this https://peps.python.org/pep-3333/.
Gunicorn runs multiple instances of the Django application and distributes the incoming request load among those instances.
Now create a file named gunicorn_start
in the project working directory, adjacent to manage.py
file. Give it the executable permissions.
Copy and paste the below content into gunicorn_start
file.
#!/bin/bash NAME="myproject" DIR=/home/myproject USER=root GROUP=root WORKERS=2 BIND=unix:/home/myproject/gunicorn.sock DJANGO_SETTINGS_MODULE=myproject.settings DJANGO_WSGI_MODULE=myproject.wsgi LOG_LEVEL=error cd $DIR source /home/virtualenvironments/myproject/bin/activate export DJANGO_SETTINGS_MODULE=$DJANGO_SETTINGS_MODULE exec gunicorn ${DJANGO_WSGI_MODULE}:application \ --name $NAME \ --workers $WORKERS \ --user=$USER \ --group=$GROUP \ --bind=$BIND \ --log-level=$LOG_LEVEL \ --log-file=-
Let's understand the above file.
"myproject" is the name of the project.
"/home/myproject/" is the working directory of the project.
This file and the workers Gunicorn will spawn will run with the root user.
WORKERS=2 means that 2 worker threads of this application server will be there to handle the requests.
Communication will be via gunicorn.sock socket file.
Settting and wsgi file location is specified.
When this code is executed, it changes to working directory of project and activate the virtual environment from the location specified.
Copy and paste the below content into /etc/supervisor.conf
file at the bottom.
[program:myproject] command=/home/myproject/gunicorn_start user=root autostart=true autorestart=true redirect_stderr=true stdout_logfile=/home/myproject/logs/gunicorn-error.log
This will keep the Gunicorn process up and running all the time.
Run the below supervisor commands to let the supervisor know of the above changes.
sudo supervisorctl reread. # output: myproject available sudo supervisorctl update # output: myproject added to process group sudo supervisorctl status myproject # Output: myproject RUNNING 10s
Note that every time the application code is changed, we need to restart the supervisor myproject
program (not the supervisor itself)
supervisorctl restart myprogram # output # myproject stopped # myproject started
Check the Gunicorn error log file if program is not starting properly.
So now our Django application is running via Gunicorn, which is handling the traffic using 2 worker threads. Gunicorn process is managed by Supervisor. Let's use a Domain to serve the traffic.
Go to Godaddy or any other domain name provider and purchase a new domain.
Go to the networking tab of DigitalOcean droplet settings. Go to domains. Add a new domain you just purchased.
Add the A record for both, the domain and www subdomain.
Record - A
Host - @
Point to the IP of the droplet. TTL 3600 seconds.
Record - A
Host - www
Point to the IP of the droplet. TTL 3600 seconds.
Add three NS records.
Record - NS
Host - @
NS - ns1.digitalocean.com (and n2.digitalocean.com and ns3.digitalocean.com)
Now go to Godaddy.
In 'manage DNS' settings, edit and update the Nameservers.
Go to the NGINX configuration directory. Switch to sites-enabled directory and add a new file. Name it same as project name.
Copy and paste the below content in this conf file.
# accepts http and non-www version server { server_name pythoncircle.com; # to avoid any error while fetching fevicon location = /favicon.ico { access_log off; log_not_found off; } location /static/ { root /home/myproject; } # to serve media files location /media/ { root /homne/myproject/; } location / { include proxy_params; # communicate via socket file created by Gunicorn proxy_pass http://unix:/home/myproject/gunicorn.sock; } }
Reload the NGINX server.
nginx -s reload
or
systemctl reload nginx
Now you should be able to access the Django project via DNS. In case of any issues, please check the NGINX and Supervisor logs.
Do not forget to add the DNS in allowed hosts
property of project.
If NGINX is not able to connect to upstream i.e. gunicorn.sock
(permission denied), it is because users of NGINX and gunicorn.conf
might be different.
Run NGINX with root as well. change the user in nginx.conf
file. reload NGINX.
Next, you might want to serve the traffic over HTTPS instead of HTTP. Check for SSL certificate installation for free using certbot.
Please make sure that in the firewall section of server settings, you have enabled only Ports 80 (HTTP traffic), 443 (HTTPS traffic) and 22 (SSH) for incoming traffic. You may open all ports of outgoing traffic based on your requirements.
As a good practice, you should not use passwords to SSH into the server. Use the public-private key combinations.
Also, create a separate user with only required permissions instead of working with the Root user.
Feel free to contact me in case if you are facing any issues in deploying the Django application on DigitalOcean.
Host your Django Application for free on PythonAnyWhere.
If you want complete control of your application and server, you should consider DigitalOcean. Create an account with this link and get $200 credits.