How to Get Started with Django in Docker

How to Get Started with Django in Docker

Django is one of the most popular Python web frameworks, known for its “batteries-included” philosophy and rapid development capabilities. If you’re looking to containerize your Django application and simplify your deployment workflow, Docker is the perfect solution. In this comprehensive tutorial, I’ll walk you through everything you need to know to get your Django application running smoothly in Docker containers.

Prerequisites

Before we dive in, make sure you have:

  • Python 3.8 or higher installed on your local machine
  • Docker Desktop installed and running
  • Basic familiarity with Django (or willingness to learn!)
  • A code editor of your choice
  • Git installed (optional, but recommended)

Why Use Docker with Django?

Before we get our hands dirty, let’s talk about why containerizing your Django application makes sense:

Consistency Across Environments: Docker ensures your application runs the same way on your laptop, your colleague’s machine, and your production server. No more “it works on my machine” problems.

Simplified Dependencies: Everything your Django app needs is packaged in the container, from Python packages to system libraries.

Easy Scaling: Docker makes it simple to spin up multiple instances of your application when traffic increases.

Isolation: Your Django app runs in its own isolated environment, preventing conflicts with other applications or system packages.

Step 1: Create Your Django Project

If you already have a Django project, feel free to skip this step. Otherwise, let’s create a fresh Django project:

# Create a project directory
mkdir django-docker-app
cd django-docker-app

# Create a virtual environment
python -m venv venv
source venv/bin/activate  # On Windows: venv\Scripts\activate

# Install Django
pip install django

# Create a new Django project
django-admin startproject myproject .

Let’s verify everything works by running the development server:

python manage.py runserver

Visit http://localhost:8000 and you should see Django’s welcome page. Perfect! Now let’s containerize this application.

Step 2: Create a Requirements File

Django applications typically depend on multiple Python packages. Let’s freeze our dependencies into a requirements.txt file:

pip freeze > requirements.txt

Your requirements.txt should look something like this:

Django>=4.2.0,<5.0.0
gunicorn==21.2.0
psycopg2-binary==2.9.9
python-decouple==3.8
whitenoise==6.6.0

Key packages explained:

  • Django: Your web framework
  • gunicorn: A production-grade WSGI HTTP server
  • psycopg2-binary: PostgreSQL database adapter
  • python-decouple: For managing environment variables
  • whitenoise: For serving static files efficiently

Step 3: Create Your Dockerfile

Now for the exciting part! Create a file named Dockerfile (no extension) in your project root:

# Use an official Python runtime as the base image
FROM python:3.11-slim

# Set environment variables
ENV PYTHONDONTWRITEBYTECODE=1
ENV PYTHONUNBUFFERED=1

# Set work 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/*

# Install Python dependencies
COPY requirements.txt /app/
RUN pip install --upgrade pip
RUN pip install --no-cache-dir -r requirements.txt

# Copy project files
COPY . /app/

# Collect static files
RUN python manage.py collectstatic --noinput

# Expose port 8000
EXPOSE 8000

# Run gunicorn
CMD ["gunicorn", "--bind", "0.0.0.0:8000", "myproject.wsgi:application"]

Let’s break down what this Dockerfile does:

Base Image: We’re using Python 3.11 slim, which is lightweight but includes everything we need.

Environment Variables:

  • PYTHONDONTWRITEBYTECODE prevents Python from writing .pyc files
  • PYTHONUNBUFFERED ensures Python output is sent straight to terminal without buffering

System Dependencies: We install PostgreSQL client libraries and build tools needed for some Python packages.

Static Files: We collect static files during the build process so they’re ready to serve.

Gunicorn: Instead of Django’s development server, we use Gunicorn, which is production-ready and handles concurrent requests efficiently.

Step 4: Configure Django Settings for Docker

Open your settings.py file and make these important changes for containerized environments:

import os
from decouple import config

