A comprehensive Django project template with CI/CD pipeline for deploying to Kubernetes. This project demonstrates modern Python development practices using Nix, devenv, Docker, Helm, Kubernetes, and GitHub Actions.
This repository is primarily a showcase of modern DevOps tooling and deployment practices rather than Django application development. The Django application itself is intentionally simple (basic user authentication and blog posts) to keep the focus on the infrastructure and deployment pipeline.
DevOps & Infrastructure:
- 🏗️ Reproducible Development: Using Nix and devenv for consistent development environments
- 🐳 Containerization: Optimized Docker build with uv for fast dependency management
- ☸️ Kubernetes Deployment: Helm charts with environment-specific configurations
- 🔐 Secrets Management: SOPS for encrypted secrets in version control
- 🚀 CI/CD Pipeline: GitHub Actions with reusable workflows
- 🤖 AI Development Tools: Pre-configured AI agents (Claude Code, Amp, Gemini, Codex, etc.) via node tooling
Django Best Practices (minimal but important):
- 🔧 Configuration Management: django-environ for environment-based settings
- 📁 Stateless Architecture: S3/object storage for static and media files
- 🗄️ Database: PostgreSQL with proper migrations
- 🔒 Custom User Model: Following Django's recommended approach
- 🧪 Testing Structure: Organized test files per Django app
- Complex Django application architecture
- Advanced Django features
- Advanced database relationships or queries
- Django REST framework or API development
If you're looking to learn Django application development, this project provides a solid foundation but focuses more on getting that application reliably deployed and managed in production.
- Prerequisites
- Local Development Setup
- Running the Application
- AI Development Tools
- Development Commands
- Testing
- Project Structure
- CI/CD Pipeline
- Deployment
- Secrets Management
- Using as a Template
- GitHub Secrets Configuration
- Troubleshooting
Before you begin, you'll need to install several tools to work with this project effectively.
First, you need to install Nix, a powerful package manager that ensures reproducible development environments.
For macOS users (and others who want the best experience), I strongly recommend using the Determinate Systems Nix installer:
curl --proto '=https' --tlsv1.2 -sSf -L https://install.determinate.systems/nix | sh -s -- install --determinateThis installer provides better defaults and improved user experience compared to the official installer. For more details, visit: https://docs.determinate.systems/determinate-nix/
If you want, you can use the official installer (but you'll probably need to manually enable nix flakes):
sh <(curl -L https://nixos.org/nix/install) --daemonAfter installation, restart your terminal or source your shell profile.
devenv is a tool that creates reproducible development environments using Nix. Install it with:
nix profile install nixpkgs#devenvdirenv automatically loads environment variables when you enter a directory. This integrates perfectly with devenv.
On macOS:
brew install direnvOr via nix:
nix profile install nixpkgs#direnvShell Integration:
Add the following to your shell configuration file (~/.bashrc, ~/.zshrc, etc.):
eval "$(direnv hook bash)" # for bash
eval "$(direnv hook zsh)" # for zshRestart your terminal after adding the hook.
-
Clone the repository:
git clone https://github.com/denibertovic/hellok8s-django.git cd hellok8s-django -
Allow direnv to manage the environment:
direnv allow
This command tells direnv that you trust this directory to automatically load environment variables. You'll be prompted to do this the first time you enter the directory. This is a security feature and you should always inspect a repo that you've cloned before running this command.
-
Enter the development shell: Once direnv is allowed, it will automatically activate the devenv shell whenever you're in the project directory. You'll see output that indicating you're in the development environment with all necessary tools available.
-
Configure environment variables: Copy the example environment file and fill in the required values:
cp env.example .env
Open
.envin your editor and fill in the necessary configuration values. The example file contains all the environment variables needed for local development with sensible defaults. You'll need to set values for:- Database connection settings (default should work already with devenv's postgres)
- Django secret key
- Any API keys or external service configurations
Note: The
.envfile is automatically loaded by direnv when you're in the project directory.
To bring up all required services (PostgreSQL, Django, Tailwind, etc.) in the background:
devenv upThis command starts all the services defined in devenv.nix using process-compose. The services will run in the background and restart automatically if they crash.
Like any Django project, you need to set up the database initially:
./manage.py migrateVisit http://localhost:8000 in your browser to see your Django application running!
To access the Django admin interface:
./manage.py createsuperuserThen visit http://localhost:8000/admin/ to log in.
This project comes pre-configured with several AI-powered development tools that are automatically installed via the devenv setup. This may be particularly useful for Python developers who may not have Node.js/npm/yarn installed on their machines.
The following AI development tools are automatically installed when you enter the devenv shell:
- Claude Code (
@anthropic-ai/claude-code) - Anthropic's official CLI for Claude AI assistance - Gemini CLI (
@google/gemini-cli) - Google's Gemini AI command-line interface - OpenAI Codex (
@openai/codex) - OpenAI's code generation and assistance tool - Sourcegraph AMP (
@sourcegraph/amp) - Sourcegraph's AI-powered development assistant
When you run direnv allow and enter the development environment, devenv automatically:
- Installs Node.js and yarn/npm - No need to install these separately
- Installs AI development tools - All configured via the
devenv.nixfile - Makes tools available - AI agents are accessible directly from your terminal and are NOT installed globally.
Once your development environment is active, you can use these tools directly:
# Use Claude Code for development assistance
claude --help
# Use Google Gemini CLI
gemini --help
# Use OpenAI Codex
codex --help
# Use Sourcegraph AMP
amp --help- No Node.js setup required - Everything is managed by devenv
- Consistent across environments - Same tools for all team members
- Zero configuration - Tools are ready to use immediately
- Reproducible - Exact versions pinned
This approach eliminates the friction of setting up JavaScript tooling just to access AI development tools, making them accessible to Python-focused developers without polluting their system with additional package managers.
This project uses uv for dependency management and includes several useful development commands:
# Install all dependencies
uv sync
# Add a new dependency
uv add <package>
# Add a development dependency
uv add --dev <package>
# Update dependencies
uv sync --upgrade# Run the development server
python manage.py runserver
# Create and apply database migrations
python manage.py makemigrations
python manage.py migrate
# Create a superuser
python manage.py createsuperuser
# Collect static files
python manage.py collectstatic
# Open Django shell
python manage.py shell# Build Docker image
make SHORT_SHA=<commit-sha> build-docker-image# Deploy to Kubernetes
make IMAGE_TAG=<tag> ENVIRONMENT=<env> NAMESPACE=<ns> KUBECONFIG=<config> deploy
# Example production deployment
make IMAGE_TAG=v1.0.0 ENVIRONMENT=prod NAMESPACE=hellok8s KUBECONFIG=~/.kube/config deployNOTE: I make it a point in all projects to enable developers to run commands locally that the CI invokes. This is very useful when fires arise.
Run the test suite using Django's built-in test runner:
# Run all tests
python manage.py test
# Run tests for a specific app
python manage.py test myauth
# Run a specific test class
python manage.py test post.tests.PostTestCase
# Run a specific test method
python manage.py test post.tests.PostTestCase.test_post_creation
# Run tests with verbose output
python manage.py test --verbosity=2Each Django app contains its tests in a tests.py file:
myauth/tests.py- Custom user model testspost/tests.py- Blog post functionality testscore/tests.py- Core functionality tests
This Django project is organized into several apps:
myauth/- Custom user model (email instead of username)post/- Just simple blog post functionalitymyutils/- Shared utilities and abstract model behaviorscore/- Core functionality including custom storage classesproject/- Django project settings and configurationchart/- Helm chart for Kubernetes deployment
- Django 5.2+ - Web framework
- PostgreSQL - Database (configured via devenv)
- uv - Fast Python package manager (replaces pip/poetry)
- Tailwind CSS - Utility-first CSS framework
- Docker - Containerization
- Kubernetes + Helm - Orchestration and deployment
- GitHub Actions - CI/CD pipeline
This project uses GitHub Actions with a reusable workflow architecture that supports deploying to multiple environments (staging, production, etc.).
-
Triggering Builds:
- Every push to
maintriggers the CI pipeline - On an actual private production repo we'd want pull requests to run tests and build validation. This is disabled here since this is a public example repo (see .github/workflows/cici.yml) which warrants special considerations.
- Triggers production deployments
- Every push to
-
Docker Image Building: The application is packaged into a Docker container but it uses
uvfor fast, reproducible builds of it's dependencies:# Uses uv for lightning-fast dependency installation FROM python:3.13-slim COPY --from=ghcr.io/astral-sh/uv:latest /uv /usr/local/bin/uv # ... rest of Dockerfile
-
Reusable Workflow: The
.github/workflows/directory contains reusable workflow templates that can be called from different environments:# Example: Deploy to prod jobs: deploy: uses: ./.github/workflows/deploy.yml with: environment: prod namespace: hellok8s
The Kubernetes deployment uses Helm for templating and configuration management:
chart/
├── Chart.yaml # Chart metadata
├── values.yaml # Default values
├── templates/
│ ├── deployment.yaml # Django app deployment
│ ├── service.yaml # Kubernetes service
│ ├── ingress.yaml # Ingress configuration
...
└── values/
└── prod.yaml # Production-specific values
└── secrets.yaml # Production-specific sops encrypted secrets
- GitHub Actions builds and pushes Docker image to docker hub
- Helm chart is deployed to Kubernetes cluster
- SOPS decrypts environment-specific secrets during deployment
- Rolling deployment ensures zero-downtime updates
The deployment command looks like:
make IMAGE_TAG=sha-123 ENVIRONMENT=prod NAMESPACE=hellok8s KUBECONFIG=/path/to/kubeconfig.yaml deployThis project uses SOPS for managing encrypted secrets in the repository.
- Environment Variables: Database passwords, API keys, and other secrets are stored encrypted in
chart/values/<env>/secrets.yaml - Encryption: We use age encryption (though AWS KMS and others are also supported)
- CI/CD Integration: GitHub Actions runners decrypt secrets during deployment using the private age key
- Runtime: Secrets are injected into Kubernetes pods as environment variables
The .sops.yaml file defines encryption rules:
creation_rules:
- path_regex: \.yaml$
age: age1... # public age keyFor production systems, consider using AWS KMS (or similar) instead of age keys. See the SOPS documentation for configuration details.
Want to use this project as a starting point for your own Django application? Here's how:
# Clone the repository
git clone https://github.com/denibertovic/hellok8s-django.git my-new-project
cd my-new-project
# Remove the existing git history
rm -rf .git
# Initialize a new git repository
git init
git add .
git commit -m "Initial commit from hellok8s-django template"
# Add your own remote origin
git remote add origin https://github.com/yourusername/my-new-project.git
git push -u origin main- Update
pyproject.tomlwith your project name and details - Modify Django settings in
project/settings.py - Update the Helm chart in
chart/with your application name - Customize the README.md for your project
Follow the Local Development Setup instructions above.
To enable the CI/CD pipeline, you need to configure several secrets in your GitHub repository.
These secrets are available to all environments and workflows:
-
DOCKERHUB_USERNAME- Your Docker Hub usernameexample: johndoe -
DOCKERHUB_TOKEN- Your Docker Hub access tokenGenerate at: https://hub.docker.com/settings/security -
AGE_KEY_FILE- Private age key for SOPS decryptionGenerate an age key:
# Install age if you haven't already nix profile install nixpkgs#age # Generate a new key pair age-keygen -o age-key.txt # Copy the ENTIRE contents of age-key.txt as the secret value cat age-key.txt # You can also use the gh cli to this more effectively # add --env prod for setting a secret in an environment like prod/staging/etc cat age-key.txt | gh secret set AGE_KEY_FILE --app actions --repo yourusername/yourrepo
Alternative: AWS KMS Instead of age keys, you can use AWS KMS for encryption. See the SOPS documentation for setup instructions.
Create a production environment in your repository settings, then add:
-
KUBECONFIG_YAML- Your Kubernetes cluster configuration⚠️ Important: This should be an Environment Secret, not a Repository Secret, for better security isolation and since you'll likely have a separate staging cluster with different credentials.# Get your kubeconfig content cat ~/.kube/my-k8s-config-limites-to-this-namespace.yaml # Copy the entire YAML content as the secret value # TODO: publish terraform module for creating these
- Go to your repository on GitHub
- Click Settings → Secrets and variables → Actions
- Add the three repository secrets listed above
- Click Environments → New environment → Name it "prod"
- Add the
KUBECONFIG_YAMLsecret to the production environment
If you prefer to use AWS ECR, Google Container Registry, or another registry instead of Docker Hub:
- Replace
DOCKERHUB_USERNAMEandDOCKERHUB_TOKENwith appropriate credentials - Update the Docker registry configuration in
.github/workflows/build.ymlfiles (there's a commented out example for AWS ECR) - Update the Helm chart's image repository settings in
chart/values.yaml(you'll also need to add imagePullSecrets since this will likely be a private registry).
Problem: direnv allow doesn't work
- Make sure you have direnv installed and the shell hook configured
- Check that you're in the project root directory
- Try running
direnv reload
Problem: Services won't start with devenv up
- Check if PostgreSQL port (5432) is already in use:
lsof -i :5432 - Ensure all dependencies are installed:
uv sync - Check the devenv logs:
devenv up --verbose
Problem: Database connection errors
- Verify PostgreSQL is running:
pg_isready -h localhost -p 5432 - Check your
.envfile has correct database settings - Try running migrations:
python manage.py migrate
Problem: Docker build fails
- Ensure you have the latest uv version in your Dockerfile
- Check that all dependencies in
pyproject.tomlare available - Verify your Docker daemon is running
Problem: Kubernetes deployment fails
- Check your kubeconfig is valid:
kubectl cluster-info - Verify secrets are properly encrypted with SOPS
- Check Helm chart syntax:
make IMAGE_TAG=foo NAMESPACE=hellok8s ENVIRONMENT=prod helm-lint
Problem: Cannot decrypt secrets
- Verify your age private key is correctly set in GitHub secrets
- Check that the public key in
.sops.yamlmatches your private key - Ensure you have the correct permissions to access the encrypted files
Problem: Environment variables not loading
- Make sure your
.envfile is in the project root - Check that direnv is loading the environment:
direnv status - Verify environment variable names match Django settings (DJANGO_* prefix)
If you encounter issues not covered here:
- Check the GitHub Issues for similar problems
- Review the devenv and Django logs for error messages
- Ensure all prerequisites are correctly installed
- Try recreating your development environment from scratch (see
make clean)
# print make commands
# (poor man's cli) :)
make help
# Check devenv status
devenv info
# Check Django configuration
python manage.py check
# connect to database (psql)
python manage.py dbshell
# Check static files collection
python manage.py collectstatic --dry-run
# Validate Helm chart
make IMAGE_TAG=foo NAMESPACE=hellok8s ENVIRONMENT=prod helm-template
# Test SOPS decryption
sops -d chart/values/prod/secrets.yamlThis project is open source and available under the BSD 3-Clause License.