ARTICLE

Docker Demystified: A Beginner’s Guide to Containerizing a Django & MySQL App

Docker Demystified: A Beginner’s Guide to Containerizing a Django & MySQL App

 

Docker Demystified: A Beginner’s Guide to Containerizing a Django & MySQL App | Omkar Kamat


Ever had an app work perfectly on your laptop, only to watch it break completely when deployed or shared with a teammate?

That "it works on my machine" headache is exactly why Docker exists.

If you are new to Docker, the terminology can feel overwhelming. In this post, we’ll break down core Docker concepts using a real-world Django + MySQL setup, including how to handle secrets safely using a .env file.

The Core Core Concepts

Before looking at the code, let’s clear up the 6 fundamental building blocks of Docker:

  1. Dockerfile: A blueprint file containing step-by-step instructions on how to build your environment.
  2. Image: A lightweight, standalone package created from your Dockerfile. Think of it as a frozen snapshot of your app and its dependencies.
  3. Container: A runnable, isolated instance of an image. If the image is the recipe, the container is the cooked meal.
  4. Volumes: Persistent storage. By default, data inside a container vanishes when the container stops. Volumes let you save data (like database records) safely on your host machine.
  5. Networks: The invisible cables connecting your containers so they can talk to each other securely.
  6. Docker Compose: A tool for defining and running multi-container applications (like a Web App + Database) with a single command.

1. Writing the Dockerfile (Building the Web Image)

Here is a standard Dockerfile for a Python/Django app:

Dockerfile

FROM python:3.12

ENV PYTHONDONTWRITEBYTECODE 1
ENV PYTHONUNBUFFERED 1
WORKDIR /app
# install dependencies
COPY requirements.txt /app/
RUN pip install --upgrade pip
RUN pip install --no-cache-dir -r requirements.txt

# copy project
COPY . /app/

EXPOSE 8000

What's happening line-by-line?

  • FROM python:3.12: Tells Docker to grab an official base image containing Python 3.12.
  • ENV ...: Sets environment variables inside Python. PYTHONDONTWRITEBYTECODE 1 stops Python from writing .pyc files, and PYTHONUNBUFFERED 1 ensures console logs show up instantly in real-time.
  • WORKDIR /app: Creates and switches to /app as the default directory inside the container.
  • COPY requirements.txt /app/ & RUN pip install...: Copies your dependency list first and installs them.

Pro Tip: We copy requirements.txt before the rest of our code so Docker caches the installed packages. If you change a line of code later, Docker doesn't re-install all Python packages from scratch!

  • COPY . /app/: Copies your local source code into the container's /app directory.
  • EXPOSE 8000: Informs Docker that the container listens on port 8000 at runtime.


2. Introducing the .env File (Securing Environment Variables)

Hardcoding database passwords or secret keys directly inside your files is a huge security risk. Instead, store sensitive credentials in a .env file.

Create a file named .env in your project root:

Code snippet:

# .env file
DEBUG=1
SECRET_KEY=your-django-secret-key
MYSQL_DATABASE=db_name
MYSQL_USER=django_user
MYSQL_PASSWORD=userpassword
MYSQL_ROOT_PASSWORD=rootpassword
DB_HOST=db
DB_PORT=3306

Important: Never commit your .env file to Git! Add it to your .gitignore file.

3. Stitching It Together with Docker Compose (docker-compose.yml)

Instead of running long docker run commands manually, docker-compose.yml ties your database, web application, volumes, and networks together cleanly.

YAML:

services:
  db:
    image: mysql:8.4
    container_name: mysql_db
    restart: always
    environment:
      MYSQL_DATABASE: db_name
      MYSQL_ROOT_PASSWORD: rootpassword
    command: --mysql-native-password=ON
    ports:
      - "3306:3306"
    volumes:
      - mysql_data:/var/lib/mysql
    healthcheck:
      test: ["CMD-SHELL", "mysqladmin ping -h localhost -uroot -prootpassword"]
      interval: 5s
      timeout: 5s
      retries: 5

  web:
    build: .
    container_name: django_app
    command: >
      sh -c "python config/manage.py migrate &&
             python config/manage.py collectstatic --noinput &&
             gunicorn --workers 3 --bind 0.0.0.0:8000 --chdir /app/config config.wsgi:application"

    env_file:
      - .env
    volumes:
      - .:/app
    ports:
      - "8000:8000"
    depends_on:
      db:
        condition: service_healthy


volumes:
  mysql_data:

Breaking down the Compose file:

  • Containers & Images: db: Pulls the ready-to-use mysql:8.4 image from Docker Hub. web: Tells Docker to build an image using the Dockerfile in the current folder (build: .).
  • Environment & .env Integration: Notice env_file: - .env in the web service? Docker Compose reads your .env file and passes those variables straight into your Django container.
  • Volumes (Data Persistence & Live Reloading): mysql_data:/var/lib/mysql: Maps a named volume mysql_data to MySQL's internal data directory. If you restart or delete the container, your database tables remain safe. .:/app (in web): Maps your local current directory (.) to /app inside the container. Changes you make in your code editor locally will instantly reflect inside the running container!
  • Networks: Notice you didn't explicitly define a networks: block? Docker Compose automatically creates a default network for all services in the file! The Django container can talk to MySQL simply using the hostname db.
  • Healthcheck & Dependencies (depends_on): Web apps often crash if they try to run migrations before the database is fully booted up. The healthcheck on db pings MySQL until it responds. The depends_on condition ensures Django waits until MySQL is completely ready before starting.

4. How to Run It

With Docker installed and these files in place, launch your entire system with one command:

docker compose up --build

  • --build forces Docker to build the image from the Dockerfile.
  • Add -d at the end if you want to run it in detached (background) mode.

To stop everything:

docker compose down

Quick Summary Checklist for Beginners

  1. Dockerfile: Used for building custom app images.
  2. Docker Compose: Manages multi-container setups.
  3. Volumes: Keeps your database data intact when containers stop.
  4. Networks: Lets containers communicate using service names (e.g., db).
  5. .env: Keeps passwords and API keys out of your code.

Found this breakdown helpful? Feel free to like, share, or drop a comment below if you have any questions about getting started with Docker!

Comments