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.