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
- Deploying With Gunicorn and Nginx
- Testing Flask Applications
- Docker installed—see Linux Docker introduction
Dockerfile (Multi-Stage Concept)
Dockerfile:
Code explanation:
- Builder stage installs deps; final image stays smaller
USER appuseravoids running as root inside container0.0.0.0bind lets Docker map port to host
Build and run:
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
.venv/
__pycache__/
.git/
.env
instance/
.pytest_cache/
tests/
*.mdKeep image lean—do not copy local venv.
docker compose Stack
docker-compose.yml:
Run:
export SECRET_KEY=$(python -c "import secrets; print(secrets.token_hex(16))")
docker compose up --buildVisit http://localhost:8000/health.
Optional Redis service for cache/Celery—add redis:7 image when needed from caching chapter.
Environment in Compose vs Production
| Environment | Secret source |
|---|---|
| Local compose | .env file (gitignored) |
| Production | Orchestrator secrets, not committed YAML |
.env.example for teammates:
SECRET_KEY=replace-me
MYSQL_PASSWORD=replace-meGitHub Actions CI
.github/workflows/flask-ci.yml:
Code explanation:
pytestusesTestingConfigin-memory SQLite from testing chapterpip audit || truewarns without failing early projects—remove|| truewhen strict
Protect main branch: require CI pass before merge—see Git and CI/CD.
Deploy Pipeline (Concept)
push → CI (pytest) → build Docker image → push registry → deploy server pulls → compose up -dTag 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:
location / {
proxy_pass http://127.0.0.1:8000;
}Map compose ports: "8000:8000" only on internal network in hardened setups.
Developer Workflow Link
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.