Skip to content

Instantly share code, notes, and snippets.

@yeiichi
Created November 9, 2025 05:37
Show Gist options
  • Select an option

  • Save yeiichi/54e57c07553cc9a9324ba3fcfc733e24 to your computer and use it in GitHub Desktop.

Select an option

Save yeiichi/54e57c07553cc9a9324ba3fcfc733e24 to your computer and use it in GitHub Desktop.
Automates the creation of a reusable Django + Docker Compose template kit
#!/usr/bin/env bash
# ==============================================================================
# make_django_docker_template_kit.sh
# ------------------------------------------------------------------------------
# PURPOSE:
# Automates the creation of a reusable **Django + Docker Compose** template kit.
# Generates all directories and placeholder files needed to start a new
# containerized Django project, including Docker, Compose, Makefile, and docs.
#
# USAGE:
# bash make_django_docker_template_kit.sh [TARGET_DIR] [PROJECT_SLUG]
#
# TARGET_DIR Directory to create (default: django_project_dir)
# PROJECT_SLUG Django project package name (default: project_name)
#
# EXAMPLES:
# bash make_django_docker_template_kit.sh
# bash make_django_docker_template_kit.sh ./django_project_dir project_name
#
# OUTPUT:
# The script creates a full template layout including:
# - Dockerfile, docker-compose.yml, .env.example
# - Django project skeleton (settings, urls, wsgi, asgi)
# - Postgres init scripts, Makefile helper, and docs/
#
# NOTES:
# • Safe to rerun — overwrites files but won’t delete existing directories.
# • Intended for local development setup, not production deployment.
# • Review generated Makefile and Docker configuration before first use.
#
# AUTHOR: Eiichi YAMAMOTO
# LICENSE: MIT License
# VERSION: 2025.11
# URL: https://yeiichi.com
# ==============================================================================
set -euo pipefail
# Display help information
if [ "${1:-}" = "--help" ] || [ "${1:-}" = "-h" ]; then
cat <<'HELP'
Usage: ./make_django_docker_template_kit.sh [TARGET_DIR] [PROJECT_SLUG]
Creates a complete Django + Docker Compose template kit with all necessary
configuration files, directory structure, and development helpers.
Arguments:
TARGET_DIR Directory to create the template in
(default: django-docker-template-kit)
PROJECT_SLUG Django project package name
(default: project_name)
Options:
--help, -h Show this help message and exit
Examples:
# Create template with default settings
./make_django_docker_template_kit.sh
# Create template in custom directory
./make_django_docker_template_kit.sh my_project
# Create template with custom directory and project name
./make_django_docker_template_kit.sh my_project my_app
Generated Structure:
TARGET_DIR/
├── README.md Project documentation
├── .env.example Environment variables template
├── .gitignore Git ignore rules
├── .dockerignore Docker ignore rules
├── docker-compose.yaml Docker Compose configuration
├── Dockerfile Django container definition
├── Makefile.django-docker-helper Development command shortcuts
├── manage.py Django management script
├── apps/ Django apps directory
│ └── example_app/
├── PROJECT_SLUG/ Django project package
│ ├── __init__.py
│ ├── settings.py
│ ├── urls.py
│ ├── asgi.py
│ └── wsgi.py
├── docker/ Docker-related files
│ ├── django/
│ │ └── entrypoint.sh
│ └── postgres/
│ └── init/
│ ├── 01_extensions.sql
│ └── 02_dev_schema.sql
└── docs/ Documentation
├── template_overview.md
├── compose_reference.md
└── makefile_reference.md
After Creation:
1. cd TARGET_DIR
2. cp .env.example .env
3. docker compose up -d (or: make -f Makefile.django-docker-helper up)
4. docker compose exec web python manage.py migrate
5. docker compose exec web python manage.py createsuperuser
For more information, visit: https://yeiichi.com
HELP
exit 0
fi
TARGET_DIR="${1:-django-docker-template-kit}"
PROJECT_SLUG="${2:-project_name}"
echo "Creating template kit at: ${TARGET_DIR} (project slug: ${PROJECT_SLUG})"
mkdir -p "${TARGET_DIR}"/{apps/example_app,"${PROJECT_SLUG}",docker/postgres/init,docker/django,docs}
# README.md
cat > "${TARGET_DIR}/README.md" <<'EOF'
# 🧩 Django + Docker Template Kit
A lightweight, production-ready **development environment** for Django projects using **Docker Compose** and **PostgreSQL**.
Includes a Makefile helper utility for one-line developer commands.
## Quickstart
cp .env.example .env
docker compose up -d
# or: make up
See `docs/` for detailed references.
EOF
# .env.example
cat > "${TARGET_DIR}/.env.example" <<'EOF'
DJANGO_DEBUG=1
SECRET_KEY=change-me-in-dev
POSTGRES_DB=appdb
POSTGRES_USER=appuser
POSTGRES_PASSWORD=appsecret
DATABASE_URL=postgres://appuser:appsecret@db:5432/appdb
EOF
# .gitignore
cat > "${TARGET_DIR}/.gitignore" <<'EOF'
# Python
__pycache__/
*.py[cod]
*$py.class
*.so
.Python
build/
develop-eggs/
dist/
downloads/
eggs/
.eggs/
lib/
lib64/
parts/
sdist/
var/
wheels/
*.egg-info/
.installed.cfg
*.egg
MANIFEST
# Virtual environments
venv/
env/
ENV/
env.bak/
venv.bak/
# Django
*.log
local_settings.py
db.sqlite3
db.sqlite3-journal
/static/
/media/
# Environment variables
.env
.env.local
# IDE
.vscode/
.idea/
*.swp
*.swo
*~
.DS_Store
# Docker
docker-compose.override.yml
# Testing
.pytest_cache/
.coverage
htmlcov/
.tox/
# Secrets
*.pem
*.key
secrets/
EOF
# .dockerignore
cat > "${TARGET_DIR}/.dockerignore" <<'EOF'
# Version control
.git
.gitignore
.gitattributes
# Python
__pycache__
*.pyc
*.pyo
*.pyd
.Python
*.so
*.egg
*.egg-info
dist
build
# Virtual environments
venv
env
ENV
# Django
*.log
db.sqlite3
db.sqlite3-journal
/static/
/media/
# Environment
.env
.env.*
!.env.example
# IDE
.vscode
.idea
*.swp
*.swo
*~
.DS_Store
# Docker
Dockerfile*
docker-compose*.yml
.dockerignore
# Documentation
README.md
docs/
*.md
# Testing
.pytest_cache
.coverage
htmlcov
.tox
# CI/CD
.github
.gitlab-ci.yml
.travis.yml
# Misc
*.md
LICENSE
Makefile*
EOF
# Makefile stub
cat > "${TARGET_DIR}/Makefile.django-docker-helper" <<'EOF'
# language: Makefile
DC ?= docker compose
WEB ?= web
DB ?= db
.DEFAULT_GOAL := help
help:
@echo "Targets: up, down, reset, migrate, superuser, logs"
up: ; $(DC) up -d
down: ; $(DC) down
reset: ; $(DC) down -v --remove-orphans
migrate: ; $(DC) exec $(WEB) python manage.py migrate
superuser: ; $(DC) exec $(WEB) python manage.py createsuperuser
logs: ; $(DC) logs -f --tail=200
EOF
# docker-compose.yaml
cat > "${TARGET_DIR}/docker-compose.yaml" <<EOF
services:
web:
build: .
container_name: \${COMPOSE_PROJECT_NAME:-django_app}_web
entrypoint: ["/app/docker/django/entrypoint.sh"]
command: python manage.py runserver 0.0.0.0:8000
env_file: .env
environment:
DATABASE_URL: \${DATABASE_URL:-postgres://appuser:appsecret@db:5432/appdb}
DJANGO_DEBUG: \${DJANGO_DEBUG:-1}
volumes:
- .:/app:cached
- media:/app/media
- static:/app/static
ports:
- "8000:8000"
depends_on:
db:
condition: service_healthy
db:
image: postgres:16-alpine
container_name: \${COMPOSE_PROJECT_NAME:-django_app}_db
environment:
POSTGRES_DB: \${POSTGRES_DB:-appdb}
POSTGRES_USER: \${POSTGRES_USER:-appuser}
POSTGRES_PASSWORD: \${POSTGRES_PASSWORD:-appsecret}
POSTGRES_INITDB_ARGS: "--encoding=UTF8 --lc-collate=C --lc-ctype=C"
healthcheck:
test: ["CMD-SHELL", "pg_isready -U \${POSTGRES_USER:-appuser} -d \${POSTGRES_DB:-appdb}"]
interval: 5s
timeout: 3s
retries: 20
ports:
- "5432:5432"
volumes:
- pgdata:/var/lib/postgresql/data
- ./docker/postgres/init:/docker-entrypoint-initdb.d:ro
volumes:
pgdata:
media:
static:
EOF
# Dockerfile
cat > "${TARGET_DIR}/Dockerfile" <<'EOF'
FROM python:3.12-slim
WORKDIR /app
ENV PYTHONDONTWRITEBYTECODE=1 PYTHONUNBUFFERED=1 PIP_NO_CACHE_DIR=1
RUN apt-get update && apt-get install -y --no-install-recommends build-essential curl libpq-dev gettext && rm -rf /var/lib/apt/lists/*
COPY . /app
RUN pip install --upgrade pip && pip install django psycopg2-binary dj-database-url
EXPOSE 8000
CMD ["python", "manage.py", "runserver", "0.0.0.0:8000"]
EOF
# Example app
cat > "${TARGET_DIR}/apps/example_app/__init__.py" <<'EOF'
# Example Django app placeholder
EOF
# manage.py
cat > "${TARGET_DIR}/manage.py" <<EOF
#!/usr/bin/env python
import os, sys
def main():
os.environ.setdefault('DJANGO_SETTINGS_MODULE', '${PROJECT_SLUG}.settings')
from django.core.management import execute_from_command_line
execute_from_command_line(sys.argv)
if __name__ == '__main__':
main()
EOF
# Django project files
cat > "${TARGET_DIR}/${PROJECT_SLUG}/__init__.py" <<'EOF'
# Django project package
EOF
cat > "${TARGET_DIR}/${PROJECT_SLUG}/settings.py" <<EOF
import os
from pathlib import Path
BASE_DIR = Path(__file__).resolve().parent.parent
SECRET_KEY = os.getenv("SECRET_KEY", "dev-secret-key")
DEBUG = os.getenv("DJANGO_DEBUG", "1") == "1"
ALLOWED_HOSTS = ["*"]
INSTALLED_APPS = ["django.contrib.admin","django.contrib.auth","django.contrib.contenttypes","django.contrib.sessions","django.contrib.messages","django.contrib.staticfiles"]
MIDDLEWARE = ["django.middleware.security.SecurityMiddleware","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"]
ROOT_URLCONF = "${PROJECT_SLUG}.urls"
TEMPLATES = [{"BACKEND":"django.template.backends.django.DjangoTemplates","DIRS":[BASE_DIR / "templates"],"APP_DIRS":True,"OPTIONS":{"context_processors":["django.template.context_processors.debug","django.template.context_processors.request","django.contrib.auth.context_processors.auth","django.contrib.messages.context_processors.messages"]}}]
WSGI_APPLICATION = "${PROJECT_SLUG}.wsgi.application"
ASGI_APPLICATION = "${PROJECT_SLUG}.asgi.application"
# Database configuration
try:
import dj_database_url
DATABASES = {
"default": dj_database_url.config(
default="sqlite:///" + str(BASE_DIR / "db.sqlite3"),
conn_max_age=600
)
}
except ImportError:
DATABASES = {"default": {"ENGINE": "django.db.backends.sqlite3", "NAME": BASE_DIR / "db.sqlite3"}}
STATIC_URL = "static/"
STATIC_ROOT = BASE_DIR / "static"
MEDIA_URL = "media/"
MEDIA_ROOT = BASE_DIR / "media"
DEFAULT_AUTO_FIELD = "django.db.models.BigAutoField"
EOF
cat > "${TARGET_DIR}/${PROJECT_SLUG}/urls.py" <<EOF
from django.contrib import admin
from django.urls import path
urlpatterns = [path("admin/", admin.site.urls)]
EOF
cat > "${TARGET_DIR}/${PROJECT_SLUG}/asgi.py" <<EOF
import os
from django.core.asgi import get_asgi_application
os.environ.setdefault("DJANGO_SETTINGS_MODULE", "${PROJECT_SLUG}.settings")
application = get_asgi_application()
EOF
cat > "${TARGET_DIR}/${PROJECT_SLUG}/wsgi.py" <<EOF
import os
from django.core.wsgi import get_wsgi_application
os.environ.setdefault("DJANGO_SETTINGS_MODULE", "${PROJECT_SLUG}.settings")
application = get_wsgi_application()
EOF
# Postgres init SQL
cat > "${TARGET_DIR}/docker/postgres/init/01_extensions.sql" <<'EOF'
CREATE EXTENSION IF NOT EXISTS "uuid-ossp";
CREATE EXTENSION IF NOT EXISTS pgcrypto;
CREATE EXTENSION IF NOT EXISTS citext;
EOF
cat > "${TARGET_DIR}/docker/postgres/init/02_dev_schema.sql" <<'EOF'
DO $$
BEGIN
IF NOT EXISTS (SELECT 1 FROM pg_namespace WHERE nspname = 'dev') THEN
EXECUTE 'CREATE SCHEMA dev AUTHORIZATION ' || quote_ident(current_user);
END IF;
END$$;
ALTER DATABASE appdb SET search_path = dev, public;
GRANT ALL ON SCHEMA dev TO PUBLIC;
EOF
# Entrypoint
cat > "${TARGET_DIR}/docker/django/entrypoint.sh" <<'EOF'
#!/usr/bin/env bash
set -e
if [ "${RUN_MIGRATIONS:-1}" = "1" ]; then
python manage.py migrate --noinput || true
fi
exec "$@"
EOF
chmod +x "${TARGET_DIR}/docker/django/entrypoint.sh"
# Docs
cat > "${TARGET_DIR}/docs/template_overview.md" <<'EOF'
# Template Overview
Dockerized Django + PostgreSQL 16 environment with health checks and init SQL.
EOF
cat > "${TARGET_DIR}/docs/compose_reference.md" <<'EOF'
# Compose Reference
- web: Django app, port 8000
- db: PostgreSQL 16 with init scripts
EOF
cat > "${TARGET_DIR}/docs/makefile_reference.md" <<'EOF'
# Makefile Reference
Common targets: up, down, reset, migrate, superuser, logs.
EOF
echo "✅ Template created at: ${TARGET_DIR}"
echo "Next steps:"
echo " cd ${TARGET_DIR}"
echo " cp .env.example .env"
echo " docker compose up -d # or: make up"
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment