Coolify your Django project

12 min read

|

Jan 21 2025

https://images.fmacedo.com/turnstile_htmx.png

Learn how to deploy a Django project with Coolify

What is Coolify?

Coolify is a tool that helps deploy web projects. It claims to be an open-source & self-hostable Heroku / Netlify / Vercel alternative.

I’ve been using it for a while with some Django projects (and other frameworks) and it has been a good experience. Some of them are up and runing like:

  • Join the DJ - a platform for DJs to accept song requests;
  • Streamliter - a platform to create streamlit apps entirely in the browser;

In this tutorial, I’ll show you how to deploy a Django project with coolify in your own VPS or virtual machine.

I’ll start by creating a new Django project with some opinionated choices. Then, I’ll show you how to deploy it with coolify.

If you just want to see the final result of the code, you can check out the django-coolify-tutorial repository.

Biggest drawback with coolify (in my opinion)

Although I’m a big fan of coolify, it’s not perfect. I have one major complaint:

It doesn’t come with an easy setup for zero downtime deployment.

This means if your project becomes large and it takes some time between the build and the deployment, the server can be down for some time. If you must guarantee zero downtime, maybe consider using other solutions. Or maybe you found a way to do avoid downtime with coolify and docker - in that case, please share it with me (you can contact me on x or email).

Prerequisites

1. A VPS, a virtual machine or your own server.

You need to have some sort of server to run coolify in.

I recommend you rent a machine from Hetzner (it can be as low as $5/month). It’s simple and cheap. Just follow along the instructions and ensure you can ssh into it.

Alternatively, you can use Digital Ocean, any other cloud provider or even your own self-hosted server.

2. Coolify installed in the server

The Coolify instalation instructions are pretty straightforward. You can follow them here.

By the end of the installation, you should have a coolify instance running in your server and accessible at http://<ip>:8000.

The Django project

For our Django project, we will use:

  • whitenoise to serve static files;
  • python-decouple to load environment variables;
  • postgres as the database. We could just use sqlite, but I’ll show you how to use postgres to incorporate coolify’s database management;
  • gunicorn as the web server.

You can use any other settings, but this is what I recommend for a quick start.

1. Install the dependencies

In a new directory, let’s start by creating a virtual environment and install the dependencies:

mkdir djproject
cd djproject
python -m venv .venv
source .venv/bin/activate
pip install django whitenoise python-decouple gunicorn psycopg2-binary dj-database-url

You should also create a requirements.txt file:

pip freeze > requirements.txt

If you prefer to use poetry, you can also do it like this:

mkdir djproject
cd djproject
poetry init
poetry add django whitenoise python-decouple gunicorn psycopg2-binary dj-database-url

In the next steps, I’ll include instruction/code for both options (poetry and pip).

2. Create the database

Locally, you can just use sqlite or have a postgres database running - the code will handle both cases. If you’re using sqlite, you can ignore this step.

If you wish to use postgres locally, you can create a new database with the following commands (assuming you’re using the default postgres user):

sudo -u postgres psql
CREATE DATABASE djproject;

the resulting database url should look like this:

postgres://postgres:postgres@localhost:5432/djproject

Depending on your setup, you might need to change the user, password or the port number. save this variable to use it later.

3. Create the Django project

Let’s create a new Django project called djproject (inside the already created djproject directory):

django-admin startproject djproject .

You should have the following structure:

djproject/ # root directory
    manage.py
    djproject/ 
        __init__.py
        asgi.py
        settings.py
        urls.py
        wsgi.py
    requirements.txt # or pyproject.toml, pyproject.lock, etc

4. Configure the Django project

We can now load environment variables with python-decouple and edit the djproject/settings.py file. You can check the final settings file here.

4.1 - Imports

In the top of the djproject/settings.py file, add the following imports:

# djproject/settings.py
from decouple import config
import dj_database_url

4.2 - Load the environment variables

Let’s add some settings to load the environment variables:

# djproject/settings.py

SECRET_KEY = config('SECRET_KEY', default='django-insecure-siqce7e*%*426p$)2hxgk0u-b11vht*fiz_mj+6z4=tbnn7la3') 

DEBUG = config('DEBUG', default=True, cast=bool)

ALLOWED_HOSTS = config('ALLOWED_HOSTS', default='localhost,127.0.0.1', cast=lambda x: [i.strip() for i in x.split(',')])

CSRF_TRUSTED_ORIGINS = config('CSRF_TRUSTED_ORIGINS', default='http://localhost:8000,http://127.0.0.1:8000', cast=lambda x: [i.strip() for i in x.split(',')])
  • The SECRET_KEY is a random string used by Django to secure the sessions. locally, we can use the default value but we will generate a new one in production.
  • The DEBUG is set to True by default, which is what we want for production. In development, we will set it to False.
  • The ALLOWED_HOSTS is a list of hosts that the server will accept connections from. In development, we will set it to localhost,127.0.0.1. In production, we will set it to the domain name of the server.
  • The CSRF_TRUSTED_ORIGINS is a list of origins that the server will accept CSRF requests from. In development, we will set it to http://localhost:8000,http://127.0.0.1:8000. In production, we will set it to the domain name of the server (with the http protocol).