# Security settings
SECRET_KEY = config('SECRET_KEY', default='your-secret-key-here')
DEBUG = config('DEBUG', default=False, cast=bool)
ALLOWED_HOSTS = config('ALLOWED_HOSTS', default='localhost,127.0.0.1').split(',')

# Database configuration
DATABASES = {
    'default': {
        'ENGINE': 'django.db.backends.postgresql',
        'NAME': config('DB_NAME', default='postgres'),
        'USER': config('DB_USER', default='postgres'),
        'PASSWORD': config('DB_PASSWORD', default='postgres'),
        'HOST': config('DB_HOST', default='db'),
        'PORT': config('DB_PORT', default='5432'),
    }
}

# Static files configuration
STATIC_URL = '/static/'
STATIC_ROOT = os.path.join(BASE_DIR, 'staticfiles')
STATICFILES_STORAGE = 'whitenoise.storage.CompressedManifestStaticFilesStorage'

# Add WhiteNoise middleware
MIDDLEWARE = [
    'django.middleware.security.SecurityMiddleware',
    'whitenoise.middleware.WhiteNoiseMiddleware',  # Add this line
    # ... rest of middleware
]

These changes make your Django app container-friendly by using environment variables for configuration and enabling efficient static file serving.

Step 5: Create a Docker Ignore File

Just like .gitignore, we need a .dockerignore file to exclude unnecessary files from our Docker image:

*.pyc
*.pyo
*.mo
*.db
*.css.map
*.egg-info
*.sql.gz
.cache
.project
.idea
.pydevproject
.idea/workspace.xml
.DS_Store
.git/
.sass-cache
.vagrant/
dist
docs
env
logs
src/{{ project_name }}/settings/local.py
venv
__pycache__
*.log
.env
.venv
node_modules/
.coverage
htmlcov/

This keeps your Docker image lean and secure by excluding development files, caches, and sensitive information.

Step 6: Set Up Docker Compose

For most Django applications, you’ll need a database. Docker Compose makes it easy to run multiple containers together. Create a docker-compose.yml file:

version: '3.8'

services:
  db:
    image: postgres:15
    volumes:
      - postgres_data:/var/lib/postgresql/data
    environment:
      - POSTGRES_DB=djangodb
      - POSTGRES_USER=djangouser
      - POSTGRES_PASSWORD=djangopass
    ports:
      - "5432:5432"

  web:
    build: .
    command: gunicorn myproject.wsgi:application --bind 0.0.0.0:8000
    volumes:
      - .:/app
      - static_volume:/app/staticfiles
    ports:
      - "8000:8000"
    environment:
      - SECRET_KEY=your-secret-key-change-this-in-production
      - DEBUG=True
      - DB_NAME=djangodb
      - DB_USER=djangouser
      - DB_PASSWORD=djangopass
      - DB_HOST=db
      - DB_PORT=5432
      - ALLOWED_HOSTS=localhost,127.0.0.1
    depends_on:
      - db

volumes:
  postgres_data:
  static_volume:

This Docker Compose configuration:

  • Sets up a PostgreSQL database container
  • Creates persistent volumes for database data and static files
  • Configures environment variables for both services
  • Ensures the web container waits for the database to be ready
  • Maps ports so you can access your application

Step 7: Create an Environment File

Create a .env file in your project root for sensitive configuration:

SECRET_KEY=django-insecure-replace-this-in-production
DEBUG=True
ALLOWED_HOSTS=localhost,127.0.0.1
DB_NAME=djangodb
DB_USER=djangouser
DB_PASSWORD=djangopass
DB_HOST=db
DB_PORT=5432

Remember to add .env to your .gitignore file so you never commit sensitive credentials to version control!

Step 8: Build and Run Your Containers

Now comes the moment of truth! Let’s build and start your containerized Django application:

# Build the images
docker-compose build

# Start the containers
docker-compose up

