Ansible role to deploy Django projects.
This is an Ansible role that automates the deployment of Django applications. It sets up the runtime environment, installs dependencies, configures the application, and optionally configures nginx, supervisor, PostgreSQL, Celery, Certbot (Let's Encrypt), and Monit.
Author: J. David Ibáñez
Minimum Ansible version: 2.1
- Ansible — configuration management and deployment orchestration
- Python / Django — the application being deployed
- nginx — reverse proxy and static file serving
- Supervisor — process control system
- uvicorn — ASGI server (production mode)
- Celery — distributed task queue worker (optional)
- PostgreSQL — database setup (optional)
- Certbot — TLS certificate provisioning via Let's Encrypt (optional)
- Monit — process monitoring with automatic restart (optional)
- Node.js / npm — frontend tooling (optional)
This project follows the standard Ansible role layout:
.
├── defaults/main.yml # Default configuration variables
├── handlers/main.yml # Handlers (reload supervisor, nginx, monit)
├── library/ # Custom Ansible modules
│ ├── filter_mods.py # Discover mods that provide a given file
│ ├── realpath.py # Return resolved absolute path
│ └── which.py # Locate binaries
├── meta/main.yml # Galaxy metadata
├── tasks/ # 20+ task files for deployment steps
├── templates/ # Jinja2 templates for generated configs
├── vars/main.yml # Internal variables
└── AGENTS.md # This file
tasks/main.yml imports the following task files in order:
- config.yml — Merge default dictionaries with user-provided role parameters.
- root.yml — Determine
django_root. If not provided, uses the current Git working tree root. - name.yml — Determine the instance name from
django_rootbasename if not provided. - pull.yml — Optionally perform a
git pull(controlled bydjango_pull). - domains.yml, python.yml, pythonpath.yml — Compute runtime facts.
- dirs.yml — Create the directory structure under
django_root. - pip.yml — Create a Python virtual environment and install requirements.
- postgres.yml — Create PostgreSQL user and database (only if
postgresis defined). - django.yml — Generate
settings_ansible.py,manage.py,urls_ansible.py, and run migrations. - supervisor.yml — Generate Supervisor configuration and start/stop scripts.
- node_modules.yml — Run
npm install/npm ciandnpm run buildifpackage.jsonexists (only ifdjango_with_nodeis true). - static.yml — Run
collectstatic(only ifnginxis defined). - nginx.yml — Generate nginx configuration and symlink into
/etc/nginx/sites/(only ifnginxis defined). - monit.yml — Generate Monit configuration and symlink into
/etc/monit.d/(only ifdjango_with_monitis true).
The role supports a modular architecture. Subdirectories ("mods") under the project root can contribute:
settings.py— included into the generatedsettings_ansible.pyurls.py— included into the generatedurls_ansible.pyrequirements.txt— included into the generatedrequirements.txt
The custom filter_mods module scans a list of mod directories and returns
only those that actually contain the requested file.
Inside django_root the role creates:
${django_root}/
├── var/ # Runtime data
│ ├── run/ # PID files and Unix sockets
│ ├── log/ # Application and supervisor logs
│ ├── static/ # Collected static files
│ ├── media/ # User-uploaded media
│ ├── sendfile/ # Internal sendfile directory
│ └── www/ # Web root (used by Certbot)
├── etc/ # Generated configuration files
└── venv${python_version_short}/ # Python virtualenv (optional)
Defaults are defined in defaults/main.yml. Key variables include:
django_repo/django_branch— Git repository and branch to deploydjango_root— Root directory of the deployed projectdjango_project— Django project package name (default:project)virtualenv— Whether to create a virtual environment (default:true)prefix— Alternative Python prefix pathpythonpath— ExtraPYTHONPATHto injectdomains— List of allowed hostnamesredirects— Map of source domains to destination domains for HTTP 301 redirectscache— Cache backend configurationstatic_url/media_url/sendfile_url— URL prefixes for static, media, and internal sendfiledjango_with_monit/django_with_node/django_with_sudo— Toggle optional features
Nested configuration dictionaries (merged with defaults in config.yml):
django_defaults/django→django_combinednginx_defaults/nginx→nginx_combinedsupervisor_defaults/supervisor→supervisor_combinedcelery_defaults/celery→celery_combineddjango_cert_defaults/django_cert→django_cert_combined
-
Production (
supervisor.daemon: true)- Runs
gunicornwithuvicornworkers as a[program:gunicorn]under Supervisor, binding directly to a Unix socket or TCP port. - Uses a Unix socket or TCP port behind nginx.
- Celery workers run as separate Supervisor programs.
- Logs go to files under
var/log/.
- Runs
-
Development (
supervisor.daemon: false)- Runs Django's
runserverdirectly. - nginx is typically not used.
- If
django_with_nodeis enabled, also startsnpm run dev. - Logs go to stdout/stderr.
- Runs Django's
All modules live in library/ and are written in Python using
ansible.module_utils.basic:
- filter_mods — Given a base path, a list of directories, a filename, and a destination fact name, sets a fact containing only the directories where the file exists.
- realpath — Returns the resolved absolute path of a given path, optionally relative to a base directory.
- which — Returns the path to a named executable, optionally constrained to a base directory (useful for virtualenv/bin lookups).
The role auto-generates the following files inside django_root:
manage.py— fromtemplates/manage.py.jinja${django_project}/settings_ansible.py— fromtemplates/settings_ansible.py.jinja${django_project}/urls_ansible.py— fromtemplates/urls_ansible.py.jinjaetc/requirements.txt— fromtemplates/requirements.txt.jinjaetc/gunicorn.conf.py— fromtemplates/gunicorn.conf.py.jinja(production only)etc/nginx.conf— fromtemplates/nginx.confetc/supervisor.conf— fromtemplates/supervisor.confetc/start.sh/etc/stop.sh— fromtemplates/start.sh/templates/stop.shetc/certbot.conf— fromtemplates/certbot.conf(HTTPS only)etc/monit.conf— fromtemplates/monit.conf(Monit only)
All generated files include a header stating they are auto-generated by Ansible.
reload supervisor— sendsHUPto the running supervisord processrestart gunicorn— restarts the gunicorn program via supervisorctl when its configuration file changesreload nginx— reloads thenginxsystem service (requiresdjango_with_sudo)restart monit— restarts themonitsystem service (requiresdjango_with_sudo)
- A random 50-character
SECRET_KEYis generated using Ansible'spasswordlookup and persisted invar/secret.txt. - HTTPS support includes optional HSTS headers.
- Uploaded files are created with permissions
0o644. - Internal sendfile URLs are isolated under
sendfile_url.
- There are no automated unit or integration tests in this repository.
- The
.gitignoreonly ignores.*.swpfiles. - Debug mode disables auth password validators, enables the console email
backend, and sets
INTERNAL_IPS. - The
TODO.txtfile tracks known improvements (e.g. nginx static compression, conditional Jinja2 delimiters, using native Ansible modules for git config and letsencrypt).
- Task files use YAML with explicit key/value formatting.
- Comments use
#and are written in English. - Task names use Title Case.
- Templates use Jinja2 with explicit block markers.
- Custom modules are written in Python 3 using
pathlib.Path.