Architecture
Stack Layers
| Layer |
Technology |
Role |
| Gateway |
NGINX |
Single public entrypoint (:8000), routing + auth checks |
| Auth/Admin |
FastAPI + Jinja |
Login/logout, session checks, user CRUD, role CRUD |
| RBAC |
Roles + role-app mapping |
Restricts which users can open which Shiny apps |
| Persistence |
PostgreSQL 16 |
Users, roles, role grants, user-role bindings, sessions |
| App 1 |
R Shiny + Plotly |
Sample analytics app at /rlang-app |
| App 2 |
Python Shiny + Plotly/Pandas |
Sample analytics app at /python-app |
| Orchestration |
Docker Compose |
Service lifecycle and network |
Container Topology
flowchart LR
browser["Browser"] --> gateway["gateway (NGINX) :8000"]
gateway --> auth["auth-admin (FastAPI) :8080"]
gateway --> rshiny["r-shiny :3838"]
gateway --> pyshiny["python-shiny :3839"]
auth --> pg["postgres :5432"]
Routing Model
/auth/* and /admin/* -> auth-admin
/rlang-app/* -> r-shiny (after successful auth check)
/python-app/* -> python-shiny (after successful auth check)
- Auth outcomes at gateway:
401 -> redirect to /auth/login?next=<path>
403 -> serve /auth/forbidden
200 -> proxy request to app
Health and Startup Model
- Each app service has a healthcheck.
gateway waits for healthy dependencies before startup.
init: true and graceful stop periods are configured for cleaner process lifecycle.