ADR-009: PostgreSQL-Backed Session Storage
On this page
Context
BFF services (canopy-web, canopy-portal) require server-side session storage for Keycloak OIDC state, CSRF tokens, and user preferences. The choice of session backend affects reliability, scalability, compliance auditability, and operational complexity.
Options Considered
-
In-memory (MemoryStore) — simplest, zero dependencies. Sessions lost on restart. No horizontal scaling (sticky sessions required). No audit trail.
-
Redis-primary — fast, widely used for sessions. Requires Redis operational expertise. Data loss risk on eviction under memory pressure. No built-in durability guarantees without AOF/RDB persistence tuning. Not queryable for compliance audits.
-
PostgreSQL-primary with Redis cache — PostgreSQL as authoritative store, Redis as optional LRU read-through cache. Sessions survive restarts and Redis eviction. Queryable for compliance audits (IRS Pub 1075 access logging). Horizontally scalable (any replica reads from PostgreSQL). Redis provides sub-millisecond reads for active sessions.
-
Cookie-only (encrypted JWT) — no server state. Limited payload size. Cannot revoke sessions server-side. Logout requires token blocklist (re-introducing server state).
Decision
Option 3: PostgreSQL-primary with Redis LRU cache.
-
tower-sessions-sqlx-store::PostgresStoreas the authoritative session backend -
Redis 7 (Alpine) as an optional LRU cache layer (128 MB maxmemory, allkeys-lru eviction)
-
MemoryStore is banned project-wide
canopy-portal) session storage backend is narrowed to Redis-primary by the separate ADR-026 (its sessions are short-lived, server-side-revocable, opaque tokens for anonymous applicants — not worker sessions). The worker portal (canopy-web) and the PostgreSQL-primary mandate above are unchanged.
Consequences
Positive
-
Sessions survive service restarts, Redis eviction, and Redis outages (graceful degradation to PostgreSQL-only)
-
Session data is queryable via SQL for compliance audits (who accessed what, when)
-
Horizontal scaling without sticky sessions — any service replica reads from PostgreSQL
-
Redis provides fast reads for the hot working set of active sessions
-
PostgreSQL is already a required dependency (no new infrastructure for session storage)