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:
Nginxhandles incoming web traffic, serves static files, and forwards application requests.Gunicornruns your Django application as a production WSGI server.Djangohandles 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:
22for SSH80for HTTP443for 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
Arecord foryourdomain.com - an
Arecord orCNAMEforwww
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
80or443not 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_ROOTcollectstaticwas run- Nginx
aliaspath 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
80is 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.servicefile - the Nginx config
- your DNS records
- HTTPS with Certbot
- final Django production settings