Deploying a Django application does not have to mean paying for expensive hosting from day one. If you want a practical, low-cost, production-style setup for a personal project, portfolio site, client app, or MVP, Oracle Cloud Free Tier is one of the best places to start.

It gives you access to a real virtual machine, a public IP, persistent storage, and enough compute to run a surprisingly capable Django stack. That means you are not limited by platform abstractions. You can configure Linux yourself, install Python packages directly, run Gunicorn, place Nginx in front, attach your domain, enable HTTPS, and deploy your app the same way many real production servers are set up.

This guide walks through that process step by step.

What You Will Build

By the end of this tutorial, you will have a live Django app running on Oracle Cloud Free Tier with a clean production-style stack built around Oracle Linux, Gunicorn, Nginx, and systemd. Static files will be served properly, your domain can point to the server, and the setup will be ready for SSL.

The deployment pattern looks like this:

Browser -> Nginx -> Gunicorn -> Django

Here is what each layer does:

  • Nginx handles incoming web traffic, serves static files, and forwards application requests.
  • Gunicorn runs your Django application as a production WSGI server.
  • Django handles your application logic, templates, models, and views.

This separation matters. Django's built-in development server is useful for local development, but it is not meant to be your production web server.

Why Oracle Cloud Free Tier Is a Good Option for Django

There are many places where you can host a Django application, but Oracle Cloud Free Tier stands out because it gives you access to an actual server environment instead of a heavily abstracted deployment platform.

That flexibility matters because Django projects often grow beyond simple static pages. You may want scheduled jobs, Celery workers, custom Python libraries, PostgreSQL, reverse proxy control, background tasks, or custom Nginx rules. A VM-based deployment gives you that control from the beginning.

For small Django projects, Oracle Cloud Free Tier is especially useful for:

  • portfolio and freelance websites
  • internal tools
  • dashboards and admin panels
  • early-stage SaaS MVPs
  • club, community, or organization web apps
  • lightweight e-commerce or booking systems

If your goal is to learn real deployment while keeping costs near zero, this is a strong option.

What You Need Before You Begin

Before starting, make sure you have:

  • an Oracle Cloud account
  • a Django project ready to deploy
  • SSH access from your computer
  • a domain name if you want a custom URL
  • your project code in Git or available to upload manually

You should also decide which database you want to use.

For very small projects, SQLite may be enough. For anything multi-user or more serious, PostgreSQL is the better long-term choice.

Step 1: Create an Oracle Cloud Free Tier Instance

The first step is to create a virtual machine inside Oracle Cloud Infrastructure.

When creating the instance:

  • choose an Always Free eligible shape
  • prefer Ampere A1 if it is available in your region
  • choose Oracle Linux
  • add your SSH public key during setup
  • ensure the instance has a public IP

Once the instance is created, Oracle Cloud will show you its public IP address. Save it, because you will need it for SSH, browser testing, and later domain setup.

At this stage, you now have the server, but it is still just a blank machine. The next steps turn it into a real Django host.

Step 2: Open the Required Network Ports

One of the most common deployment mistakes is assuming the server is broken when the real issue is network access.

In Oracle Cloud, make sure your networking rules allow inbound traffic on these ports:

  • 22 for SSH
  • 80 for HTTP
  • 443 for HTTPS

If these ports are not open in your security list or network security group, your app may be running perfectly and still remain inaccessible from the outside.

Step 3: Connect to the Server Over SSH

Once the instance is live, connect to it from your terminal:

ssh -i /path/to/your/private-key opc@YOUR_SERVER_IP

On many Oracle Linux images, the default login user is opc.

After logging in, update the system:

sudo dnf update -y

This ensures you are starting from a clean and updated base.

Step 4: Install Python, Git, and Nginx

Install the core packages needed for deployment:

sudo dnf install -y python3 python3-pip python3-venv git nginx

Depending on your dependencies, you may also want:

sudo dnf install -y gcc python3-devel

These extra packages help if some Python dependencies need compilation during installation.

Step 5: Create a Directory for Your Django Project

A clean server structure makes maintenance easier later.

Create a project folder:

sudo mkdir -p /srv/myproject
sudo chown -R $USER:$USER /srv/myproject
cd /srv/myproject

Now bring your code onto the server.

If your project is in Git:

git clone https://github.com/yourusername/yourrepo.git .

If not, upload your files manually and place them in this directory.

This becomes the main application root for your deployment.

Step 6: Create a Virtual Environment and Install Dependencies

A virtual environment keeps your application dependencies isolated from the rest of the server.

Inside your project folder, run:

python3 -m venv .venv
source .venv/bin/activate
pip install --upgrade pip
pip install -r requirements.txt

If Gunicorn is not already included in your requirements file, install it explicitly:

pip install gunicorn

If dependency installation fails here, resolve that first before moving forward. Later deployment steps depend on this being correct.

Step 7: Configure Django for Production

This is where many deployments go wrong. Local development settings and production settings are not the same.

Open your Django settings file and review the following carefully.

Turn off debug mode:

