Docker β NHS Quickstart
π³ Containers Β· Reproducible builds Β· App Runner / Azure App Service Β· Secrets Β· Health checks
Why Docker in the NHS
Standardise runtime environments for dashboards, APIs, and services to eliminate βworks on my machineβ. Containers are the hand-off unit for AWS App Runner, Azure App Service/Container Apps, or Kubernetes.
Great for: Developer Β· Data Engineer Β· BI Analyst (with deployment role).
βοΈ 10-minute installβ
- Windows: Install Docker Desktop; enable WSL2 backend.
- macOS/Linux: Install Docker Desktop or your distro package.
- Add your user to the
dockergroup (Linux) and restart your session.
π βHello NHSβ container (FastAPI)β
Folder layout
nhs-kpi-api/
app.py
requirements.txt
Dockerfile
.dockerignore
requirements.txt
fastapi
uvicorn
app.py
from fastapi import FastAPI
app = FastAPI()
@app.get("/")
def hello():
return {"msg": "Hello NHS"}
@app.get("/health")
def health():
return {"status": "ok"}
.dockerignore
__pycache__/
*.pyc
*.pyo
*.env
.venv/
dist/
build/
Dockerfile (simple)
FROM python:3.11-slim
WORKDIR /app
COPY requirements.txt ./
RUN pip install --no-cache-dir -r requirements.txt
COPY . .
ENV PORT=8000
EXPOSE 8000
HEALTHCHECK --interval=30s --timeout=3s CMD curl -f http://localhost:8000/health || exit 1
CMD ["uvicorn", "app:app", "--host", "0.0.0.0", "--port", "8000"]
Build & run
docker build -t nhs-kpi-api .
docker run -p 8000:8000 nhs-kpi-api
# open http://127.0.0.1:8000
π§± Production-ready variantsβ
- Gunicorn (multi-worker)
- Run as non-root
- SQL Server client (optional)
Dockerfile
FROM python:3.11-slim
WORKDIR /app
COPY requirements.txt ./
RUN pip install --no-cache-dir -r requirements.txt gunicorn
COPY . .
ENV PORT=8000
EXPOSE 8000
HEALTHCHECK --interval=30s --timeout=3s CMD curl -f http://localhost:8000/health || exit 1
CMD ["gunicorn","-w","2","-k","uvicorn.workers.UvicornWorker","-b","0.0.0.0:8000","app:app"]
Dockerfile (non-root)
FROM python:3.11-slim
WORKDIR /app
RUN groupadd -g 10001 app && useradd -r -u 10001 -g app app
COPY requirements.txt ./
RUN pip install --no-cache-dir -r requirements.txt gunicorn && mkdir -p /app && chown -R app:app /app
COPY . .
USER app
ENV PORT=8000
EXPOSE 8000
CMD ["gunicorn","-w","2","-k","uvicorn.workers.UvicornWorker","-b","0.0.0.0:8000","app:app"]
Dockerfile (with msodbcsql18)
FROM python:3.11-slim
# Install Microsoft ODBC Driver 18 for SQL Server (Debian-based)
RUN apt-get update && apt-get install -y curl gnupg ca-certificates unixodbc-dev \ && curl https://packages.microsoft.com/keys/microsoft.asc | gpg --dearmor > /usr/share/keyrings/ms.gpg \ && echo "deb [signed-by=/usr/share/keyrings/ms.gpg arch=amd64] https://packages.microsoft.com/debian/12/prod bookworm main" > /etc/apt/sources.list.d/mssql-release.list \ && apt-get update && ACCEPT_EULA=Y apt-get install -y msodbcsql18 \ && rm -rf /var/lib/apt/lists/*
WORKDIR /app
COPY requirements.txt ./
RUN pip install --no-cache-dir -r requirements.txt
COPY . .
ENV PORT=8000
EXPOSE 8000
CMD ["uvicorn","app:app","--host","0.0.0.0","--port","8000"]
requirements.txt should include: sqlalchemy pyodbc python-dotenv.
π§ͺ Docker Compose (local dev)β
compose.yml
services:
api:
build: .
ports: ["8000:8000"]
env_file: .env
restart: unless-stopped
Run with:
docker compose up --build
π Secrets & configurationβ
- Use
--env-file .envorenv_file: .env(Compose) to inject config. - Prefer cloud secret stores in production (Key Vault / Secrets Manager).
- Do not bake secrets into images; keep
.envout of Git.
π¦ Push images (cloud)β
- AWS ECR
- Azure ACR
REGION=<region>
REPO=nhs-kpi-api
AWS_ACCOUNT=$(aws sts get-caller-identity --query Account --output text)
aws ecr create-repository --repository-name $REPO --region $REGION || true
aws ecr get-login-password --region $REGION | docker login --username AWS --password-stdin $AWS_ACCOUNT.dkr.ecr.$REGION.amazonaws.com
docker tag nhs-kpi-api:latest $AWS_ACCOUNT.dkr.ecr.$REGION.amazonaws.com/$REPO:latest
docker push $AWS_ACCOUNT.dkr.ecr.$REGION.amazonaws.com/$REPO:latest
RG=rg-nhs
ACR=nhsregistry
IMAGE=nhs-kpi-api
az group create -n $RG -l uksouth
az acr create -g $RG -n $ACR --sku Basic
az acr login -n $ACR
docker tag $IMAGE:latest $ACR.azurecr.io/$IMAGE:latest
docker push $ACR.azurecr.io/$IMAGE:latest
Deploy using the platform pages: AWS Β· Azure.
π‘ IG & safety checklistβ
- Keep secrets out of images; mount via env or secret stores.
- Avoid PHI in logs; enable request IDs and health checks.
- Run as non-root where possible; pin base images; auto-scan in CI.
- Tag images immutably; use lifecycle policies to clean up old tags.
- Store DPIA reference and system owner in the repo README/runbook.
π Measuring impactβ
- Reliability: container health pass rate; mean time to recovery.
- Consistency: builds reproduce on CI; identical behaviour across machines.
- Security: zero committed secrets; scan findings trend; base image freshness.
- Speed: build time; time to deploy to AWS/Azure.
π See alsoβ
Whatβs next?
Youβve completed the Learn β Docker stage. Keep momentum: