Skip to main content

djevops: Self-host Django easily

djevops is a command-line tool for deploying your Django web app to a Linux VPS. It runs and manages all necessary components (database, Redis, etc.) on your server.

Unlike other tools, djevops does not use Docker. This makes it possible to "push to prod" in seconds. Compared to Ansible, djevops' specialization on Django lets you write significantly less code. The flip side is that djevops is less general.

To get started with djevops, all you need is SSH root access to a Linux VPS running Ubuntu or Debian. Install djevops on your local machine with pip install djevops. Then, execute djevops init in your Django app's Git repository. You get a config file that looks similar to the following:

server: 1.2.3.4

git:
  repo: githubuser/mydjangoapp
  branch: main

services:
  web:
    type: django
    env:
      clear:
        ALLOWED_HOSTS: your.website.com
      secret:
        - DJANGO_SECRET_KEY

db:
  type: sqlite

mail:
  host: smtp.gmail.com
  user: SMTP_USER
  password: SMTP_PASSWORD

Secrets such as DJANGO_SECRET_KEY or SMTP_PASSWORD can be specified as constants in file deploy/secrets.py.

Most config values are optional. Fill in the ones you want and run djevops deploy. djevops then clones your Git repo on the server and starts all services. As you work on your Django app and push new commits to Git, simply run djevops deploy again to apply them to your server.

Features

Automatic SSL certificates

djevops generates and automatically renews SSL certificates for any domains you specify in Django setting ALLOWED_HOSTS. The domains need to be tied to your server's IP address.

Error emails

When you fill in the mail section in the config file, then djevops sets up a local Postfix relay. This can be used to send emails from your server, for example via Django's send_mail function. If you additionally want to receive emails when errors occur on your server, set ADMINS and SERVER_EMAIL in Django's settings.py file, as follows:

ADMINS = [('Your Name', 'your@email.com')]
SERVER_EMAIL = 'sender@your.website.com'

In the above example, your@email.com receives an email from sender@your.website.com when an error occurs on the server. In order for this to work, the credentials configured in the mail section must be allowed to send emails as SERVER_EMAIL.

Error emails are only sent when Django setting DEBUG is False.

Automatic database backups

You can set up automatic database backups by adding a backup element to the db section in the djevops config file. For example:

db:
  type: sqlite
  backup:
    type: s3
    bucket: mybackup
    access-key-id: S3_BACKUP_ACCESS_KEY
    secret-access-key: S3_BACKUP_SECRET_KEY
    path: db
    region: us-east-1

Backups are created continuously while your server is running. If you ever re-install your server, then the latest backup is automatically restored.

For database type sqlite, djevops uses Litestream for backups. Litestream can store backups in S3, Azure Blob Storage and many others. The keys you add to the backup element above get copied into a replica element in Litestream's config. For more information about the available options, please see Litestream's documentation.

Djevops also supports database type postgres. For more information about this, please see below.

PostgreSQL

Instead of SQLite, you can use PostgreSQL by setting the database type to postgres:

db:
  type: postgres

You then configure the connection yourself in your settings.py:

import os

DATABASES['default'] = {
    'ENGINE': 'django.db.backends.postgresql',
    'NAME': 'myapp',
    'USER': 'myapp',
    'PASSWORD': os.environ['DB_PASSWORD'],
    'HOST': 'localhost'
}

djevops reads these settings and installs PostgreSQL on the server, creating the database and user with the password you specified. Keep the password out of Git by storing it in deploy/secrets.py. For example:

DB_PASSWORD = "<some strong password>"

Then reference it as a secret in the Django service's environment:

services:
  web:
    type: django
    env:
      secret:
        - DB_PASSWORD

You also need to add psycopg[binary] to your pyproject.toml or requirements.txt file.

As with SQLite, you can add a backup element to enable automatic backups. By default, PostgreSQL backups are taken once per day. You can customize this by setting sync-interval in the backup element, for example to 1h for hourly backups.

Background tasks via Celery and Redis

If your Django app uses the celery Python package, then you can add a Celery worker by adding the following item to the djevops config:

services:
  web:
    # as before
  celery:
    type: celery
    env:
      inherit: web

