Deploying a Django project to a live server can feel overwhelming at first — but once you break it into steps, it becomes straightforward. In this guide, you'll learn how to deploy your Django app using PostgreSQL and Nginx on a DigitalOcean Ubuntu server.
Step 1: Set Up Your DigitalOcean Droplet
1. Create a DigitalOcean Droplet
- Go to DigitalOcean and log in.
- Click “Create Droplet”.
- Choose Ubuntu 22.04 LTS as the operating system.
- Select a plan (e.g., Basic with 1GB RAM for small projects).
- Add your SSH key (or choose a password if you're not using SSH).
- Finalize and launch your droplet.
2. Access Your Droplet via SSH
ssh root@your_server_ip
3. Create a New User (Recommended for Security)
adduser dydevops
usermod -aG sudo dydevops
4. Enable the UFW Firewall
ufw allow OpenSSH
ufw enable
5. Install Essential Packages:
sudo apt update && sudo apt upgrade -y
sudo apt install python3-pip python3-dev ufw nginx -y
Step 2: Set Up Your Project Directory
1. Switch to the New User:
su - dydevops
2. Create Project Directory:
cd /home/dydevops/
mkdir myproject
cd myproject
3. Upload Your Project Files:
Use an FTP client like FileZilla to upload your local Django project files to the /home/dydevops/myproject/
directory on the server.
4. Now ssh in to the server as dydevops user
ssh dydevops@SERVER_IP_ADDRES
sudo ufw allow ssh
sudo ufw enable
sudo apt update
sudo pip3 install virtualenv
Step 3 : Security & Access
Creating SSH keys (Optional)
You can choose to create SSH keys to login if you want. If not, you will get the password sent to your email to login via SSH
To generate a key on your local machine
$ ssh-keygen
Hint enter all the way through and it will create a public and private key at
~/.ssh/id_rsa
~/.ssh/id_rsa.pub
Add SSH key for new user
Navigate to the new users home folder and create a file at ‘.ssh/authorized_keys’ and paste in the key
cd /home/dydevops
mkdir .ssh
cd .ssh
Open the authorized_keys
file (or create it if it doesn’t exist):
nano authorized_keys
Paste your public key into the file (usually starts with ssh-rsa
or ssh-ed25519
).
Save and exit:
- Press
Ctrl + X
to exit - Press
Y
to confirm saving - Press
Enter
to finalize the file name
Set Proper Permissions:
chmod 600 authorized_keys
chmod 700 ~/.ssh
Now you can SSH into the server using your key, without typing the password every time.
Possible Deployment Issues & How to Prevent Them
Before pushing your Django project to production, it's important to handle a few key items that are often the root cause of errors. Follow these best practices to avoid common deployment pitfalls:
Set ALLOWED_HOSTS
in settings.py
Make sure you include your server's IP address or domain name:
ALLOWED_HOSTS = ['your_domain.com', 'your_server_ip']
❗ Not setting this correctly can lead to a
DisallowedHost
error when trying to access your site.
Release the media
Folder
Ensure your media folder (for user-uploaded content) is tracked and accessible on the production server. Update your Nginx config to serve media files correctly.
In your settings.py
:
MEDIA_URL = '/media/'
MEDIA_ROOT = os.path.join(BASE_DIR, 'media')
Collect Static Files
Run this command to gather all static files into your STATIC_ROOT
directory:
python manage.py collectstatic
This is necessary for serving CSS, JS, and image files through Nginx in production.
Create requirements.txt
To ensure your production server installs the exact same Python packages as your local machine:
pip freeze > requirements.txt
Then, upload this file to your server and run:
pip install -r requirements.txt
- Replace psycopg2 package with psycopg2-binary
- Install backports.zoneinfo only if the server python version is less than 3.9
- backports.zoneinfo==0.2.1;python_version<"3.9"
- Add gunicorn to requirements.txt

Django File Upload Using FTP (FileZilla Server Setup & Example)
In some cases, especially for legacy systems or restricted hosting environments, you may want to handle Django file uploads via FTP instead of directly through HTTP POST.
This section will walk you through:
- Setting up FileZilla Server on Windows
- Using Django with an FTP backend
- Backing up and restoring your local Django database to the production server
Install FileZilla Server (Windows 64-bit)
To allow FTP uploads from your local development environment or external clients:
- Download FileZilla Server from the official site:
Download FileZilla Server 64-bit - Install and configure it:
- Set your FTP port (default: 21)
- Create a new user and set a password
- Assign a home directory (this is where files will be uploaded)
- Ensure the Windows firewall allows FTP traffic (port 21)
FileZilla Server acts as the FTP backend which your Django app or manual workflows can interact with.
Use Minimal Django File Upload Example (Educational Demo)
To keep things clean and educational, use a minimal Django project designed specifically to show how file uploading works.
Example source: minimal-django-file-upload-example
Why use this?
- It's stripped of extra features, so you can focus on the file upload logic.
- Helps you clearly understand what Django needs: form, view,
MEDIA_ROOT
, and upload handler.
Basic components include:
forms.py
: withFileField
views.py
: usingrequest.FILES
to handle uploadssettings.py
: withMEDIA_URL
andMEDIA_ROOT
configuredurls.py
: to serve uploaded files in development
Backup & Restore Django Data Between Local & Production Servers
When moving data from your local development environment to the live server, you’ll want to back up your database and restore it properly.
On Your Local Machine
Export your database to JSON format:
python manage.py dumpdata > backup.json
While you are logged in to the server via SSH, create a project directory as myproject inside /home/dydevops/ folder
cd /home/dydeops/
mkdir myproject
cd myproject
Now check the remote directory, our project is pushed to the server. That’s all, we have set up Filezilla to upload the code from local machine to ftp server.
Install & Configure PostgreSQL
While you are logged into the server, run following commands to install, start, enable and see the status of postgresql database:
sudo apt-get install postgresql postgresql-contrib
sudo systemctl start postgresql.service
sudo systemctl enable postgresql.service
sudo systemctl status postgresql.service
Install PostgreSQL and Extensions
Run the following command to install PostgreSQL and its standard additional tools:
sudo apt-get install postgresql postgresql-contrib
postgresql
installs the core database systempostgresql-contrib
provides additional features and extensions
Start the PostgreSQL Service
After installation, start the PostgreSQL service:
sudo systemctl start postgresql.service
Enable PostgreSQL on Boot
Ensure PostgreSQL automatically starts when your server boots:
sudo systemctl enable postgresql.service
Check PostgreSQL Service Status
Verify that PostgreSQL is running correctly:
sudo systemctl status postgresql.service
PostgreSQL Database & User Setup for Django
Once PostgreSQL is installed and running, you'll need to create:
- A database
- A user with access to that database
- And grant privileges so Django can connect properly
Access PostgreSQL Shell
Switch to the postgres
system user to access the PostgreSQL command-line interface:
sudo -u postgres psql
You'll now be in the psql
prompt:
postgres=#
Create a New Database
CREATE DATABASE myprojectdb;
Replace myprojectdb
with your actual database name.
Create a New User
CREATE USER myprojectuser WITH PASSWORD 'your_secure_password';
Make sure to replace myprojectuser
and 'your_secure_password'
with your desired username and a strong password.
Set default encoding, tansaction isolation scheme (Recommended from Django)
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 Access to the User
GRANT ALL PRIVILEGES ON DATABASE myprojectdb TO myprojectuser;
Exit PostgreSQL Shell
\q
You're now back to the Ubuntu shell.
Set Up a Virtual Environment & Run the Server
Using a virtual environment keeps your Django project's dependencies isolated from the system Python and avoids package conflicts.
Follow these steps inside your project directory (e.g., /home/dydevops/myproject/
):
1. Install virtualenv
(if not already installed)
sudo apt install python3-virtualenv
2. Create a Virtual Environment
Navigate to your project directory:
cd /home/dydevops/myproject/
Then create a virtual environment named env
:
virtualenv env
This will create a new folder named env/
containing an isolated Python environment.
Activate the Virtual Environment
source env/bin/activate
You should see your terminal prompt prefixed with (env)
— indicating that the virtual environment is active.
Install Project Requirements
If you already created requirements.txt
, install all necessary packages:
pip install -r requirements.txt
Run makemigrations and migrate commands
python manage.py migrate
If not, install Django and any other dependencies manually, then generate the file:
pip install django psycopg2-binary
pip freeze > requirements.txt
Allow port 8000
sudo ufw allow 8000
Check status of ufw
sudo ufw status
Run the Django Development Server (for Testing)
To verify that everything works:
python manage.py runserver 0.0.0.0:8000
You should now be able to visit your server’s IP in the browser via port 8000
to see the default Django page.
Note: For production use, you'll run Django with Gunicorn + Nginx, not the development server.
Remote server
python manage.py shell
from django.contrib.contenttypes.models import ContentType
ContentType.objects.all().delete()
quit()
python manage.py loaddata backup.json
Configuring Gunicorn
Install Gunicorn on our server
sudo apt install gunicorn
Tell gunicorn to bind to our Django application and start running
gunicorn --bind 0.0.0.0:8000 mwcshop.wsgi
Test the link and make sure the site is working
exit
Go to this location and paste below code:
sudo nano /etc/systemd/system/gunicorn.socket
[Unit]
Description=gunicorn socket
[Socket]
ListenStream=/run/gunicorn.sock
[Install]
WantedBy=sockets.target
Location:
sudo nano /etc/systemd/system/gunicorn.service
[Unit]
Description=gunicorn daemon
Requires=gunicorn.socket
After=network.target
[Service]
User=dydevops
Group=www-data
WorkingDirectory=/home/dydevops/myproject
ExecStart=/home/dydevops/myproject/env/bin/gunicorn \
--access-logfile - \
--workers 3 \
--bind unix:/run/gunicorn.sock \
mwcshop.wsgi:application
[Install]
WantedBy=multi-user.target
Restart and enable Gunicorn socket
sudo systemctl start gunicorn.socket
sudo systemctl enable gunicorn.socket
Configuring Nginx as a reverse proxy
sudo nano /etc/nginx/sites-available/dydevops
server {
listen 80;
server_name dydevops.com www.dydevops.com;
location ~ ^/.well-known {
root /home/dydevops/myproject;
allow all;
}
location = /favicon.ico { access_log off; log_not_found off; }
location /static/ {
root /home/dydevops/myproject;
}
location /media/ {
root /home/dydevops/myproject;
}
location / {
include proxy_params;
proxy_pass http://unix:/run/gunicorn.sock;
}
}
sudo ln -s /etc/nginx/sites-available/dydevops /etc/nginx/sites-enabled/
sudo systemctl restart nginx
Open port 80 and close 8000
sudo ufw allow 80
sudo ufw allow 'Nginx Full'
sudo ufw allow 586
sudo ufw deny 8000
Go to sites-enabled directory and delete the default site
cd /etc/nginx/sites-enabled/
ls
sudo rm default
Fix Static Files on Server
cd /etc/nginx/
ls
sudo nano nginx.conf

sudo systemctl restart nginx
cd myproject
sudo nano mwcshop/settings.py
ALLOWED_HOSTS = ['SERVER_IP_ADDRESS', 'www.dydevops.com', 'dydevops.com']
sudo service nginx restart
sudo service gunicorn restart