Django

Deploy a Django App

Django (opens in a new tab) is a robust, full-featured web framework for Python that simplifies building scalable, performant web applications and sites. With a large community and an impressive list of features and integrations, Django takes a batteries-included approach to bundling functionality while allowing users to swap out or add additional components as they see fit.

This guide explains how to deploy a Django application on Koyeb using:

  1. Git-driven deployment to automatically build and deploy a new version of your application each time a change is detected on your branch.
  2. Pre-built containers you can deploy from any public or private registry.

To successfully follow this documentation, you will need to have a Koyeb account (opens in a new tab). You can optionally install the Koyeb CLI if you prefer to follow this guide without leaving the terminal.

You can deploy and preview the Django application from this guide using the Deploy to Koyeb button below.

Deploy to Koyeb (opens in a new tab)

You can consult the repository on GitHub (opens in a new tab) to find out more about the example application that this guide uses.

Create the Django app

Get started by creating a minimalistic Django application that we will deploy on Koyeb. You will need Python (opens in a new tab) installed on your machine.

Create a virtual environment

In your terminal, run the following commands to create the directory that will hold the application code:

mkdir example-django
cd example-django

In the folder you created, create a new virtual environment folder. Virtual environments provide isolation from the system software, allowing each project to manage its own installation directories, dependencies, etc.

On some platforms, the Python interpreter is installed as python3 instead of python. For these platforms, substitute python3 for python in the command below.

Create a new virtual environment for the project by typing:

python -m venv venv

Activate and load your virtual environment by typing:

source venv/bin/activate

Your prompt should now include the venv environment name, indicating that the virtual environment is now active.

Install dependencies

Next, install:

pip install Django gunicorn whitenoise

Koyeb detects Python applications when one of the Python matching criteria is met. Create a requirements.txt file to store the dependencies and versions of each package required to run the application. This file is necessary in order for Koyeb to identify this as a Python project:

pip freeze > requirements.txt

Create and configure a Django project

Next, use the django-admin startproject command to start a new project. Because you already created a project directory to store the virtual environment, you can tell Django to install the new project within the context of the current directory:

django-admin startproject example_django ./

This will generate a project management script called manage.py as well as a directory called example_django containing the main project code.

Your directory structure should now look like this. You can see similar output using tree -L 2 or du -d 2 -a:

example-django
├── example_django
│   ├── asgi.py
│   ├── __init__.py
│   ├── settings.py
│   ├── urls.py
│   └── wsgi.py
├── manage.py
├── requirements.txt
└── venv
    ├── . . .

Open the example_django/settings.py file in your editor to configure the project.

Towards the top of the file, add a line to import the os module:

example_django/settings.py
import os
from pathlib import Path

Afterwards, find the ALLOWED_HOSTS setting. Use the os module to modify this entry so that Django looks for permitted hosts in an environment variable called DJANGO_ALLOWED_HOSTS:

example_django/settings.py
#ALLOWED_HOSTS = []
ALLOWED_HOSTS = os.getenv("DJANGO_ALLOWED_HOSTS", "localhost,127.0.0.1,[::1]").split(",")

This configuration uses the environment variable if it is set and falls back to Django's default setting otherwise.

Next, find the MIDDLEWARE dictionary. Directly below Django's SecurityMiddleware entry, add the WhiteNoise middleware:

example_django/settings.py
MIDDLEWARE = [
    'django.middleware.security.SecurityMiddleware',
    'whitenoise.middleware.WhiteNoiseMiddleware',
    'django.contrib.sessions.middleware.SessionMiddleware',
    'django.middleware.common.CommonMiddleware',
    'django.middleware.csrf.CsrfViewMiddleware',
    'django.contrib.auth.middleware.AuthenticationMiddleware',
    'django.contrib.messages.middleware.MessageMiddleware',
    'django.middleware.clickjacking.XFrameOptionsMiddleware',
]

Finally, set up the static file configuration. Locate the STATIC_URL setting and add the following lines:

example_django/settings.py
STATIC_URL = 'static/'
STATIC_ROOT = BASE_DIR / 'staticfiles'
STATICFILES_STORAGE = "whitenoise.storage.CompressedManifestStaticFilesStorage"

Setting STATIC_ROOT tells Django where to gather static assets. The STATICFILES_STORAGE value enables compression and caching.

Run the Django app locally

Now, you can launch the application locally to make sure everything is running as expected.

First, launch the application using Django's native development server. Run the following command from the project root directory where the manage.py file is located:

python manage.py runserver

