Local development and unpaid smoke
A local stack lets you follow URL creation, persistence, redirect lookup, and Redis fallback without a cloud account or wallet. It is the shortest way to observe the application’s dependency contract. Ordinary Compose runs unpaid; the separate payment override and opt-in payment script have a different scope.
Use Python 3.12 for source work, Docker with Compose for the stack, and curl for the smoke script. First run source validation if the checkout or dependencies have changed.
Start an interactive stack
Section titled “Start an interactive stack”make local-upThe Compose definition builds the application and starts PostgreSQL and Redis. The app is exposed at http://localhost:8000; PostgreSQL uses host port 5433 and Redis uses 6380 to reduce conflicts with common local services. These are development credentials and disposable local volumes, not a template for a public deployment.
Wait for readiness, then create one unpaid short URL:
curl --fail http://localhost:8000/readycurl --fail --header 'content-type: application/json' \ --data '{"url":"https://example.test/local"}' \ http://localhost:8000/shortenRead code from the actual response. Inspect the redirect without following its destination:
curl --dump-header - --output /dev/null \ http://localhost:8000/<returned-code>The response should identify a Location matching the submitted URL. This is an expected contract, not output from a recorded run. Use the real returned code; an invented code will exercise the not-found path.
Exercise the maintained smoke
Section titled “Exercise the maintained smoke”make smoke-localscripts/smoke-local.sh builds and starts the stack, waits up to thirty readiness attempts with two-second pauses, creates a URL, and checks its redirect header. It stops Redis, checks /livez and steady-state /ready, starts Redis again, then removes the Compose project and volumes through its exit trap. It also tears down after a failure. Use it only with disposable data in this project.
The Redis checks matter because health endpoints answer different questions. /livez proves the app process responds without querying dependencies. /ready requires PostgreSQL throughout the pod lifetime. Redis is required for the first successful readiness check; afterward its loss causes cache operations to fall back to PostgreSQL rather than making the app unready or triggering a dependency-driven restart loop.
The smoke script checks liveness and readiness while Redis is stopped. Its redirect-header check occurs earlier, so that script alone does not prove a redirect completed during the Redis outage. The fallback behavior has separate cache tests and a marked recovery contract. This distinction keeps a convenience smoke from carrying a broader claim than its actual sequence.
Investigate before restarting
Section titled “Investigate before restarting”docker compose logs app postgres redisdocker compose psIf initial readiness fails, inspect PostgreSQL connectivity and the Redis startup requirement. If steady-state readiness fails after Redis alone is lost, compare the observed behavior with health tests. If URL creation fails while readiness is good, follow the application error response and database logs rather than assuming the cache caused it.
Avoid treating ordinary cache misses as outage evidence. A miss can occur during normal lookup of an uncached URL. Read dependency errors, latency, and recovery together; a single counter does not identify the fault.
For an interactive stack, finish with:
make local-downThis removes the project’s volumes. The local smoke supports the local container and request contract. Kubernetes targeting, paid useful traffic, bounded fault injection, telemetry scoring, cleanup, and downstream eligibility require the separate promotion and verification path.
Maintained by Satyam Agnihotri · DevOps & Cloud Engineer