Deploying Django on DigitalOcean

Deploying Django on DigitalOcean

Deploying a Django application can feel like a daunting task, especially if you’re new to server management. In this guide, I’ll walk you through the entire process of deploying a Django app on a DigitalOcean Droplet. We’ll cover everything from setting up your server and configuring your Django project, to securing it with SSL and serving it with Gunicorn and Nginx. Grab a cup of coffee and let’s get started!

Preparing Your Django Project

Before you even think about deploying, it's important to make sure your Django project is ready for production. There are a few key steps you shouldn’t skip:

  • Update your settings.py to handle production environments securely.
  • Set DEBUG = False.
  • Configure your ALLOWED_HOSTS to include your domain or server IP.
  • Make sure your static and media files are properly set up.
  • Use environment variables for sensitive information like secret keys and database passwords.

Here’s a quick example of how you might adjust your settings:

# settings.py
import os
from pathlib import Path

BASE_DIR = Path(__file__).resolve().parent.parent

SECRET_KEY = os.environ.get('SECRET_KEY')
DEBUG = os.environ.get('DEBUG', 'False') == 'True'
ALLOWED_HOSTS = os.environ.get('ALLOWED_HOSTS', '').split(',')

# Database
DATABASES = {
    'default': {
        'ENGINE': 'django.db.backends.postgresql',
        'NAME': os.environ.get('DB_NAME'),
        'USER': os.environ.get('DB_USER'),
        'PASSWORD': os.environ.get('DB_PASSWORD'),
        'HOST': 'localhost',
        'PORT': '5432',
    }
}

# Static files
STATIC_URL = '/static/'
STATIC_ROOT = os.path.join(BASE_DIR, 'staticfiles')

This approach keeps your configuration flexible and secure. Remember, never hardcode secrets in your version control!

Setting Development Value Production Value
DEBUG True False
ALLOWED_HOSTS [] ['yourdomain.com', 'IP']
Secret Key Hardcoded (for dev only) Environment Variable
Database SQLite PostgreSQL/MySQL

Setting Up Your DigitalOcean Droplet

Now, let’s move on to creating your server. DigitalOcean makes this process straightforward:

  • Log in to your DigitalOcean account and click "Create Droplet."
  • Choose Ubuntu as your operating system (I recommend the latest LTS version).
  • Select a plan that fits your needs; for a small to medium Django app, the basic $5/month plan is a good start.
  • Pick a datacenter region closest to your target audience.
  • Add your SSH key for secure, password-less login. If you haven’t set one up yet, now’s the time.
  • Finally, click "Create Droplet" and wait a minute or two for it to be ready.

Once your Droplet is live, note its IP address—you’ll need it to connect via SSH.

Here’s how to connect:

ssh root@your_droplet_ip

You’re now inside your new server! The first thing you should do is update the package list and upgrade existing packages:

apt update && apt upgrade -y

Installing Required Software

With your server up and running, it's time to install the necessary software. We'll need:

  • Python and pip
  • PostgreSQL (or MySQL if you prefer)
  • Nginx to serve our app
  • Gunicorn as our WSGI server
  • virtualenv to manage our Python environment

Let’s install them step by step.

First, install Python, pip, and other essential build tools:

apt install python3-pip python3-dev libpq-dev postgresql postgresql-contrib nginx curl -y

Now, set up PostgreSQL. Start by switching to the postgres user:

sudo -u postgres psql

Within the PostgreSQL prompt, create a database and user for your Django project:

CREATE DATABASE myproject;
CREATE USER myprojectuser WITH PASSWORD 'secure_password_here';
ALTER ROLE myprojectuser SET client_encoding TO 'utf8';
ALTER ROLE myprojectuser SET default_transaction_isolation TO 'read committed';
ALTER ROLE myprojectuser SET timezone TO 'UTC';
GRANT ALL PRIVILEGES ON DATABASE myproject TO myprojectuser;
\q

Replace 'secure_password_here' with a strong, unique password. Now, exit back to your regular user.

Next, let’s create a system user for running your application. This isn’t strictly necessary, but it’s a good security practice:

adduser --system --group --shell /bin/bash django

This creates a user named django without a home directory or password login.

Setting Up Your Django Project Environment

It’s time to get your actual Django code onto the server. I recommend using Git for this, but you can also use SCP or rsync if you prefer.

First, install virtualenv:

pip3 install virtualenv

Now, switch to your django user and navigate to a suitable directory:

su - django
cd /home/django

Clone your project repository (replace with your actual repo URL):

git clone https://github.com/yourusername/yourproject.git
cd yourproject

Create a virtual environment and activate it:

virtualenv venv
source venv/bin/activate

Now install your project requirements:

pip install -r requirements.txt

At this stage, you should set your environment variables. You can do this by creating a .env file in your project directory (but make sure it’s not committed to version control!) or by exporting them directly. For example:

export SECRET_KEY='your_secret_key_here'
export DEBUG='False'
export ALLOWED_HOSTS='your_droplet_ip,your_domain.com'
export DB_NAME='myproject'
export DB_USER='myprojectuser'
export DB_PASSWORD='secure_password_here'

Run migrations to set up your database:

python manage.py migrate

Collect static files:

python manage.py collectstatic

You might be prompted to confirm; type 'yes' and proceed.

Configuring Gunicorn