The first time you run this, Docker will download the necessary images and install all dependencies. This might take a few minutes, so grab a coffee!

Step 9: Run Database Migrations

Once your containers are running, open a new terminal and run your Django migrations:

# Run migrations
docker-compose exec web python manage.py migrate

# Create a superuser
docker-compose exec web python manage.py createsuperuser

Now visit http://localhost:8000 in your browser. You should see your Django application running!

Step 10: Useful Docker Commands for Django Development

Here are some essential commands you’ll use regularly:

View Running Containers:

docker-compose ps

View Logs:

docker-compose logs -f web

Execute Django Management Commands:

docker-compose exec web python manage.py makemigrations
docker-compose exec web python manage.py migrate
docker-compose exec web python manage.py shell

Stop Containers:

docker-compose down

Stop and Remove Volumes (careful, this deletes your database!):

docker-compose down -v

Rebuild After Changes:

docker-compose up --build

Production Considerations

When deploying to production, keep these important points in mind:

Security First: Always use strong, randomly generated secret keys. Never commit them to version control. Use a secrets management service or encrypted environment variables.

Debug Mode Off: Set DEBUG=False in production. Debug mode exposes sensitive information and is a security risk.

Static Files: Ensure your static files are properly collected and served. Consider using a CDN or object storage service like AWS S3 for better performance.

Database Backups: Implement regular automated backups of your PostgreSQL database. Use volume backups or PostgreSQL’s native backup tools.

HTTPS: Always use HTTPS in production. Configure your reverse proxy or load balancer to handle SSL/TLS certificates.

Resource Limits: Set memory and CPU limits in your docker-compose file to prevent resource exhaustion.

Also Read: How To Deploy & Use Docker Containers on Your VPS

Troubleshooting Common Issues

Database Connection Errors: Make sure your database container is fully started before the web container tries to connect. You can add a health check or use a tool like wait-for-it.sh.

Static Files Not Loading: Verify that WhiteNoise is properly configured in your middleware and that collectstatic ran successfully during the build.

Port Already in Use: If port 8000 is already taken, change the port mapping in docker-compose.yml from "8000:8000" to something like "8001:8000".

Permission Issues: If you encounter permission errors with volumes, you may need to adjust the user running the container or volume permissions.

Cost Comparison for Deployment

If you’re planning to deploy your Dockerized Django application, here’s a quick comparison of popular platforms:

Platform Monthly Cost Features
DigitalOcean App Platform $12-25 Managed, auto-scaling, 1-2 vCPU
Heroku Standard $25-50 Managed, easy deployment, 2GB RAM
AWS ECS Fargate ~$30 Pay-per-use, highly scalable
Railway $5-20 Simple deployment, great DX
VPSWala $0-7 Zero-config, auto-deploy

Choose based on your budget, scale requirements, and technical expertise.

Next Steps

Congratulations! You now have a fully containerized Django application. Here are some suggestions for what to do next:

Add Redis: Integrate Redis for caching and session storage to improve performance.

Set Up CI/CD: Use GitHub Actions or GitLab CI to automatically build and test your Docker images on every commit.

Implement Monitoring: Add tools like Sentry for error tracking and Prometheus for metrics.

Configure Nginx: Add an Nginx container as a reverse proxy for better performance and security.

Add Celery: If you need background task processing, add Celery with a message broker like Redis or RabbitMQ.

Conclusion

Docker transforms Django development and deployment by providing consistency, portability, and ease of use. While there’s a learning curve, the benefits far outweigh the initial investment of time. Your Django application is now ready to run anywhere Docker is supported, from your laptop to cloud platforms to your own servers.

Remember, containerization is just one part of the puzzle. Continue learning about orchestration with Kubernetes, security best practices, and performance optimization to take your Django applications to the next level.

Happy coding, and may your containers always start successfully!

Comments

No comments yet. Why don’t you start the discussion?

Leave a Reply

Your email address will not be published. Required fields are marked *