The ground-floor reference implementation for the k3s-pi5 DevOps learning platform — a complete three-tier architecture pairing a Node.js frontend, a .NET API gateway, and a PostgreSQL database.
Everything below is an encyclopedia entry for this reference stack — the why behind the API choice, the architecture, the three progressive scenarios, and the patterns that recur throughout the curriculum. Expand any section to dig in.
Every DevOps curriculum needs a sample application — something real enough to demonstrate production patterns, but simple enough that the infrastructure stays the focus, not the app logic. The Rick and Morty API hits a rare sweet spot.
What makes it ideal for a learning platform
| Property | Why it matters |
|---|---|
| Free & public | No API keys, no billing, no sign-up. Focus on architecture, not credential management. Every example is reproducible without registering for anything. |
| REST + GraphQL | Teaches both paradigms. The reference uses REST for simplicity, with GraphQL as a natural extension. |
| Stable data | Production ended in 2022 — canonical, no drift, no breaking changes. Perfect for caching demos and CI/CD integration tests. |
| Predictable paging | 20 results per page, 42 pages of characters. Ideal for demonstrating pagination and resilient HTTP clients. |
| Relational data | Characters link to episodes, locations have residents, episodes have cast lists — a natural fit for SQL joins, junction tables, and full-text search. |
| Memorable | DevOps docs are often dry. The Rick and Morty framing gives every component a personality — Rick is the .NET layer, Morty the Node.js frontend, the garage is PostgreSQL. |
What this is not
- Not a production app. A reference architecture for learning — it demonstrates production patterns but stays intentionally minimal.
- Not a fan site. The show is the theme, not the product. The product is the infrastructure patterns.
- Not the only sample. This is the first reference; later modules add sample apps that build on the same patterns.
The reference implements a 3-tier architecture with a .NET API gateway as the central orchestrator. Every data request from the frontend passes through the .NET layer — never directly to the upstream API or the database.
Key architectural decisions
| Decision | Rationale |
|---|---|
| .NET as orchestrator | The author's primary stack is .NET/C#. The gateway pattern maps cleanly to ASP.NET Core middleware — caching, rate limiting, auth, and logging are all first-class concerns. The same patterns work in Express, Go, or Python; .NET is the example, not the requirement. |
| PostgreSQL over SQLite | SQLite is simpler but teaches nothing about connection strings, network isolation, or persistent volumes — exactly the skills you need for Kubernetes StatefulSets. |
| Node.js frontend, not Blazor | A polyglot stack demonstrates cross-service communication. The Node app must handle HTTP clients, errors, and retries — universal skills independent of language. |
| Cache-aside, not write-through | The data is read-heavy and changes rarely. Cache-aside (check → miss → fetch → store → return) is the simplest pattern that still teaches invalidation and TTL management. |
| Fixed-window rate limiter | Simple to grasp, built into .NET 8 with zero extra packages, and produces clear 429s with Retry-After headers. Token-bucket would be more precise but adds complexity without learning value here. |
The reference is presented as three progressive scenarios. Each adds a layer without removing the previous one — you see why each layer exists before it's introduced.
VM2 proxies straight to the upstream API. Teaches HTTP client basics, JSON deserialization, and API proxying. Best for local dev and freshness-critical demos.
Cache-aside with PostgreSQL as an L2 store. Teaches TTL management, connection pooling, and reducing upstream calls. The production-like default.
Each VM maps to a Kubernetes resource — Deployments, Services, Ingress, StatefulSets, PVCs, NetworkPolicies. The target architecture for everything beyond local dev.
The pedagogical thread
Scenario A is the "hello world" — it proves connectivity. Scenario B adds
caching and introduces the database. Scenario C maps the whole thing to
Kubernetes. Each step is small enough to understand in isolation, but together
they cover the full journey from localhost to a production-shaped
homelab cluster.
Every pattern in this reference recurs in later k3s-pi5 modules. Understanding them here means you recognise them everywhere.
1. Cache-Aside Pattern
Client → API → Cache? ──hit──→ return cached
│
miss
│
▼
Upstream API → Store in cache → return
Where it recurs: M2 Docker Compose (.NET + Redis), M3 Sample Apps (Node.js + Postgres on k3s).
2. Rate Limiting (Fixed Window)
Request → RateLimiter middleware
│
├─ within limit → controller action
│
└─ exceeded → 429 Too Many Requests + Retry-After header
Where it recurs: M3 Security (NetworkPolicies + RBAC), M4 Observability (Prometheus metrics on 429 counts).
3. Health + Metrics Endpoints (un-rate-limited)
GET /health → { "status": "healthy" } ← k8s liveness/readiness
GET /metrics → { "requests_served": 1423, ... } ← Prometheus scrape
Where it recurs: Every module that deploys to k3s. M4 Observability replaces the stub with real Prometheus metrics.
4. Environment-Driven Configuration
Every tunable — cache TTL, rate-limit threshold, connection string — is an environment variable. No magic numbers in code. This convention is non-negotiable and enforced across every k3s-pi5 module.
The .NET API enforces rate limiting using ASP.NET Core 8's built-in
System.Threading.RateLimiting — zero extra NuGet packages.
Why it matters in this project
- Protects the upstream API. The Rick and Morty API has no auth and no documented rate limit. Being a good API citizen means self-throttling.
- Prevents runaway frontends. A buggy React effect loop could fire thousands of requests; the limiter catches it at the gateway before it reaches the DB or upstream.
- Demonstrates middleware composition. Rate limiting is early in the pipeline — it shows how ASP.NET Core middleware order matters: auth, rate limiting, caching, then the controller.
Policy design
| Policy | Limit | Window | Queue | Applies to |
|---|---|---|---|---|
fixed |
100 requests | 60 seconds | 0 (reject immediately) | All /api/* endpoints |
| none | Disabled via [DisableRateLimiting] |
/health, /metrics |
||
Health and metrics endpoints are deliberately excluded — Kubernetes probes fire every 10 seconds and Prometheus scrapes every 15 seconds; throttling either would trigger false-positive pod restarts.
Full implementation: see the .NET Proxy Controller section in the README.
This tracker is the canonical source of truth for iteration progress. Completed items move to Done, and their stable content lives in the README.
| # | Item | Status | Notes |
|---|---|---|---|
| 1 | Scenario A — Direct Fetch | ✅ Done | Passthrough proxy, zero DB dependency |
| 2 | Scenario B — .NET Proxy + PostgreSQL Cache | ✅ Done | Cache-aside pattern, configurable TTL |
| 3 | Scenario C — Containerized on k3s | ✅ Done | VMs → Kubernetes mapping |
| 4 | Rate limiter on .NET API | ✅ Done | Fixed-window, 100 req/min per IP |
| 5 | Docker Compose quick-start | ✅ Done | Single-command local dev |
| 6 | Monitoring & backups | ✅ Done | Health checks, pg_dump cron |
| 7 | Curriculum mapping | ✅ Done | README concepts → site modules |
| 8 | CI/CD pipeline (Jenkins) | ✅ Done | Self-hosted on Vagrant VMs (master + agents) |
| 9 | Config management (Ansible) | ✅ Done | Agentless playbooks deploy the apps |
| 10 | Monitoring (Nagios Core) | ✅ Done | Check-based monitoring + alerting |
| 11 | Cloud reach (AWS free tier) | ✅ Done | EC2, IAM + CLI, ECS |
| 12 | Add Redis L1 cache | ⏳ Planned | Sub-millisecond cache layer |
| 13 | Auth (JWT + API keys) | ⏳ Planned | Protect VM2 endpoints |
| 14 | Observability (Prometheus + Grafana + Loki) | 🔭 Optional | Richer metrics/logs/dashboards |
| 15 | Infrastructure as Code (Terraform) | 🔭 Optional | Provision cloud resources declaratively |
| 16 | GitOps (ArgoCD or Flux) | 🔭 Optional | App-of-apps; reconcile from Git |
This reference is the first concrete example in the k3s-pi5 learning platform. Every concept here maps to one or more curriculum modules:
| Reference concept | k3s-pi5 module(s) | What you learn |
|---|---|---|
| VM provisioning (VM1/2/3) | Virtual Machines & Provisioning | Vagrant, Parallels/VMware, Ansible |
| Manual service startup | DevOps Intro | The "before containers" baseline |
| Docker Compose | Standalone Containers | Dockerfiles, multi-stage builds, Compose |
| .NET API + PostgreSQL | Sample Apps (k3s) | ConfigMaps, Secrets, Ingress on k3s |
| PostgreSQL schema + PVC | Persistent Storage | StatefulSets, PVCs, storage classes |
| Rate limiting | Security | NetworkPolicies, RBAC, Secrets rotation |
| Health checks + metrics | Monitoring (Nagios Core) | Check-based monitoring + alerting |
| Scenario C (k3s) | K3s on Pi 5 | Cluster setup, Cloudflare Tunnel, cert-manager |
| Build → test → deploy | CI/CD (Jenkins) | Self-hosted pipelines on Vagrant VMs |
| Cloud reach | Cloud (AWS, free tier) | EC2, IAM + CLI, ECS |
Recommended learning path
- Read this encyclopedia page — understand the architecture, decisions, and patterns.
- Read the README — study the full code, env vars, and deployment options.
- Follow the k3s-pi5 modules in order on the main site.
- Return here whenever you meet a pattern you recognise — cache-aside, rate limiting, health checks — and see how it fits the larger architecture.
The Rick and Morty framing isn't just decorative — it gives every component a personality that makes the stack easier to reason about.