Gunicorn will serve your Django application. First, make sure it’s installed in your virtual environment:

pip install gunicorn

Now, let’s create a Gunicorn service file so it can run in the background and restart automatically if needed. Exit back to the root user and create a service file:

exit
nano /etc/systemd/system/gunicorn.service

Add the following content, adjusting paths to match your project:

[Unit]
Description=gunicorn daemon for Django
After=network.target

[Service]
User=django
Group=django
WorkingDirectory=/home/django/yourproject
ExecStart=/home/django/yourproject/venv/bin/gunicorn --access-logfile - --workers 3 --bind unix:/home/django/yourproject/yourproject.sock yourproject.wsgi:application

[Install]
WantedBy=multi-user.target

Save and close the file. Now, start and enable the Gunicorn service:

systemctl start gunicorn
systemctl enable gunicorn

Check its status to make sure it’s running without errors:

systemctl status gunicorn

If you see active (running), you’re good to go!

Configuring Nginx

Nginx will act as a reverse proxy, handling client requests and passing them to Gunicorn. It will also serve your static files.

Create a new Nginx configuration file for your site:

nano /etc/nginx/sites-available/yourproject

Add the following configuration, replacing your_droplet_ip with your actual IP or domain:

server {
    listen 80;
    server_name your_droplet_ip your_domain.com;

    location = /favicon.ico { access_log off; log_not_found off; }
    location /static/ {
        root /home/django/yourproject;
    }

    location / {
        include proxy_params;
        proxy_pass http://unix:/home/django/yourproject/yourproject.sock;
    }
}

Enable the site by creating a symbolic link:

ln -s /etc/nginx/sites-available/yourproject /etc/nginx/sites-enabled/

Test your Nginx configuration to make sure there are no syntax errors:

nginx -t

If everything looks good, restart Nginx:

systemctl restart nginx

You should now be able to visit your server’s IP address in a browser and see your Django app! If you see a 502 Bad Gateway error, check that Gunicorn is running and that the socket file permissions are correct.

Securing Your Deployment with SSL

No modern website should run without HTTPS. Let’s Encrypt provides free SSL certificates, and Certbot makes it easy to set up.

Install Certbot and its Nginx plugin:

apt install certbot python3-certbot-nginx -y

Now, run Certbot to obtain and install your SSL certificate:

certbot --nginx -d your_domain.com -d www.your_domain.com

Follow the prompts. Certbot will automatically update your Nginx configuration to use HTTPS and set up automatic certificate renewal.

You can test the renewal process with:

certbot renew --dry-run

If that runs without errors, you’re all set!

Additional Steps for a Robust Deployment

A basic deployment is up and running, but there are a few more things you can do to make it production-ready.

Set up a firewall using UFW (Uncomplicated Firewall):

ufw allow 'Nginx Full'
ufw allow OpenSSH
ufw enable

This allows HTTPS, HTTP, and SSH traffic while blocking everything else.

Configure automatic security updates:

apt install unattended-upgrades
dpkg-reconfigure -plow unattended-upgrades

Select 'yes' when prompted.

Set up regular backups for your database. You can use pg_dump for PostgreSQL:

# Example backup command
pg_dump -U myprojectuser -h localhost myproject > /backup/path/backup.sql

Consider automating this with a cron job.

Monitor your application logs. Key logs to check:

  • Nginx access and error logs: /var/log/nginx/
  • Gunicorn logs: Check with journalctl -u gunicorn
  • Your Django application logs (if you have logging configured)
Log Type Location Purpose
Nginx Access /var/log/nginx/access.log Client requests and responses
Nginx Error /var/log/nginx/error.log Server and configuration errors
Gunicorn journalctl -u gunicorn Application server events
Django (custom) Defined in your logging config Application-specific events/errors

Troubleshooting Common Issues

Even with careful setup, you might run into issues. Here are some common problems and how to fix them.

Static files not loading? Check that STATIC_ROOT is set correctly and that the Nginx user has read permissions on the static files directory. You might need to run:

chmod -R 755 /home/django/yourproject/staticfiles

Database connection errors? Double-check that your database user and password are correctly set in the environment variables and that the PostgreSQL service is running:

systemctl status postgresql

Gunicorn not starting? Check the journal logs for details:

journalctl -u gunicorn --no-pager -n 50

This will show the last 50 log entries for the Gunicorn service.

Nginx configuration errors? Always test after making changes:

nginx -t

If you get an error, it will usually tell you exactly which line has the problem.

Maintaining Your Deployment

Your Django app is now live, but the work doesn’t stop there. Regular maintenance is key to keeping it secure and performant.

  • Keep your system and Python packages updated. For system packages:
apt update && apt upgrade -y

For Python packages, periodically update your requirements:

source /home/django/yourproject/venv/bin/activate
pip install --upgrade -r requirements.txt

Then restart Gunicorn to apply changes:

systemctl restart gunicorn
  • Monitor your server’s resource usage with tools like htop or glances.
  • Set up monitoring and alerts for downtime or high resource usage. DigitalOcean offers monitoring, or you can use something like UptimeRobot.
  • Regularly back up your database and important files. DigitalOcean provides automated backups for a fee, or you can set up your own script.

Deploying Django on DigitalOcean might seem complex at first, but once you’ve done it a few times, it becomes second nature. You now have a solid foundation to host your Django projects securely and efficiently. Happy coding!