diff --git a/README.md b/README.md index 1f72126..06fca65 100644 --- a/README.md +++ b/README.md @@ -6,6 +6,43 @@ Staging contains an unchanged copy of production todo data. Treat staging hosts, ## Architecture +```mermaid +flowchart TB + client([HTTP clients]) + + subgraph cloud[Hetzner Cloud] + lb[Hetzner Load Balancer] + + subgraph network[Private Network 10.0.0.0/16] + subgraph prod[Production VM - Rocky Linux 10] + prod_app[Go App
Rootless Podman] + prod_db[(PostgreSQL
127.0.0.1:5432)] + prod_app -->|Local connection| prod_db + end + + subgraph staging[Staging VM - Rocky Linux 10] + staging_app[Go App
Rootless Podman] + staging_db[(PostgreSQL
127.0.0.1:5432)] + staging_app -->|Local connection| staging_db + end + end + + storage[(Hetzner Object Storage
S3-compatible bucket)] + end + + client -->|Port 80| lb + lb -->|10.0.1.10:8080| prod_app + prod_db -->|WAL archive and hourly base backup| storage + storage -->|Restore LATEST at 06:00 and 12:00| staging_db + + classDef production fill:#fee2e2,stroke:#b91c1c,color:#7f1d1d,stroke-width:2px; + classDef stagingEnv fill:#dbeafe,stroke:#1d4ed8,color:#1e3a8a,stroke-width:2px; + class prod_app,prod_db production; + class staging_app,staging_db stagingEnv; + style prod fill:#fff1f2,stroke:#b91c1c,stroke-width:2px + style staging fill:#eff6ff,stroke:#1d4ed8,stroke-width:2px +``` + - The load balancer accepts HTTP on port 80 and reaches production on `10.0.1.10:8080` over the private network. - The Go application runs as the rootless `todo` user through Quadlet-generated `app-todo.service` with host networking. - PostgreSQL listens only on `127.0.0.1:5432`; the cloud firewall does not expose it.