Flask Docker and CI

Introduction

Docker packages your Flask app with its Python dependencies into a reproducible image. CI runs tests on every push so broken code never reaches production. This chapter builds a production-oriented Dockerfile, a docker compose stack with MySQL, and a GitHub Actions workflow linked to the Git CI/CD track.

Prerequisites

Dockerfile (Multi-Stage Concept)

Dockerfile:

Code explanation:

  • Builder stage installs deps; final image stays smaller
  • USER appuser avoids running as root inside container
  • 0.0.0.0 bind lets Docker map port to host

Build and run:

bash
docker build -t flask-app .
docker run -p 8000:8000 \
  -e SECRET_KEY=dev-docker-key \
  -e DATABASE_URL=sqlite:////app/instance/app.db \
  flask-app

.dockerignore

dockerignore
.venv/
__pycache__/
.git/
.env
instance/
.pytest_cache/
tests/
*.md

Keep image lean—do not copy local venv.

docker compose Stack

docker-compose.yml:

Run:

bash
export SECRET_KEY=$(python -c "import secrets; print(secrets.token_hex(16))")
docker compose up --build

Visit http://localhost:8000/health.

Optional Redis service for cache/Celery—add redis:7 image when needed from caching chapter.

Environment in Compose vs Production

EnvironmentSecret source
Local compose.env file (gitignored)
ProductionOrchestrator secrets, not committed YAML

.env.example for teammates:

env
SECRET_KEY=replace-me
MYSQL_PASSWORD=replace-me

GitHub Actions CI

.github/workflows/flask-ci.yml:

Code explanation:

  • pytest uses TestingConfig in-memory SQLite from testing chapter
  • pip audit || true warns without failing early projects—remove || true when strict

Protect main branch: require CI pass before merge—see Git and CI/CD.

Deploy Pipeline (Concept)

text
push → CI (pytest) → build Docker image → push registry → deploy server pulls → compose up -d

Tag images with Git SHA for rollback.

Migrations in Containers

Run flask db upgrade on deploy—shown in compose command. For zero-downtime, run migrations as one-off job before switching traffic.

Nginx in Front of Compose

Production often runs Nginx on host or separate container:

nginx
location / {
    proxy_pass http://127.0.0.1:8000;
}

Map compose ports: "8000:8000" only on internal network in hardened setups.

Day-to-day Git, SSH, and server habits—Developer workflow on Linux.

FAQ

ModuleNotFoundError in container?

WORKDIR, PYTHONPATH, or missing COPY of package.

DB connection refused on startup?

Use depends_on + healthcheck; retry logic in app optional.

Image too large?

Slim base image, multi-stage build, .dockerignore.

CI passes but production fails?

Production uses MySQL—run integration tests against MySQL service in CI optionally.

Secrets in compose file?

Use ${VAR} from env file—never commit real passwords.

Windows Docker?

Same Dockerfile; path and line endings—use WSL2 backend for best results.

Build in CI and push image?

Add docker/build-push-action after tests pass on main.