Files
Cashow-error-pages/README.md
T
2026-08-17 19:30:25 +00:00

5.7 KiB
Raw Blame History

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:

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:

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.

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

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:

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:

    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.