4.3 - Configure the database

Let’s add the following settings to configure the database:

# djproject/settings.py
DATABASES = {
    "default": dj_database_url.config(
        default=config("DATABASE_URL", f"sqlite:///{BASE_DIR}/db.sqlite3"),
        conn_max_age=600,
        ssl_require=False,
    ),
}

We are using the dj_database_url library to parse the DATABASE_URL environment variable and configure the database.

4.4 - Configure static files and whitenoise

Let’s also add STATIC_ROOT to the settings.py file, so we can collect static files in production:

STATIC_ROOT = BASE_DIR / 'staticfiles'

We will also add the whitenoise middleware to the settings.py file, so we can serve static files in production:

# djproject/settings.py

MIDDLEWARE = [
    ... # other middlewares
    'whitenoise.middleware.WhiteNoiseMiddleware',
]

4.5 - Configure template settings

Let’s also add the following settings to the settings.py file, so we can add templates in the templates directory:

# djproject/settings.py
TEMPLATES = [
    {
        ... # other settings  
        'DIRS': [BASE_DIR / 'templates'],
        ... # other settings
    },
]

4.6 - Create the .env file

To match the settings changes, we can now create a .env file locally with the following content:

# .env - local development environment variables


SECRET_KEY=django-insecure-siqce7e*%*426p$)2hxg7la3
DEBUG=True
ALLOWED_HOSTS=localhost,127.0.0.1
CSRF_TRUSTED_ORIGINS=http://localhost:8000,http://127.0.0.1:8000

# database url - you can leave it empty if you're using sqlite
DATABASE_URL=postgres://postgres:postgres@localhost:5432/djproject 

5. Create a home page

We will also create a home page in the templates/index.html file, so we can test if everything is working.

<h1>Hello, World!</h1>

In our demo project, we are using a more rich layout using mvp.css, but it’s just to look nice, it’s not really necessary to follow along this tutorial.

We will also create a djproject/views.py file for the home view:

# djproject/views.py
from django.shortcuts import render

def index(request):
    return render(request, 'index.html')

and add the following to the djproject/urls.py file:

# djproject/urls.py
from django.contrib import admin
from django.urls import path
from . import views

urlpatterns = [
    path('admin/', admin.site.urls),
    path('', views.index, name='index'),
]

6. Apply migrations and run the Django project

Let’s apply the migrations and run the Django project:

python manage.py migrate
python manage.py runserver

You should now be able to access the home page at http://localhost:8000 and the admin interface at http://localhost:8000/admin.

7. Add docker

To deploy the Django project with coolify, we need to add a docker file and a docker compose file. We will also add and entrypoint script to run the Django project.

7.1. Create the docker file

Let’s create a Dockerfile file with the following content:

# Dockerfile
FROM python:3.12-slim-bookworm

ENV PYTHONDONTWRITEBYTECODE=1
ENV PYTHONUNBUFFERED=1