DEBUG = False

Leaving debug enabled in production is both insecure and unprofessional.

Set allowed hosts:

ALLOWED_HOSTS = ["yourdomain.com", "www.yourdomain.com", "YOUR_SERVER_IP"]

If this value is incorrect, Django may reject incoming requests with a 400 Bad Request error.

Configure static files:

STATIC_URL = "/static/"
STATIC_ROOT = BASE_DIR / "staticfiles"

Later, this allows Nginx to serve collected static assets directly.

Use environment variables for secrets.

Do not hardcode your production secret key or database credentials directly in your settings file. Load them from environment variables instead.

A typical production app should also eventually enable:

SECURE_PROXY_SSL_HEADER = ("HTTP_X_FORWARDED_PROTO", "https")
SESSION_COOKIE_SECURE = True
CSRF_COOKIE_SECURE = True

Once HTTPS is working, you can also enable:

SECURE_SSL_REDIRECT = True

This is a good point to think of the deployment as transitioning from "my app runs locally" to "my app is safe to expose on the public internet."

Step 8: Create an Environment File

Create a .env file in your project directory:

nano /srv/myproject/.env

A simple example looks like this:

SECRET_KEY=your-long-secret-key
DEBUG=False
ALLOWED_HOSTS=yourdomain.com,www.yourdomain.com

If you are using PostgreSQL, add your database values too.

Then update your Django settings to read from these environment variables. You can do this with Python's built-in os.environ or libraries such as django-environ.

This approach keeps secrets outside your codebase and makes your deployment cleaner.

Step 9: Run Migrations and Collect Static Files

Now that the project is configured, apply database migrations and gather static files into the location Nginx will use later.

source /srv/myproject/.venv/bin/activate
python manage.py migrate
python manage.py collectstatic --noinput

If your project needs an admin user, create one now:

python manage.py createsuperuser

This is also a good moment to verify that Django itself runs correctly.

You can do a quick test using:

python manage.py runserver 0.0.0.0:8000

Then visit:

http://YOUR_SERVER_IP:8000

If the site works here, that is a strong sign that your Django project itself is fine and any later issues are likely in Gunicorn, Nginx, or networking.

Step 10: Test Gunicorn Directly

Before introducing Nginx, test the production application server on its own.

Run:

source /srv/myproject/.venv/bin/activate
gunicorn --bind 0.0.0.0:8000 myproject.wsgi:application

Replace myproject with your actual Django project package name.

Then open your server IP again in the browser at port 8000.

If the app loads through Gunicorn, you have confirmed that your Django application and WSGI entry point are working correctly.

Step 11: Create a systemd Service for Gunicorn

You do not want to manually start Gunicorn every time the server reboots.

That is where systemd comes in.

Create a service file:

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

Add the following:

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

[Service]
User=opc
Group=nginx
WorkingDirectory=/srv/myproject
EnvironmentFile=/srv/myproject/.env
ExecStart=/srv/myproject/.venv/bin/gunicorn \
    --workers 3 \
    --bind unix:/run/gunicorn.sock \
    myproject.wsgi:application

[Install]
WantedBy=multi-user.target

Then enable and start it:

sudo systemctl daemon-reload
sudo systemctl enable gunicorn
sudo systemctl start gunicorn
sudo systemctl status gunicorn

If something fails, inspect the logs:

sudo journalctl -u gunicorn -n 100 --no-pager

Using a Unix socket instead of a public TCP port is a clean same-server pattern and works very well with Nginx.

Step 12: Configure Nginx as a Reverse Proxy

Now configure Nginx to serve static files and pass application requests to Gunicorn.

Create a config file:

sudo nano /etc/nginx/conf.d/myproject.conf

Use this:

server {
    listen 80;
    server_name yourdomain.com www.yourdomain.com YOUR_SERVER_IP;

    location /static/ {
        alias /srv/myproject/staticfiles/;
    }

    location / {
        include proxy_params;
        proxy_pass http://unix:/run/gunicorn.sock;
    }
}

Then test the config:

sudo nginx -t

If the syntax is valid, start or restart Nginx:

sudo systemctl enable nginx
sudo systemctl restart nginx
sudo systemctl status nginx

At this point, your app should be reachable through normal HTTP traffic on port 80.

Step 13: Point Your Domain to the Server

If you want your app on a real domain instead of just an IP address, go to your domain provider and create the required DNS records.

Usually this means:

  • an A record for yourdomain.com
  • an A record or CNAME for www

Point them to your Oracle server's public IP address.

Once DNS propagates, visiting your domain should load the site through Nginx.

Step 14: Add HTTPS with Let's Encrypt

A production site should use HTTPS. It builds trust, improves security, and is expected by browsers and users.

Once your domain is correctly resolving and your site is loading on HTTP, install Certbot and request an SSL certificate.

Then run:

sudo certbot --nginx -d yourdomain.com -d www.yourdomain.com

Follow the prompts to complete setup.

After HTTPS is working, return to Django settings and enable:

