Traffic flow
flowchart LR
DNS["lomavuokraus.fi\nstaging.lomavuokraus.fi\napi.lomavuokraus.fi"] --> Traefik["Traefik ingress\n(class: traefik)"]
User["User browser"] -->|"HTTPS"| Traefik
CertMgr["cert-manager\nletsencrypt prod/staging"] -->|"TLS"| Traefik
subgraph Cluster["k3s hel1 cx23 (157.180.66.64)"]
Traefik --> Service["Service :80 -> 8080"]
Service --> Varnish["Varnish cache\n(static + /api/images/*)"]
Varnish --> Pod["Next.js pods (2)\n(port 3000)"]
Pod --> DB["PostgreSQL 46.62.203.202"]
Pod --> SMTP["smtp.lomavuokraus.fi"]
Secret["Secret: lomavuokraus-web-secrets"]
CM["ConfigMap: lomavuokraus-web-config"]
end
Registry["registry.halla-aho.net/thalla/lomavuokraus-web"] -->|"pull"| Pod
Mermaid renders directly in the browser; edit the graph in this file
to update.
Hetzner nodes
flowchart TB
Users["Users"] -->|"HTTPS"| K3s["Node A: k3s (hel1 cx23)\nTraefik + cert-manager"]
subgraph HetznerCloud["Hetzner Cloud"]
K3s
DB["Node B: Postgres VM\n46.62.203.202"]
end
subgraph Prod["Prod namespace"]
Prod1["Next.js pod #1 (prod)"]
Prod2["Next.js pod #2 (prod)"]
end
subgraph Staging["Staging namespace"]
Stg1["Next.js pod #1 (staging)"]
Stg2["Next.js pod #2 (staging)"]
end
K3s --> Prod1
K3s --> Stg1
Prod1 --> DB
Prod2 --> DB
Stg1 --> DB
Stg2 --> DB
Cluster & Namespaces
-
Single-node k3s (Hetzner hel1 cx23) at
157.180.66.64. -
Namespaces:
lomavuokraus-prod,lomavuokraus-staging,lomavuokraus-test. - Ingress controller: Traefik (k3s default).
-
cert-manager v1.15.3 with ClusterIssuers:
letsencrypt-prod(ACME prod)-
letsencrypt-staging(ACME staging for test certs)
-
Service points to a Varnish sidecar (port 8080) in each pod before
the Next.js container (3000) to cache
/api/images/*and static assets. -
Cache policy: images cached 24h with
Cache-Control: public, max-age=86400, immutable;_next/staticcached 7d; non-GET traffic and health checks bypass cache. -
DNS:
lomavuokraus.fi,staging.lomavuokraus.fi,api.lomavuokraus.fi-> cluster IP.
Registry
-
Private registry:
registry.halla-aho.net/thalla/lomavuokraus-web. -
Credentials stored outside repo (
creds/), image pull secretregistry-hallain staging/prod namespaces. -
Images tagged with git SHA-derived numeric tag and
:latest.
App Manifests
-
k8s/app.yamltemplated via envsubst in deploy scripts. -
Objects:
-
ConfigMap:
lomavuokraus-web-config(public env). -
Deployment: 2 replicas, Varnish sidecar on port 8080 in front of
the Next.js container (3000), liveness/readiness on
/api/healthvia Varnish. - Service: ClusterIP on port 80 targeting the Varnish container.
- Ingress: Traefik class, TLS via cert-manager, HTTPS redirect middleware.
-
Traefik Middleware:
https-redirectto force HTTPS.
-
ConfigMap:
-
Secrets:
lomavuokraus-web-secretsin cluster (not in repo).
Runtime Environment
- Next.js 14.2.33 (App Router) running via Node.js 20 in Docker.
-
PostgreSQL at
46.62.203.202; staging/prod DBlomavuokrausis clean with only the seeded admin, testing DBlomavuokraus_testingholds the previous data. Schema snapshot tracked indocs/db-schema.sql. -
SMTP: smtp.lomavuokraus.fi (CNAME to smtp.sohva.org), DKIM key under
creds/dkim/.... -
Session auth: signed JWT cookie
session_token; roles: USER, ADMIN, USER_MODERATOR, LISTING_MODERATOR.
Emergency shutdown
-
Script:
scripts/emergency-shutdown.shissues Hetzner poweroff/shutdown commands for tracked nodes (currentlynode1.lomavuokraus.fianddb1.lomavuokraus.fi). -
List tracked nodes:
./scripts/emergency-shutdown.sh --list. -
Hard stop everything:
./scripts/emergency-shutdown.sh --yes --confirm "SHUTDOWN ALL LOMAVUOKRAUS NODES NOW"(default action ispoweroff; use--action shutdownfor ACPI). -
Requires
hcloudCLI withHCLOUD_TOKENor configured at~/.config/hcloud/cli.toml; keep the node list updated when adding servers.