Flask Docker Basics

Docker packages your Flask application with all its dependencies into a container — a self-contained unit that runs identically on every machine. The classic problem "it works on my computer but not the server" disappears when you use Docker.

The Shipping Container Analogy

Before standardized shipping containers, loading cargo onto different ships required custom handling for every shipment. A standardized container fits every ship, truck, and crane without modification. Docker containers work the same way — one container image runs on your laptop, a coworker's Mac, a Linux server, or a cloud platform without any changes.

Without Docker:                   With Docker:
  Dev: Python 3.11, Flask 3.0      Container: Python 3.11 + Flask 3.0
  Server: Python 3.9, Flask 2.2    Runs same container on every machine
  Result: version conflicts         Result: identical behavior everywhere

Core Docker Concepts

ConceptAnalogyDescription
ImageBlueprintA read-only template with your app and dependencies
ContainerRunning buildingA running instance of an image
DockerfileConstruction planInstructions for building an image
RegistryApp storeStorage for images (Docker Hub, AWS ECR)

Installing Docker

Download Docker Desktop from docker.com for Windows and macOS. On Linux:

apt install docker.io docker-compose -y
systemctl start docker
systemctl enable docker

Writing a Dockerfile

Create a file named Dockerfile (no extension) in your project root:

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

# Set the working directory inside the container
WORKDIR /app

# Copy dependency list first (Docker caches this layer)
COPY requirements.txt .

# Install all Python dependencies
RUN pip install --no-cache-dir -r requirements.txt

# Copy the rest of the application code
COPY . .

# Set environment variables
ENV FLASK_ENV=production
ENV PYTHONUNBUFFERED=1

# Expose the port Gunicorn will listen on
EXPOSE 8000

# Command to run when the container starts
CMD ["gunicorn", "--bind", "0.0.0.0:8000", "--workers", "3", "run:app"]

The .dockerignore File

Prevent unnecessary files from entering the image. Create .dockerignore:

venv/
__pycache__/
*.pyc
.env
.git/
tests/
*.md

This keeps the image small and prevents secrets in .env from being baked into the image.

Building and Running the Image

# Build the image (tag it as 'myflaskapp:latest')
docker build -t myflaskapp:latest .

# Run a container from the image
docker run -d \
  -p 8000:8000 \
  -e SECRET_KEY=my-secret \
  -e DATABASE_URL=sqlite:///app.db \
  --name flaskapp \
  myflaskapp:latest

# Check running containers
docker ps

# View container logs
docker logs flaskapp

# Stop the container
docker stop flaskapp

Docker Compose for Multi-Container Apps

Real Flask apps often use a separate database container. Docker Compose defines and starts multiple containers together with one command.

Create docker-compose.yml:

version: '3.8'

services:
  web:
    build: .
    ports:
      - "8000:8000"
    environment:
      - SECRET_KEY=mysecret
      - DATABASE_URL=postgresql://postgres:password@db:5432/myapp
    depends_on:
      - db
    volumes:
      - .:/app

  db:
    image: postgres:15
    environment:
      - POSTGRES_USER=postgres
      - POSTGRES_PASSWORD=password
      - POSTGRES_DB=myapp
    volumes:
      - postgres_data:/var/lib/postgresql/data

volumes:
  postgres_data:
# Start all services
docker-compose up -d

# Run migrations inside the web container
docker-compose exec web flask db upgrade

# Stop all services
docker-compose down

How Docker Compose Networking Works

docker-compose creates a private network:

  [web container]  ──▶  [db container]
  hostname: web         hostname: db
  port: 8000            port: 5432

Flask connects to PostgreSQL at:
  postgresql://postgres:password@db:5432/myapp
                                  ▲
                         hostname 'db' resolves to the
                         db container's IP automatically

Publishing to Docker Hub

# Log in to Docker Hub
docker login

# Tag your image
docker tag myflaskapp:latest yourusername/myflaskapp:latest

# Push to Docker Hub
docker push yourusername/myflaskapp:latest

# On any other machine, run it with:
docker pull yourusername/myflaskapp:latest
docker run -d -p 8000:8000 yourusername/myflaskapp:latest

Dockerfile Layer Caching

Docker caches each step (layer) in the Dockerfile. If a layer has not changed, Docker reuses the cache. Copying requirements.txt before the rest of the code means Docker only reinstalls packages when requirements.txt changes — not on every code edit. This dramatically speeds up build times during development.

COPY requirements.txt .   ← cached unless requirements change
RUN pip install ...       ← cached unless requirements change
COPY . .                  ← rebuilds on every code change (fast, no pip)

Summary

Docker packages your Flask app and all its dependencies into a portable container. Write a Dockerfile with instructions to build the image, then run it with docker run. Use Docker Compose to manage multi-container setups like Flask + PostgreSQL with one command. Pass secrets through environment variables at runtime — never bake them into the image. Docker eliminates environment differences between development, staging, and production.

Leave a Comment

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