To install Redis on the server (which many Django apps use as Celery's backend), add an empty top-level redis block:

redis:

This setup lets you run Python functions asynchronously and on a schedule such as "every five hours". The service of type celery also runs the necessary beat scheduler.

Easy access to log files

djevops writes the log file for each service to /var/log/<service>.log. To read it, simply SSH into the server and do less, tail -f, etc. To prevent log files from filling up your server's disk space, djevops also rotates and compresses log files.

Secret handling

Very often, you have secrets that you need on the server but should not commit to Git. djevops lets you specify such values in the file deploy/secrets.py, and refer to them from your config file. The way this works is that secrets.py gets executed on your local machine, and the produced values then get uploaded as constants to the server. This gives you a lot of flexibility. You can hardcode values in secrets.py and not commit that file to Git. Or you can for example make secrets.py read from environment variables that are available when you do djevops deploy:

import os
MY_SECRET = os.environ['MY_SECRET']

You can also invoke password managers in secrets.py, etc.

Secure defaults

djevops uses secure defaults whenever possible. For example, each service runs as a separate user. This means that environment variables cannot leak from one service to another. djevops also makes sure that no unintended ports are open, such as for example port 25 when using Postfix for sending emails.

Automatic OS updates

djevops sets up automatic OS updates to keep your server up-to-date and secure. This does not apply major version upgrades, which could introduce potentially breaking changes.

Easy access to `manage.py shell` on the server

Just type djevops shell to be dropped into a remote Django shell on your server. This uses the environment variables and user of the first service of type django in deploy/djevops.yml.

Custom services

We saw several kinds of services above. Another one is type: command. For example:

services:
  myservice:
    type: command
    command: ./my-service.sh

This runs my-service.sh from your Git repository whenever your server is running. The command must not exit; if it does, djevops restarts it.

Hooks

Sometimes your app needs things on the server that djevops does not know about, for example an apt package, a tool built from source, or an SSH key for one of your services. For such cases, you can create executable hooks next to deploy/djevops.yml. djevops runs them on the server at specific points during djevops deploy. The available hooks are:

  • deploy/pre-install runs after your Git repository has been cloned into /srv/app but before your app's dependencies are installed.
  • deploy/post-install runs after everything else has been set up and just before your services are started.

For example:

#!/bin/bash
set -euxo pipefail
apt-get install -yq faketime

Don't forget to chmod +x the hook. It can be any executable: a shell script as above, a Python script, a binary, etc. Hooks run as root in an empty temporary directory. The values from your deploy/secrets.py are available to them as environment variables.

To keep deploys fast, djevops only runs a hook when its contents or your secrets have changed since its last successful run. Because secrets sometimes do change, your hooks must be able to re-run over the results of a previous execution.

Development

Install the test dependencies from pyproject.toml. The easiest way I know for doing this is with uv:

uv venv
source .venv/bin/activate
uv sync --no-install-project --extra test

Then, you can do python -m unittest to run tests. This requires several API keys specified in environment variables.

Metadata

Release files for djevops 0.2.12

For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.

Source distribution (sdist)

Source distribution for djevops 0.2.12
File Size Uploaded Provenance Actions
djevops-0.2.12.tar.gz 69.0 kB Without provenance Details for djevops-0.2.12.tar.gz

Built distribution (wheel)

Table of built distributions (wheels) for djevops 0.2.12
File Interpreter ABI Platform Actions
djevops-0.2.12-py3-none-any.whl Python 3 none any Details for djevops-0.2.12-py3-none-any.whl

Total release size: 131.8 kB

Release files / djevops-0.2.12.tar.gz

Download URL djevops-0.2.12.tar.gz
Size 69.0 kB
Tags Source
SHA-256 checksum
How to use checksums
4d4c75888f186e2cf2a393a891dea9c8aa1ab74d2c4de2554a78236d3a09eed6
BLAKE2b-256 checksum
How to use checksums
b710c099ce1463a02e2c6c1b9e7f127d05c0d1f2c0003cc1338cb034b2a8e30f
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
No
Uploaded via uv/0.8.18

Release files / djevops-0.2.12-py3-none-any.whl

Download URL djevops-0.2.12-py3-none-any.whl
Size 62.8 kB
Tags Python 3
SHA-256 checksum
How to use checksums
7279cbda1f76e5d1b2d2ee86831c32d29ea9abda4ee7e20c0b1a6253cafe1be9
BLAKE2b-256 checksum
How to use checksums
e72e0f019f1cb4d04b6c8a19722a77f28f6363d0add3318f8121ab740dc96d0c
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
No
Uploaded via uv/0.8.18

Security for djevops 0.2.12

Provenance

Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.

No provenance found

None of the files in this release were uploaded with provenance metadata.

A note on package security

PyPI is a repository for distributing packages, not a service for auditing them. While third-party security researchers and our staff do their best to detect and remove malicious packages, PyPI cannot guarantee the quality, security or suitability of any given package.

To learn more about protecting yourself from malicious actors, visit our package security documentation.

Anthropic, PBC Visionary sponsor Bloomberg Visionary sponsor Hudson River Trading Visionary sponsor Meta Visionary sponsor NVIDIA Visionary sponsor Microsoft Sustainability sponsor Depot Continuous Integration AWS Cloud computing and Security Sponsor Datadog Monitoring Fastly CDN Google Download Analytics Sentry Error logging StatusPage Status page