Flask Knowledge Pack
Applies to: Projects detected with
requirements.txt,pyproject.toml, orPipfilecontainingflask
Quick Reference
| Property | Value |
|---|---|
| Signal files | requirements.txt/pyproject.toml/Pipfile containing flask |
| Default port | 8000 prod (5000 dev — never in prod) |
| Health path | /health + /ready |
| Base template | templates/dockerfiles/python.Dockerfile (+ references/base-images.md) |
Health Endpoints
Flask does not include health check endpoints — they must be defined explicitly in application code:
Minimal health route
from flask import Flask, jsonify
app = Flask(__name__)
@app.route("/health")
def health():
return jsonify(status="ok"), 200Readiness route with database check
from flask import jsonify
from sqlalchemy import text
@app.route("/ready")
def ready():
try:
db.session.execute(text("SELECT 1"))
return jsonify(status="ready"), 200
except Exception:
return jsonify(status="not ready"), 503Probe configuration in Deployment manifest
livenessProbe:
httpGet:
path: /health
port: 8000
initialDelaySeconds: 5
periodSeconds: 15
timeoutSeconds: 3
failureThreshold: 3
readinessProbe:
httpGet:
path: /ready
port: 8000
initialDelaySeconds: 5
periodSeconds: 10
timeoutSeconds: 3
failureThreshold: 3Note: Flask apps behind gunicorn start quickly (typically <3s), so initialDelaySeconds: 5 is sufficient — much lower than JVM-based frameworks.
Database Profiles
Flask does not have a built-in profile system. Database configuration is typically driven by environment variables:
| ORM / Driver | Package(s) | Connection String Env Var |
|---|---|---|
| Flask-SQLAlchemy | flask-sqlalchemy, psycopg2-binary |
SQLALCHEMY_DATABASE_URI |
| SQLAlchemy direct | sqlalchemy, psycopg2-binary |
DATABASE_URL |
| psycopg2 direct | psycopg2-binary |
DATABASE_URL |
Important: Flask-SQLAlchemy reads the connection string from app.config["SQLALCHEMY_DATABASE_URI"], which is typically set via os.environ.get("SQLALCHEMY_DATABASE_URI") or os.environ.get("DATABASE_URL"). Ensure the env var name matches what the app expects.
Environment variables for PostgreSQL on AKS
env:
- name: SQLALCHEMY_DATABASE_URI
value: "postgresql://{{IDENTITY_NAME}}@{{PG_SERVER_NAME}}.postgres.database.azure.com:5432/{{DB_NAME}}?sslmode=require"
- name: SECRET_KEY
valueFrom:
secretKeyRef:
name: {{APP_NAME}}-secrets
key: secret-keySecret for SECRET_KEY
Flask requires SECRET_KEY for session signing, CSRF tokens, and any use of flask.session. Never hardcode it — store it in a Kubernetes Secret:
apiVersion: v1
kind: Secret
metadata:
name: {{APP_NAME}}-secrets
type: Opaque
stringData:
secret-key: "<generate-a-random-string>"ConfigMap pattern
apiVersion: v1
kind: ConfigMap
metadata:
name: {{APP_NAME}}-config
data:
SQLALCHEMY_DATABASE_URI: "postgresql://{{IDENTITY_NAME}}@{{PG_SERVER_NAME}}.postgres.database.azure.com:5432/{{DB_NAME}}?sslmode=require"Workload Identity with azure-identity
See references/workload-identity.md for connection patterns. Requires azure-identity package.
Writable Paths (DS012 Compliance)
When readOnlyRootFilesystem: true is set, Flask apps typically only need /tmp writable:
- Uploaded files use
/tmpas the default staging directory forrequest.files - Temporary processing may write intermediate results to
/tmp
Required volume mount
volumes:
- name: tmp
emptyDir: {}
containers:
- name: app
volumeMounts:
- name: tmp
mountPath: /tmpNo other writable paths are typically needed for production Flask apps.
Resource Sizing
Flask with Gunicorn runs multiple worker processes. Size for the number of workers (default: 2-4).
| Resource | Request | Limit |
|---|---|---|
| CPU | 150m | 500m |
| Memory | 128Mi | 256Mi |
Port Configuration
- Development port: 5000 (
flask rundefault — do not use in production) - Production port: 8000 (gunicorn convention)
- CLI flag:
--bind 0.0.0.0:8000passed togunicorn - Env var override:
PORT(read viagunicorn --bind 0.0.0.0:$PORTor in app codeint(os.environ.get("PORT", 8000))) - Workers formula:
2 * CPU_CORES + 1— override at runtime viaWEB_CONCURRENCYenv var - Entry point variants:
gunicorn "app:app"(module-level) orgunicorn "myapp:create_app()"(application factory)
Gunicorn logs the port on startup: Listening at: http://0.0.0.0:8000
Build Commands
| Tool | Install Command |
|---|---|
| pip | pip install --no-cache-dir -r requirements.txt |
| Poetry | poetry install --only main --no-interaction |
Ensure gunicorn is listed in requirements.txt or pyproject.toml production dependencies.
Common Issues on AKS
| Issue | Symptom | Fix |
|---|---|---|
| Running dev server in production | Single-threaded, poor performance, WARNING: This is a development server in logs |
Use gunicorn as the ENTRYPOINT — never use flask run or app.run() in production containers |
SECRET_KEY not set |
RuntimeError: The session is unavailable because no secret key was set, CSRF failures |
Set SECRET_KEY via a Kubernetes Secret and reference it as an env var in the Deployment manifest |
| Flask binds to localhost | Connection refused from Kubernetes probes | Pass --bind 0.0.0.0:8000 to gunicorn — the Flask dev server defaults to 127.0.0.1 which is unreachable from outside the container |
| Gunicorn not installed | ModuleNotFoundError: No module named 'gunicorn' |
Ensure gunicorn is in requirements.txt or pyproject.toml main dependencies — it is often only in dev dependencies or missing entirely |
| DB connections not closed | sqlalchemy.exc.TimeoutError: QueuePool limit, PostgreSQL max_connections exhaustion |
Configure pool size with SQLALCHEMY_ENGINE_OPTIONS = {"pool_size": 5, "max_overflow": 10, "pool_recycle": 300} and match PostgreSQL max_connections |