RUN apt-get update && apt-get install -y \
    libpq-dev \
    gcc \
    && rm -rf /var/lib/apt/lists/*

RUN mkdir -p /code

WORKDIR /code

# If you want to use poetry
RUN pip install poetry
COPY pyproject.toml poetry.lock /code/
RUN poetry config virtualenvs.create false
RUN poetry install --only main --no-root --no-interaction

# If you want to use pip
COPY requirements.txt /code/
RUN pip install -r requirements.txt

COPY . /code

RUN python manage.py collectstatic --noinput

RUN chmod +x /code/entrypoint.sh

In this Dockerfile, we install the dependencies, collect static files and make the entrypoint script executable (you should choose between poetry or pip to install the dependencies).

7.2. Create the entrypoint script

Let’s create an entrypoint.sh file with the following content:

# entrypoint.sh
#!/bin/sh

until cd /code
do
    echo "Waiting for server volume..."
done


until python manage.py migrate
do
    echo "Waiting for db to be ready..."
    sleep 2
done


gunicorn --bind :8000 --workers 2 djproject.wsgi

This script does the following:

  • Waits for the server volume to be ready;
  • Waits for the database to be ready;
  • Runs the Django server with gunicorn.

7.3. Create the docker compose file

Let’s create a docker-compose.yml file with the following content:

# docker-compose.yml
version: '3'

services:
    server:
        container_name: djproject
        restart: unless-stopped
        build:
            context: .
            dockerfile: ./Dockerfile
        entrypoint: /code/entrypoint.sh
        volumes:
            - static_volume:/code/staticfiles
        expose:
            - 8000     
        environment:
            DEBUG: "False"
            SECRET_KEY: ${SECRET_KEY}
            ALLOWED_HOSTS: ${ALLOWED_HOSTS}
            CSRF_TRUSTED_ORIGINS: ${CSRF_TRUSTED_ORIGINS}
            DATABASE_URL: ${DATABASE_URL}
   
volumes:
    static_volume: {}

This docker compose file will build the docker image, run the entrypoint script and expose the server on port 8000. It also loads the relevant environment variables - we will configure coolify to pass the relevant environment variables to the server.

Deploying the Django project with coolify

Now that we have a Django project running locally, we can deploy it with coolify.

1. Push the code to github/gitlab/bitbucket/etc…

Let’s start by pushing the code to github (or any other git provider), so we can access it from coolify.

git init
git add .
git commit -m "Initial commit"
git branch -M main
git remote add origin https://github.com/your-username/your-repo.git
git push -u origin main

In this tutorial, we are using the django-coolify-tutorial repository as an example.

2. Create a new coolify project

Assuming your coolify instance is running at http://<ip>:8000, you can now create a new coolify project by going to http://<ip>:8000/projects and clicking on the New button in the top right corner. Give it a name and description and click on the Continue button.

You should now see the default production environment. If you click on it, you will see an + Add new resource button. Click on it.

3. Create a postgres database

We can use the postgresql resource to create a postgres database directly in coolify. For this tutorial, we will use the default PostgreSQL 16.

  • click on the PostgreSQL button;
  • choose the PostgreSQL 16 (default) version;
  • edit whatever settings you want and click on the Save button. In this tutorial, we will mostly use the default settings and just change the name of the resource;
  • click on the Start button to create the database;
  • After all this, copy the Postgres URL (internal) value and save it somewhere temporary - this will be used to connect the server to the database; It’s important to only copy this value in the last step, so we avoid copying outdated values. The URL will look something like this:
postgres://postgres:e1646815-c08e-4814-b66f-b1d0646fec36@87a33fe-8a8b-425a-9db5-5d7b913d763:5432/postgres

Your configuration should look something like this:

4. Connect the github repository

Going back to the production project page (in https://coolify.fmacedo.com/project/<PROJECT_ID>/production), click on the + New button.

Let’s add our code repository. You can choose between a private or public repository.

In this case, since we are using a public repository, we can just click on the Public repository button, paste the repository url and click on the Check repository button.

We can now select the branch we want to deploy and choose docker as the deployment method. Your choices should look somehing like this:

You can now press Continue and start configuring your project.

5. Configuring the project

We will go through the most relevant steps in the coolify Application page.

5.1. General tab

In the General tab, the one that should be active when you click on the Continue button of the previous step, you can:

  • Set the project name to django-coolify-tutorial-app or whatever you want;
  • Generate a new domain or set your own domain - if you generate a new one, coolify will create something like http://pw8css18cfctg8shhwjw3cww.5.74.172.122.sslip.io;
  • Copy this domain - this will be used in the environment variables;

5.2. Environment variables tab

In the Environment variables tab, you can set the environment variables to the relevant values. As you can see, coolify already set the variable names with default empty values based on the docker compose file.

  • Set ALLOWED_HOSTS variable to the domain you copied without the protocol (e.g. pw8css18cfctg8shhwjw3cww.5.74.172.122.sslip.io);
  • Set CSRF_TRUSTED_ORIGINS variable to the domain you copied with the protocol (e.g. http://pw8css18cfctg8shhwjw3cww.5.74.172.122.sslip.io);
  • Set the DATABASE_URL variable to the postgres url you copied (e.g. postgres://postgres:e1646815-c08e-4814-b66f-b1d0646fec36@87a33fe-8a8b-425a-9db5-5d7b913d763:5432/postgres);
  • Set the SECRET_KEY to some generated new value. To generate one using Django:
python -c 'from django.core.management.utils import get_random_secret_key; print(get_random_secret_key())'

Your environment variables should look something like this:

Remember to click on the Update button of each variable to save the changes.

5.3. Advanced tab

In the Advanced tab, we need to check the Connect To Predefined Network setting so that our postgres database is accessible from the server. If you don’t do this, you will have to manually set the postgres host to the server IP address and configure port mapping in the postgres resource settings.

Your advanced settings should look something like this:

6. Deploying the project

We are now ready to deploy the project. On the top right corner of the page, click on the Deploy button. After a few seconds, the project should be deployed and you should be able to access it at the domain you chose.

If you want to create a super user to test the admin interface, you can jump over to the terminal tab and run the django superuser command:

python manage.py createsuperuser

You should now be able to access the admin interface at something like http://pw8cgs88ckcog8swswgw0cww.5.74.172.122.sslip.io/admin.

Everytime you push a new commit to the main branch, coolify will automatically deploy the project. To configure this setup further, check out the coolify documentation.

Conclusion

In this tutorial, we learned how to deploy a Django project with coolify. We also learned how to use coolify’s database management to create a postgres database and connect it to the server.

If you have any questions, feel free to ask me on x or email.

Thanks for reading!

Published: Jan 21 2025
Updated: Jan 21 2025

Subscribe to my newsletter

I won't spam you, I promise. Just some occasional thoughts on software and building products.