Skeleton template for a Python >=3.12 microservice (API + worker) with ready-to-wire observability and infra hooks.
- API + worker entrypoint (
python -m skelv2.app) controlled byAPP_TYPE(api/worker). - Container-friendly
entrypoint.shthat runs Gunicorn for API or a Python module for worker; env-driven. - Health/ready endpoints with optional Redis-backed API-key protection.
- Structured JSON logging, Postgres + Redis wiring, pytest scaffolding.
- Name/version alignment helper (
set_versname.py) to keep code, env files, and folders consistent.
src/skelv2/— app package (renameable viaset_versname.py).api/— Flask bootstrap, routes, health checks.worker/— worker loop placeholder..env— service-level defaults (used bydotenvon local).
.env— outer file for local tooling (e.g.,PYTHONPATH="src/<pkg>").entrypoint.sh— container entrypoint; readsAPP_TYPE,APP_MODULE,GUNICORN_APP,WORKER_TARGET.pyproject.toml— source of truth for projectnameandversion.set_versname.py— synchronizes names/versions across code and env files.
- Local:
.env(outer) setsPYTHONPATH="src/<package>";src/<package>/.envsets service defaults. - Runtime config:
src/skelv2/config.pyloads from environment (12-factor). Use env vars in containers/CI. - Startup:
APP_TYPE=api→ Gunicorn runs<APP_MODULE>.wsgi:app.APP_TYPE=worker→python -m <WORKER_TARGET>.- Defaults:
APP_MODULE=<project_name>,GUNICORN_APP=<project_name>.wsgi:app,WORKER_TARGET=<project_name>.app.
Run after changing pyproject.toml name/version to keep everything aligned.
What it does:
- Reads
[project].nameand[project].versionplus[tool.poetry].packages[*].include. - Updates
pyproject.tomlso project name and include match. - Rewrites all
__version__occurrences in.pyfiles to the project version. - Renames
src/<old>folder tosrc/<project_name>if needed. - Updates
src/<project_name>/.env:SERVICE_NAME(appends-serviceif missing)SERVICE_VERSIONAPP_MODULE,GUNICORN_APP,WORKER_TARGET(derived from project name)
- Updates outer
.envPYTHONPATH="src/<project_name>".
Usage:
python3 set_versname.pyInstall dependencies:
pip install poetry==1.8.3
poetry installRun API:
export APP_TYPE=api FLASK_PORT=9000
poetry run python -m skelv2.appRun worker:
export APP_TYPE=worker
poetry run python -m skelv2.appBuild:
docker build -f iac/docker/alpine.dockerfile -t skelv2 .Key env vars:
APP_TYPE:apiorworkerGUNIPORT: Gunicorn port (default 9000)APP_MODULE/GUNICORN_APP/WORKER_TARGET: override module names if renamedREDIS_*,PG_*: backing services
entrypoint.sh chooses the command based on APP_TYPE and module vars.
/health: basic liveness, returnsserviceandversion./ready: checks config presence, optional Redis/PG status.- If
REDIS_ENABLED=trueand a Redis client is present, endpoints can be API-key protected (seeutil.decorators.require_apikey).
Seed a key for testing:
redis-cli -h $REDIS_HOST -p $REDIS_PORT -a "$REDIS_PASSWORD" \
HSET "apikey:test-key" customer_id "local" tier "dev" disabled "0"
curl -H "X-API-Key: test-key" http://127.0.0.1:9000/healthStructured JSON to stdout (API and worker). Fields include service, env, file, line, request_id (API), etc., ready for log collectors (Loki/SIEM).
- Werkzeug/Gunicorn access logs remain enabled so HTTP traffic is also emitted as JSON; use
LOG_LEVELfor noise control and override withFLASK_DEBUGwhen you still want the Flask debugger/reloader locally.
poetry run pytest- Extend easily with new routes/worker tasks.
- Add new tests as new features are added.