Preamble
This tutorial aims to guide a beginner developer in setting up a containerized Django development environment, from initial installation to complete configuration.
System Prerequisites
1. Prior Installations
Before beginning, ensure you have installed the following tools:
For Ubuntu/Debian:
# System updates
sudo apt update && sudo apt upgrade -y
# Essential installations
sudo apt install -y \
git \
curl \
wget \
software-properties-common \
apt-transport-https \
ca-certificates \
gnupg \
lsb-release \
postgresql-client \
python3-pip \
python3-venv \
build-essential \
libpq-dev
For other Linux distributions, adapt the commands:
- Fedora/CentOS:
dnf
oryum
- Arch Linux:
pacman
2. Docker Installation
# Docker installation
curl -fsSL https://download.docker.com/linux/ubuntu/gpg | sudo gpg --dearmor -o /usr/share/keyrings/docker-archive-keyring.gpg
echo \
"deb [arch=$(dpkg --print-architecture) signed-by=/usr/share/keyrings/docker-archive-keyring.gpg] https://download.docker.com/linux/ubuntu \
$(lsb_release -cs) stable" | sudo tee /etc/apt/sources.list.d/docker.list > /dev/null
sudo apt update
sudo apt install -y docker-ce docker-ce-cli containerd.io docker compose-plugin
# Add your user to the docker group
sudo usermod -aG docker $USER
# Restart to apply changes
sudo systemctl restart docker
3. Verifications
# Verify installations
docker --version
docker compose --version
python3 --version
psql --version
Dockerized Django Project Structure
Creating Project Structure
# Create project directory
mkdir projet_django_docker
cd projet_django_docker
# Directory structure
mkdir -p app/mon_projet
touch app/Dockerfile
touch app/requirements.txt
touch app/entrypoint.sh
touch docker-compose.yml
touch .env
1. requirements.txt File
# app/requirements.txt
Django==4.2.7
psycopg2-binary==2.9.9
gunicorn==21.2.0
python-dotenv==1.0.0
2. Dockerfile
# app/Dockerfile
FROM python:3.11-slim
# Metadata
LABEL maintainer="Your Name <your.email@example.com>"
# Environment variables
ENV PYTHONDONTWRITEBYTECODE 1
ENV PYTHONUNBUFFERED 1
ENV PYTHONPATH=/app
# Working directory
WORKDIR /app
# Install system dependencies
RUN apt-get update && apt-get install -y \
postgresql-client \
build-essential \
libpq-dev \
&& rm -rf /var/lib/apt/lists/*
# Copy and install requirements
COPY requirements.txt .
RUN pip install --no-cache-dir -r requirements.txt
# Copy the rest of the project
COPY . .
# Give permissions to entry script
RUN chmod +x /app/entrypoint.sh
# Entry point
ENTRYPOINT ["/app/entrypoint.sh"]
entrypoint.sh Script: Understanding Database Waiting
Why Wait for the Database?
#!/bin/bash
# Wait for database availability
echo "Waiting for database..."
while ! nc -z db 5432; do
sleep 1
done
echo "Database ready!"
Technical Context
When starting a containerized application, services do not all start instantaneously. The PostgreSQL database can take a few seconds to be fully operational.
Concrete Problem
If your Django application attempts to connect to the database before it's ready, you'll encounter a connection error. This could block the complete startup of your application.
How Does It Work?
nc -z db 5432
: Checks if port 5432 (PostgreSQL port) is accessiblewhile
: Loops until connection is possiblesleep 1
: One-second pause between each attempt
Complete Code with Migrations
#!/bin/bash
# Wait for database
echo "Waiting for database..."
while ! nc -z db 5432; do
sleep 1
done
echo "Database ready!"
# Apply Django migrations
python manage.py migrate
# Launch web server
exec gunicorn --bind 0.0.0.0:8000 mon_projet.wsgi:application
Docker Compose: Understanding the Configuration
Structure of docker-compose.yml File
version: '3.8' # Docker Compose syntax version
services:
# Web Service (Django Application)
web:
build:
context: ./app
dockerfile: Dockerfile
volumes:
- ./app:/app # Source code synchronization
ports:
- "8000:8000" # Port mapping
env_file:
- .env # Environment file
depends_on:
- db # Dependency with database service
networks:
- dev_network # Custom network
# Database Service
db:
image: postgres:15-alpine
volumes:
- postgres_data:/var/lib/postgresql/data # Data persistence
environment:
- POSTGRES_DB=projet_db
- POSTGRES_USER=projet_user
- POSTGRES_PASSWORD=secret_password
ports:
- "5432:5432" # PostgreSQL port exposure
networks:
- dev_network
# Volumes and Networks
volumes:
postgres_data: # Volume for PostgreSQL data
networks:
dev_network:
driver: bridge # Network type
Detailed Explanations
1. Networks
dev_network
: A custom network allowing communication between containersdriver: bridge
: Creates an isolated network on the host
2. Ports: Internal vs External
- Format:
"8000:8000"
- First port: External port (accessible from host)
- Second port: Container's internal port
3. Service Dependencies
depends_on: - db
means the web service will start after the database service- Guarantees startup order, not a "ready to use" guarantee
Accessing Container Terminals
# Access terminal of a specific service
docker compose exec web bash
docker compose exec db bash
Important Points
- Works ONLY when containers are running
- Allows performing actions directly in the container
- Equivalent to SSH access for containers
Additional Tips
- Recent Docker versions automatically detect Compose version
- Always specify a context and a Dockerfile for more clarity
- Use environment variables for sensitive configuration
Useful Commands
# Start services
docker compose up
# Start in detached mode
docker compose up -d
# Stop services
docker compose down
# View logs
docker compose logs
# Run a command in a service
docker compose run web python manage.py createsuperuser
Why Are These Details Important?
- In-depth understanding of mechanisms
- More efficient debugging
- More flexible configuration
- Better control of development environment
.env File
# Django Configuration
DEBUG=1
SECRET_KEY=your_ultra_secure_secret_key
DJANGO_ALLOWED_HOSTS=localhost,127.0.0.1
# Database Configuration
DB_NAME=projet_db
DB_USER=projet_user
DB_PASSWORD=secret_password
DB_HOST=db
DB_PORT=5432
Creating the Django Project
Project Initialization
# Create and initialize the Django project
docker compose run --rm web django-admin startproject mon_projet .
# Configure settings.py for the database
# Modify the DATABASES section:
DATABASES = {
'default': {
'ENGINE': 'django.db.backends.postgresql',
'NAME': os.getenv('DB_NAME', 'projet_db'),
'USER': os.getenv('DB_USER', 'projet_user'),
'PASSWORD': os.getenv('DB_PASSWORD', 'secret_password'),
'HOST': os.getenv('DB_HOST', 'db'),
'PORT': os.getenv('DB_PORT', '5432'),
}
}
Starting and Using
Essential Commands
# Build and start containers
docker compose up --build
# Stop containers
docker compose down
# Create a new application
docker compose run --rm web python manage.py startapp my_new_app
# Create migrations
docker compose run --rm web python manage.py makemigrations
# Apply migrations
docker compose run --rm web python manage.py migrate
Tips and Best Practices
- Security:
- Never commit the
.env
file - Use a
.env.example
with dummy values - Generate robust secret keys
- Never commit the
- Performance:
- Use volumes for development
- Optimize Docker images
- Manage dependencies carefully
- Development:
- Use
docker-compose.override.yml
for specific configurations - Set up an adapted git workflow
- Use
Troubleshooting
- Check logs:
docker compose logs
- Check container status:
docker compose ps
- Connect to a container:
docker compose exec web bash
Possible Evolutions
- Add a Redis/Celery container
- Set up an Nginx proxy
- Configure a volume for static files