SECURE_SSL_REDIRECT = True
SESSION_COOKIE_SECURE = True
CSRF_COOKIE_SECURE = True

This is one of the most satisfying moments in deployment. Your site is now encrypted, accessible through a real domain, and much closer to a true production environment.

Step 15: Optional PostgreSQL Setup for More Serious Projects

If your application is small and mostly read-only, SQLite may be enough for now. But if you expect multiple users, more writes, or future scaling, PostgreSQL is the better choice.

Install PostgreSQL:

sudo dnf install -y postgresql-server postgresql-contrib

Initialize and start it:

sudo postgresql-setup --initdb
sudo systemctl enable postgresql
sudo systemctl start postgresql

Create a database and user:

sudo -u postgres psql

Then inside PostgreSQL:

CREATE DATABASE mydb;
CREATE USER mydbuser WITH PASSWORD 'strongpassword';
GRANT ALL PRIVILEGES ON DATABASE mydb TO mydbuser;
\q

Update your Django settings to use the PostgreSQL connection details from environment variables.

This gives you a more production-ready data layer and is worth doing for client-facing or multi-user apps.

Common Problems You May Run Into

Every deployment has friction points. Knowing the likely ones makes troubleshooting much easier.

The site does not open in the browser

Usually this is caused by one of these:

  • port 80 or 443 not open in Oracle networking
  • Nginx not running
  • domain pointing to the wrong IP
  • Gunicorn not running behind Nginx

Django returns 400 Bad Request

This almost always means your ALLOWED_HOSTS value is wrong or incomplete.

Static files are missing

Check these carefully:

  • STATIC_ROOT
  • collectstatic was run
  • Nginx alias path is correct
  • file permissions allow Nginx access

Gunicorn fails to start

Common causes include:

  • wrong project path
  • wrong WSGI module path
  • missing environment variables
  • virtual environment path mistakes
  • application import errors

Use:

sudo journalctl -u gunicorn -n 100 --no-pager

HTTPS certificate request fails

Usually because:

  • DNS has not propagated yet
  • port 80 is not reachable
  • Nginx config for the domain is incorrect

Most deployment issues are not complicated once you isolate the layer where the problem exists.

A Simple Deployment Workflow for Future Updates

Once the server is working, deploying code updates becomes much easier.

A typical update flow looks like this:

cd /srv/myproject
git pull
source .venv/bin/activate
pip install -r requirements.txt
python manage.py migrate
python manage.py collectstatic --noinput
sudo systemctl restart gunicorn

That is enough for many small Django applications.

If your changes only affect Python code and templates, you may not always need every step, but this full sequence is a safe baseline.

Is Oracle Cloud Free Tier Good Enough for Production Django?

For small projects, yes. It is more than good enough for many portfolio apps, club apps, internal tools, dashboards, early SaaS MVPs, and community platforms.

What makes it attractive is not just the price. It is the level of control. You are learning how to deploy Django in a way that maps well to real Linux server environments, and that knowledge carries forward to larger VPS or cloud deployments later.

That said, free tier infrastructure still has limits. It is not ideal for high-traffic applications, complex distributed systems, or anything that requires guaranteed enterprise-grade reliability without redundancy.

But for solo developers, freelancers, and early builders, it is one of the most practical ways to get a real Django app online.

Final Thoughts

Deploying a Django application to Oracle Cloud Free Tier is one of the best ways to combine low-cost hosting with real deployment experience.

You are not just pressing a deploy button and hoping for the best. You are learning how each layer of a production web application fits together:

  • cloud networking
  • Linux server setup
  • Python virtual environments
  • Django production settings
  • Gunicorn as the app server
  • Nginx as the reverse proxy
  • SSL for security
  • systemd for reliability

Once you understand that flow, deploying Django becomes much less intimidating.

The good news is that the setup is not actually complicated once broken into steps. Create the server, open the ports, install Python, configure Django, run Gunicorn, place Nginx in front, attach your domain, and enable HTTPS. That is the core pattern, and it works extremely well.

If you are building a personal project, a client web app, or an MVP and want a serious hosting setup without paying for expensive infrastructure, Oracle Cloud Free Tier is absolutely worth considering.

Quick Command Summary

Here is the condensed version of the workflow:

sudo dnf update -y
sudo dnf install -y python3 python3-pip python3-venv git nginx

sudo mkdir -p /srv/myproject
sudo chown -R $USER:$USER /srv/myproject
cd /srv/myproject

git clone https://github.com/yourusername/yourrepo.git .

python3 -m venv .venv
source .venv/bin/activate
pip install --upgrade pip
pip install -r requirements.txt
pip install gunicorn

python manage.py migrate
python manage.py collectstatic --noinput

gunicorn --bind 0.0.0.0:8000 myproject.wsgi:application

Then add:

  • the gunicorn.service file
  • the Nginx config
  • your DNS records
  • HTTPS with Certbot
  • final Django production settings

About Me

Abhyuday Singh

I build products that mix software, data, automation, UI/UX, and AI, and I like writing about what I learn along the way.

Get in touch