Skip to content

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.

Terminal window
make local-up

The 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:

Terminal window
curl --fail http://localhost:8000/ready
curl --fail --header 'content-type: application/json' \
--data '{"url":"https://example.test/local"}' \
http://localhost:8000/shorten

Read code from the actual response. Inspect the redirect without following its destination:

Terminal window
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.

Terminal window
make smoke-local

scripts/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.

Terminal window
docker compose logs app postgres redis
docker compose ps

If 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:

Terminal window
make local-down

This 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