You will see a note about unapplied migrations that we can safely ignore for this demo. Access the demo server by visiting http://127.0.0.1:8000 in your browser.

If you navigate to http://127.0.0.1:8000/admin, you will see the log in page for Django's default administration interface. Django's development server automatically serves the static assets associated with the site.

Press CTRL-C to stop the development server after verifying.

Next, check to make sure that gunicorn can serve the project correctly. From the same directory, type:

gunicorn example_django.wsgi

Again, verify that the application is being served at http://127.0.0.1:8000. Check the administration interface at http://127.0.0.1:8000/admin again and refresh the page. Since we are no longer serving the site using Django's development server, WhiteNoise is responsible for serving the static files correctly.

Press CTRL-C to stop the development server when you are finished.

This example application should run on any modern version of Python. If your application requires a specific Python version, create a runtime.txt file in your repository with the version number. Consult the build with Python page to learn more.

Push the project to GitHub

In the project root directory, initialize a new git repository by running the following command:

git init

We will use this repository to version the application code and push the changes to a GitHub repository. If you don't have an existing GitHub repository to push the code to, create one now.

Pull down GitHub's default .gitignore file for Python to help avoid accidentally committing unnecessary and unwanted files:

curl -L https://raw.githubusercontent.com/github/gitignore/master/Python.gitignore -o .gitignore

Run the following commands to commit and push changes to the repository. Remember to replace the values of <YOUR_GITHUB_USERNAME> and <YOUR_REPOSITORY_NAME> with your own information:

git add .
git commit -m "Initial commit"
git remote add origin git@github.com:<YOUR_GITHUB_USERNAME>/<YOUR_REPOSITORY_NAME>.git
git push -u origin main

Deploy to Koyeb using git-driven deployment

To deploy the Django app on Koyeb, using the control panel (opens in a new tab) follow the steps below:

  1. Click Create Web Service on the Overview tab of the Koyeb control panel.

  2. Select GitHub as the deployment option.

  3. Choose the GitHub repository and branch containing your application code. Alternatively, you can enter our public Django example repository (opens in a new tab) into the Public GitHub repository at the bottom of the page: https://github.com/koyeb/example-django.

  4. In the Builder section, keep Buildpack selected. Click the Override toggle associated with the Run command. In the command field, enter the gunicorn command from earlier:

    gunicorn example_django.wsgi
  5. Towards the bottom, name your Service and App, for example example-django.

  6. In the Environment variables section and click Add variable. Add a variable named DJANGO_ALLOWED_HOSTS using the pattern <YOUR_APP_NAME>-<YOUR_KOYEB_ORG>.koyeb.app. Be sure to replace <YOUR_APP_NAME> and <YOUR_KOYEB_ORG> with your own information.

  7. Click the Deploy button.

A Koyeb App and Service will be created. Your application will be built and deployed to Koyeb. Once the build has finished, you will be able to access your application running on Koyeb by clicking the URL ending with .koyeb.app.

Deploy to Koyeb using a pre-built container

As an alternative to using git-driven deployment, you can deploy a pre-built container from any public or private registry. This can be useful if your application needs specific system dependencies or you need more control over how the build is performed.

To dockerize the Django application, create a Dockerfile in your project root directory and copy the content below:

FROM python:3-alpine AS builder
 
WORKDIR /app
 
RUN python3 -m venv venv
ENV VIRTUAL_ENV=/app/venv
ENV PATH="$VIRTUAL_ENV/bin:$PATH"
 
COPY requirements.txt .
RUN pip install -r requirements.txt
 
# Stage 2
FROM python:3-alpine AS runner
 
WORKDIR /app
 
COPY --from=builder /app/venv venv
COPY example_django example_django
 
ENV VIRTUAL_ENV=/app/venv
ENV PATH="$VIRTUAL_ENV/bin:$PATH"
ENV PORT=8000
 
EXPOSE ${PORT}
 
CMD gunicorn --bind :${PORT} --workers 2 example_django.wsgi

The Dockerfile above provides the minimum requirements to run the Django application. You can easily extend it depending on your needs.

To build and push the Docker image to a registry and deploy it on Koyeb, refer to the Deploy an app from a Docker image.

During configuration, be sure to add an environment variable called DJANGO_ALLOWED_HOSTS that follows the <YOUR_APP_NAME>-<YOUR_KOYEB_ORG>.koyeb.app pattern, replacing <YOUR_APP_NAME> and <YOUR_KOYEB_ORG> with your own information. This will tell Django to allow connections to your Koyeb subdomain URL.