Files
2026-08-17 19:30:25 +00:00

182 lines
5.7 KiB
Markdown
Raw Permalink Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# Cashow error pages
Branded static error pages served by Traefik in front of the LNbits instances,
replacing Traefik's default plain-text bodies (`no available server`,
`Internal Server Error`).
```
deploy/error-pages/
├── README.md you are here — not deployed
├── preview.html local review grid — not deployed
└── pages/ ← the ConfigMap is built from this directory only
├── 429.html
├── 500.html
├── 502.html
├── 503.html
├── 504.html
└── onderhoud.html
```
`README.md` and `preview.html` sit *outside* `pages/` on purpose: the ConfigMap
is built with `--from-file=.`, which would otherwise sweep them both in as keys.
## Design constraints
Each page is a single self-contained HTML file making **zero external requests** —
inline `<style>`, inline `<script>`, the logo inlined as a base64 `<image>` inside
an `<svg>`. This is not a preference. Traefik's `errors` middleware fetches only
the HTML document from the error service and serves that body on the *original*
domain, so any relative subresource (`/logo.svg`, `/style.css`) would resolve
against the LNbits instance that is currently down and 404.
System font stack throughout — no webfont, embedded or otherwise.
| file | size |
|---|---|
| `500.html` | 20.7 KB |
| `429.html`, `502.html`, `503.html`, `504.html`, `onderhoud.html` | 22.1 KB each |
| **total** | **131 KB** |
`500.html` is smaller because it has no retry block, and therefore ships no
countdown JS, no `<noscript>` refresh and none of the rail CSS.
About 16 KB of every page is the base64 logo. Budget is 1 MB (ConfigMap hard cap),
so there is room, but keep it in mind before adding anything per-page.
## Regenerate the ConfigMap
From `deploy/error-pages/pages`:
```bash
kubectl -n kube-system create configmap error-pages --from-file=. --dry-run=client -o yaml | kubectl apply -f -
```
The pod serving these mounts the ConfigMap; if it is mounted as a volume the
kubelet refreshes it within a minute or so, but restart the deployment if you
want the change immediately:
```bash
kubectl -n kube-system rollout restart deploy/error-pages
```
## Serving pod
The `errors` middleware needs a real backend to fetch pages from. Anything that
serves static files works; this is the smallest version.
```yaml
apiVersion: apps/v1
kind: Deployment
metadata:
name: error-pages
namespace: kube-system
spec:
replicas: 1
selector:
matchLabels: { app: error-pages }
template:
metadata:
labels: { app: error-pages }
spec:
containers:
- name: nginx
image: nginxinc/nginx-unprivileged:alpine
ports:
- containerPort: 8080
volumeMounts:
- name: pages
mountPath: /usr/share/nginx/html
readinessProbe:
httpGet: { path: /503.html, port: 8080 }
volumes:
- name: pages
configMap:
name: error-pages
---
apiVersion: v1
kind: Service
metadata:
name: error-pages
namespace: kube-system
spec:
selector: { app: error-pages }
ports:
- port: 80
targetPort: 8080
```
## Middleware
```yaml
apiVersion: traefik.io/v1alpha1
kind: Middleware
metadata:
name: error-pages
namespace: kube-system
spec:
errors:
status:
- "429"
- "500-599"
service:
name: error-pages
port: 80
query: "/{status}.html"
```
`query` substitutes the numeric status directly, which is why the filenames must
be exactly `{status}.html`. A status in the range with no matching file (`501`,
`507`, …) gets a 404 from the error service and Traefik falls back to its own
plain body — so the six files cover the codes that actually occur, and anything
else degrades to today's behaviour rather than breaking.
Attach it to an instance ingress with the usual annotation:
```yaml
traefik.ingress.kubernetes.io/router.middlewares: kube-system-error-pages@kubernetescrd
```
> **Cross-namespace reference.** The middleware lives in `kube-system` while the
> instance ingresses live in their own namespaces. Traefik allows this by default,
> but if `providers.kubernetesCRD.allowCrossNamespace=false` is set the reference
> is silently ignored and you get the default error bodies back. Either enable it
> or copy the Middleware object into each instance namespace.
## No 404 page
Deliberate. Traefik does not run the middleware chain when no router matches, so
a 404 page would only ever fire for LNbits-generated 404s. Not worth the file.
## Planned maintenance
`onderhoud.html` is not wired to a status code — it is swapped in by hand.
Repoint the middleware query for the duration of the window:
```yaml
query: "/onderhoud.html"
```
Apply it, and every 5xx from the instances renders the maintenance page instead
of the per-status ones. Revert to `query: "/{status}.html"` when the window ends.
This only changes what is *rendered*. Traefik still returns the upstream status
code, so `onderhoud.html` goes out as a 503 for as long as the instances are
down — which is the correct signal for crawlers and uptime checks.
## Open TODOs
Both are marked in the HTML.
- **Logo.** Currently `www.cashow.be/img/cashow_extrasmall.png` base64-inlined —
a 250×250 raster, not a vector. Drop `assets/cashow-logo.svg` in the repo and
swap the marked `<!-- ==== CASHOW LOGO ==== -->` block in all six files; it is
byte-identical across them, so it is one find-and-replace.
- **Status host.** The ghost pill links to `https://status.cashow.be`, which did
not resolve as of 2026-08-17 (NXDOMAIN). Point it at the real status page or
remove the pill.
## Local review
Open `preview.html` in a browser to see all six side by side. It is a review aid
only and is not part of the ConfigMap.