Skip to content

Instantly share code, notes, and snippets.

@CodeSigils
Created November 26, 2025 18:37
Show Gist options
  • Select an option

  • Save CodeSigils/3d7646124b08708ca5e23a305a2273a3 to your computer and use it in GitHub Desktop.

Select an option

Save CodeSigils/3d7646124b08708ca5e23a305a2273a3 to your computer and use it in GitHub Desktop.
A set of Django framework pre-deployment preparation mandatory steps based on best practices for security.

Django projects deployment preparation guide

A set of Django framework pre-deployment preparation mandatory steps based on best practices for security.

See: https://docs.djangoproject.com/en/5.2/howto/deployment/

It can be used as a TODO list template for any relevant project.

Note: This guide uses an older version of environs for dependency conflict resolution demonstration purposes.

Steps list

  • Install Gunicorn as a WSGI server
  • Install Psycopg adapter to connect with a PostgreSQL database
  • Install environs for environment variables management and conflict resolution demo
  • Update DATABASES in settings file
  • Install WhiteNoise for static files
  • Create a requirement.txt file and freeze dependencies
  • Add a .dockerignore file with correct paths
  • Create a .env file
  • Update .gitignore file
  • Update ALLOWED_HOSTS, CSRF_TRUSTED_ORIGINS, DEBUG, SECRET_KEY

Install dependencies

python -m pip install gunicorn==20.1.0
python -m pip install "psycopg[binary]"==3.1.8
python -m pip install "environs[django]"==9.5.0
python -m pip install whitenoise==6.4.0

Environment settings using the environs package

The environs package is a Python library for parsing environment variables. It allows you to store configuration separate from your code, as per "The Twelve-Factor App" methodology.

There is also the alternative Django specific django-environ package.

# django_project/settings.py
from pathlib import Path
from environs import Env # -> Import here

env = Env() # -> Register here
env.read_env() # -> Activate here

Downgrading dependencies in a Python project

A chance of package versions conflict occurrence in Python in high. In case of older environs version there is a problem with the marshmalow dependency.

When trying running the server there is a chance of the following error reported:

# ...
 File ".../django_project/settings.py", line 14, in <module>
    from environs import Env
  File ".../.venv/lib/python3.13/site-packages/environs/__init__.py", line 58, in <module>
    _SUPPORTS_LOAD_DEFAULT = ma.__version_info__ >= (3, 13)
                             ^^^^^^^^^^^^^^^^^^^
AttributeError: module 'marshmallow' has no attribute '__version_info__'

Welcome to Python version dependency hell.

  • Solution:

    First find the version of the reported package and the dependency located in the last line is the output of the command:

    pip show marshmallow
    
    # Name: marshmallow
    # Version: 4.1.0
    # Summary: A lightweight library for converting complex datatypes
    # to and from native Python datatypes.
    # Home-page: https://github.com/marshmallow-code/marshmallow
    # Author: Steven Loria
    # Author-email: [email protected]
    # License: MIT
    # Location: /project-path/app-path/.venv/lib/python3.13/site-packages
    # Requires: packaging
    # Required-by: environs
    1. Open your requirements.txt file and change the marshmallow line from:
    from
    marshmallow==4.1.0
    to
    marshmallow==3.19.0
    1. Save the requirements.txt file.

    2. Uninstall the current marshmallow version:

    python -m pip uninstall marshmallow
    1. Install the downgraded dependency:
    python -m pip install -r requirements.txt
    1. Run the server again:
    python manage.py runserver
    1. If you want to automate reinstalling with freezing versions in the future, you can use:
    pip freeze > requirements.txt

Databases settings

Make sure to use PostgreSQL in production and fallback to Sqlite driver in local development.

# django_project/settings.py
DATABASES = {
  "default": env.dj_db_url("DATABASE_URL", default="sqlite:///db.sqlite3"),
}

Static files settings

Add WhiteNoise above the build-in staticfiles app, middleware and default storage engine and after the default Security middleware.

YT: Intro to WhiteNoise

# django_project/settings.py
INSTALLED_APPS = {
  # ...
  "whitenoise.runserver_nostatic", # -> Add here
  "django.contrib.staticfiles",
  # ... 
}

MIDDLEWARE = [
  # ...
  "whitenoise.middleware.WhiteNoiseMiddleware",
  # ...
]


STATIC_URL = "static/"
STATICFILES_DIRS = [BASE_DIR / "static"]
STATIC_ROOT = BASE_DIR / "staticfiles"
STORAGES = {
    "default": {
        "BACKEND": "django.core.files.storage.FileSystemStorage",
    },
    "staticfiles": {
        # "BACKEND": "django.contrib.staticfiles.storage.StaticFilesStorage", # -> Django's default Static Files management
        ## Replace the above with the following:
        "BACKEND": "whitenoise.storage.CompressedManifestStaticFilesStorage",
    },
}

Collect the static files and overwrite the older ones. Then restart the server:

python manage.py collectstatic

# You have requested to collect static files at the destination
# location as specified in your settings:
#
#    # /path-to-project/path-to-app/staticfiles
#
# This will overwrite existing files!
# Are you sure you want to do this?
#
# Type 'yes' to continue, or 'no' to cancel: 
yes
#
# 127 static files copied to '/path-to-project/path-to-app/staticfiles', 1 unmodified, 384 post-processed.

python manage.py runserver

Freeze requirements

python -m pip freeze > requirements.txt

Ignore files

The .env file is used to store sensitive variables and secrets so we must make sure is excluded from deployment.

  • The .dockerignore file:
.venv/
__pycache__/
*.sqlite3
.git
.env
  • The .gitignore file:
.venv/
__pycache__/
*.sqlite3
.env

Create the .env file

Create the file to hold our sensitive information and custom variable values. This file must be never uploaded in any Version Control system, or deployment platform.

There should be NO spaces included in this file.

DEBUG=True
SECRET_KEY=

Disable DEBUG mode in production

The DEBUG setting is a boolean and must be never set to True in a production environment. This will disable the local server among other things and hide all sensitive info and diagnostics that are exposed in localhost:8000/debug by default.

Manage the variable value through the .env file so that returns False in production and True in local development:

# django_project/settings.py
# DEBUG = True # -> Change this to the following:
DEBUG = env.bool("DEBUG", default=False)

Manage the secrets module

IMPORTANT: In case there was a previous commit in github or any other VCS online service, the SECRET_KEY MUST be regenerated, as its value can be tracked through VCS history. After the setup run again the local server to ensure everything is working with the new key.

  • Regenerate a new key for the project and copy the new one from terminal:
python -c "import secrets; print(secrets.token_urlsafe())"
# new-generated-key
  • Paste the new key in case of previous commit, to the .env file without spaces:
DEBUG=True
SECRET_KEY=new-generated-key
  • Replace the value in settings:
# django_project/settings.py
# SECRET_KEY = "django-insecure-old-key" # -> Replace with the following:
SECRET_KEY = env.str("SECRET_KEY")

Server security

Except DEBUG and SECRET_KEY settings, there are two more main sensitive variables that must be secured for production: ALLOWED_HOSTS and CSRF_TRUSTED_ORIGINS:

# django_project/settings.py
# ALLOWED_HOSTS = ["*"] # -> Allows access to any host. Change to:
ALLOWED_HOSTS = [ ".my-platform.dev", "localhost", "127.0.0.1" ]
CSRF_TRUSTED_ORIGINS = ["https://*.my-platform.dev"] # -> Add your platform here

Finally we can proceed with the actual deployment.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment