Hello! Welcome to the final module on deploying your Django application.
In the last two lessons, you prepared your application for the real world by managing its dependencies with requirements.txt and configuring static file serving with WhiteNoise. You're on the last stretch before your project goes live!
Today's lesson focuses on the final preparations your codebase needs before it can be deployed to a Platform-as-a-Service (PaaS). A PaaS like Render or Heroku manages the underlying server infrastructure for you, allowing you to focus on your code. Our goal is to make your application robust, secure, and ready for this kind of environment.
By the end of this lesson, you will have prepared your application for deployment by configuring a production-ready web server, setting up for a professional-grade database, securing sensitive settings, and creating a build script to automate deployment tasks.
Let's get your application ready for launch!
1. Production vs. Development Servers: Introducing Gunicorn
So far, you've been running your project with the command python manage.py runserver. This is Django's built-in development server. It's fantastic for development because it's simple and automatically reloads when you change your code.
However, the development server is not suitable for a live, production environment. It's not designed to handle multiple users, it's not very secure, and it's not efficient. For production, we need a dedicated Web Server Gateway Interface (WSGI) server. A popular and robust choice for Django is Gunicorn.

Let's install Gunicorn and add it to our project's dependencies.
- Make sure your virtual environment is activated.
- Install Gunicorn:
pip install gunicorn - Update your
requirements.txtfile immediately:pip freeze > requirements.txt
When we deploy, we will tell the PaaS to start our application using a command like gunicorn your_project.wsgi:application. This command tells Gunicorn to run the application object found in your project's wsgi.py file.
2. Preparing for a Production Database
Just like the development server, the default SQLite database is not ideal for production. It's a single file on your server, which isn't robust enough for handling many simultaneous users and can lead to data integrity issues.
The standard database for production Django applications is PostgreSQL. When we deploy to a service like Render, we'll create a managed PostgreSQL database in the cloud. Our Django application needs the right tools to connect to it.
We need two packages for this:
psycopg2-binary: A "driver" that allows Python to communicate with a PostgreSQL database.dj-database-url: A small utility that lets us configure our database connection from a single URL string, which is how PaaS providers supply database credentials.
Let's install them now.
- Run the following install command:
pip install psycopg2-binary dj-database-url - Once again, update your dependencies:
pip freeze > requirements.txt
With these packages installed, our application is now capable of connecting to a PostgreSQL database once we provide it with the right credentials.
3. Securing Settings with Environment Variables
This is the most critical part of preparing for deployment. Your settings.py file currently contains sensitive information like your SECRET_KEY and settings that should be different in production, like DEBUG = True. Hardcoding these values is insecure and inflexible.
The best practice is to store these values in environment variables. Your local machine will have one set of variables (e.g., DEBUG=True), and the production server will have another (DEBUG=False).
We'll use a package called python-decouple to make reading these variables easy.
Deploy Django Project & Postgresql Database to Render for Free | 2025
The following video provides an excellent walkthrough of setting up environment variables. It explains why we need to hide sensitive data and demonstrates how to use python-decouple to achieve this.
Watch the segment from 00:11:26 to 00:20:27. Focus on these key steps: Installing python-decouple. Creating a .env file to store secrets locally. Creating a .gitignore file to ensure the .env file is never committed to your repository. Modifying settings.py to pull values for SECRET_KEY, DEBUG, ALLOWED_HOSTS, and DATABASE_URL from the environment.
Let's follow the steps shown in the video to prepare your project.
Step 1: Install python-decouple
pip install python-decouple
pip freeze > requirements.txt
Step 2: Create .env and .gitignore
-
In the root directory of your project (the same level as
manage.py), create a file named.env. -
Add your
SECRET_KEYand other development settings to this file. Do not use quotes.# .env SECRET_KEY=your-django-secret-key-goes-here-dont-share-it DEBUG=True DATABASE_URL=sqlite:///db.sqlite3 ALLOWED_HOSTS=127.0.0.1,localhostNote: The
DATABASE_URLfor SQLite points to thedb.sqlite3file in your root directory. -
In the same root directory, create a file named
.gitignore(if you don't already have one). Add.envto it to prevent your secrets from ever being uploaded to GitHub.# .gitignore .env # other files to ignore...
Step 3: Update settings.py
Now, let's modify settings.py to read from these environment variables.
# settings.py
import os
from pathlib import Path
from decouple import config
import dj_database_url # Add this import
# ... (BASE_DIR should already be defined)
# Read the secret key from the environment variable
SECRET_KEY = config('SECRET_KEY')
# Read the debug setting, defaulting to False if not found.
# The `cast=bool` converts the string 'True' or 'False' to a boolean.
DEBUG = config('DEBUG', default=False, cast=bool)
# Read allowed hosts from a comma-separated string
ALLOWED_HOSTS = config('ALLOWED_HOSTS', default='127.0.0.1').split(',')
# ... (your INSTALLED_APPS, MIDDLEWARE, etc.)
# Database configuration
# This will use the DATABASE_URL from your .env file locally,
# and the one provided by Render in production.
DATABASES = {
'default': dj_database_url.parse(config('DATABASE_URL'))
}
# ... (rest of your settings)
Your application is now securely configured. It will use your local .env file for development and will read its settings from the PaaS environment in production.
Test your understanding!
Why is it important to add the .env file to .gitignore? What would happen if you accidentally committed it to a public GitHub repository?
Show answer
Adding .env to .gitignore prevents your secret keys, database passwords, and other sensitive credentials from being stored in your version control history. If you accidentally committed it to a public repository, anyone could see your SECRET_KEY, which would allow them to potentially forge sessions and compromise your application's security. They might also get access to your production database credentials.
4. Creating a Build Script
When you deploy your code, the PaaS needs to run a few commands to set everything up. These commands typically include:
- Installing all the Python packages from
requirements.txt. - Running
collectstaticto gather all your static files for WhiteNoise. - Running
migrateto apply any database schema changes.
We can automate this by creating a simple shell script. Render specifically looks for a build.sh file.
The official Render documentation explains the need for a build script. We will follow its recommendation to create one.
Read the section titled 'Create a build script'. This short section gives you the exact content for the build.sh file and the command to make it executable.
As described in the documentation, create a new file named build.sh in your project's root directory and add the following content:
#!/usr/bin/env bash
# exit on error
set -o errexit
pip install -r requirements.txt
python manage.py collectstatic --no-input
python manage.py migrate
set -o errexit: This is a safety measure. It tells the script to exit immediately if any command fails.pip install ...: Installs your dependencies.collectstatic --no-input: Collects static files without asking for user confirmation.migrate: Applies database migrations.
Finally, make the script executable by running this command in your terminal:
chmod +x build.sh
Now, your project contains all the instructions needed for a PaaS to build and run it automatically.
Conclusion
Excellent work! Your application is now fully prepared for deployment. You've taken it from a development-centric setup to a secure, robust, and platform-ready project.
Key Takeaways:
- The Django development server is for development only; Gunicorn is a production-grade WSGI server for handling live traffic.
- SQLite is not suitable for production; you've prepared your app to use PostgreSQL by installing the necessary driver (
psycopg2-binary) and helper (dj-database-url). - Sensitive data and environment-specific settings must be stored in environment variables, not in code.
python-decouplehelps manage this securely. - A
build.shscript automates the setup process on the deployment server, ensuring dependencies are installed and database migrations are run.
Preview of the Next Lesson:
You've done all the prep work. In the next and final lesson of this module, we will perform the actual deployment. You'll create a GitHub repository, set up your services on Render, push your code, and watch your application go live on the internet. Get ready to see your project in the wild