AWS — NHS Quickstart
Managed services give you TLS by default, autoscaling, and no servers to patch. You can keep apps private with VPC networking and store credentials in Secrets Manager. Use RDS for SQL Server when you need a managed database (or keep using an on‑prem SQL Server via private networking).
Great for: Developer · Data Engineer (services, pipelines) · BI (host thin UI).
⚙️ 10‑minute “hello” deploy (App Runner)
Goal: containerise a tiny API and put it on a public HTTPS URL (lock down later).
0) Minimal API + Dockerfile
- FastAPI (Python)
- Express (Node.js)
from fastapi import FastAPI
app = FastAPI()
@app.get("/health")
def health(): return {"status":"ok"}
@app.get("/hello")
def hello(): return {"message":"Hello, NHS!"}
FROM python:3.11-slim
WORKDIR /app
COPY service/main.py ./main.py
RUN pip install fastapi uvicorn[standard]
EXPOSE 8000
CMD ["uvicorn","main:app","--host","0.0.0.0","--port","8000"]
import express from "express";
const app = express();
app.get("/health", (_req,res)=>res.json({status:"ok"}));
app.get("/hello", (_req,res)=>res.json({message:"Hello, NHS!"}));
app.listen(8000, ()=>console.log("Running on :8000"));
{ "name": "nhs-hello", "type": "module",
"dependencies": { "express": "^4.19.2" },
"scripts": { "start": "node server.js" } }
FROM node:20-alpine
WORKDIR /app
COPY service/package.json service/server.js ./
RUN npm ci --only=production || npm i --only=production
EXPOSE 8000
CMD ["node","server.js"]
1) Build & push to ECR
# variables
REGION=<your-region> # e.g., eu-west-2
REPO=nhs-hello-api
AWS_ACCOUNT=$(aws sts get-caller-identity --query Account --output text)
# create ECR repo (idempotent)
aws ecr create-repository --repository-name $REPO --image-scanning-configuration scanOnPush=true --region $REGION || true
# login and push
aws ecr get-login-password --region $REGION | docker login --username AWS --password-stdin $AWS_ACCOUNT.dkr.ecr.$REGION.amazonaws.com
docker build -t $REPO ./
docker tag $REPO:latest $AWS_ACCOUNT.dkr.ecr.$REGION.amazonaws.com/$REPO:latest
docker push $AWS_ACCOUNT.dkr.ecr.$REGION.amazonaws.com/$REPO:latest
2) Create the App Runner service (Console)
- Create service → Container registry → ECR. Select the image you pushed.
- Port
8000. Health check path/health. - Auto scaling: default is fine for a demo.
- Env vars: add later or from Secrets Manager.
- Deployment: manual; update on each push (or enable ECR auto‑deploy).
You’ll get a public HTTPS URL like https://<random>.<region>.awsapprunner.com/hello.
Lockdown later: attach WAF, restrict to specific IPs, or front with CloudFront; for private DB access, add a VPC connector (egress) to reach RDS/On‑prem via VPN/DirectConnect.
🌐 Add a static React UI (S3 + CloudFront)
Build your app (React/Vite/Evidence.dev) and deploy as a static site.
npm run build # produces dist/ or build/
BUCKET=nhs-hello-ui-<unique-suffix>
aws s3 mb s3://$BUCKET
aws s3 sync dist/ s3://$BUCKET --delete
Create a CloudFront distribution via Console (or with a config file) pointing to the S3 bucket. Set your UI to call the API:
VITE_API_URL=https://<apprunner-url>
🔒 NHS‑ready patterns
- Secrets: store DB/API credentials in Secrets Manager; reference as App Runner environment secrets.
- Database: Amazon RDS for SQL Server (or existing on‑prem SQL Server). Use private subnets, security groups, KMS‑encrypted storage, backups.
- Networking: App Runner VPC connector for egress to RDS/VPN; private subnets for databases.
- Logging/Monitoring: CloudWatch Logs/metrics; alarms on 5xx and latency; retain logs per Trust policy.
- Edge/WAF: CloudFront + WAF for rate‑limits, IP allow/deny; custom headers for simple origin auth.
- Cost control: small containers; turn off auto deploy on big images; lifecycle rules on S3/ECR.
🧭 Alternate deploy route (ECS Fargate with Copilot)
If you need VPC control from day one or multiple services:
# install: https://docs.aws.amazon.com/AmazonECS/latest/developerguide/AWS_Copilot.html
copilot init --app nhs-tools --name api --type "Load Balanced Web Service" --dockerfile ./service/Dockerfile --port 8000
copilot env init --name dev --default-config
copilot deploy --name api --env dev
Copilot sets up an ECS Fargate service + ALB, HTTPS certs, logs, and CI/CD hooks you can extend.
🧪 Health, checks, and DB connectivity
# after deploy
curl https://<apprunner-or-alb-url>/health
# connect to RDS for SQL Server from a bastion/SSMS
# (use SQL authentication; store the password in Secrets Manager)
🛡 IG & safety checklist
- Use private subnets for databases; no public RDS endpoints.
- Keep secrets out of code; rotate via Secrets Manager.
- Avoid writing PHI to logs; enable access logs and retention policies.
- Apply small‑number suppression in any aggregated endpoints.
- Record DPIA reference and system owner in the repo README/runbook.
📏 Measuring impact
- Reliability: uptime, p95 latency, error rate.
- Security: secret rotation cadence; zero committed secrets.
- Cost: monthly cost per service; S3/ECR lifecycle effectiveness.
- Adoption: endpoints used; UI sessions; teams integrated.
🔗 See also
What’s next?
You’ve completed the Learn — AWS stage. Keep momentum: