Content Management that can withstand the plundering hoards

Health, metrics, logs and build identity

Viking exposes telemetry without requiring an observability backend. Administrators connect a scraper, log collector and dashboards from their platform. Admin listeners are unauthenticated and must remain inside the trusted network. Monitoring is not proof that every page renders correctly.

Endpoints and what readiness means

Component Default listener /healthz /readyz /metrics
Frontend :8081 Process alive Root resolves to a readable directory Frontend HTTP registry
CMS :8081 Process alive Writable private project, readable project metadata, usable auth manager CMS HTTP/publish registry
Controller :8080 Ping Ping Controller-runtime registry

The controller's probes are pings; they do not aggregate Website health or prove leader status, successful reconciliation, storage binding or publication. Frontend readiness does not require an index.html at /; a readable release directory can still give a homepage 404. CMS readiness does not verify every page or guarantee the next publication will have enough disk space.

Standard probes return JSON and Cache-Control: no-store, for example {"status":"ready","version":"dev"}. Use HTTP status for success/failure; CMS/frontend unavailable readiness is 503. Health status is ok, successful readiness status is ready. Errors can include an error string. The optional controller --health-probe-addr listener uses the native probe format instead of this JSON but still carries the Viking Server header.

To inspect a CMS without exposing admin ports, run from a workstation:

kubectl port-forward -n websites-example deployment/example-cms 18081:8081

In another terminal, use curl -i http://127.0.0.1:18081/readyz or curl http://127.0.0.1:18081/metrics; stop forwarding with Ctrl+C. Use a frontend Deployment instead for frontend telemetry, and forward port 8080 for controller telemetry. Forwarding one Deployment selects one pod, not an aggregate across replicas.

Metrics

Component Metric Meaning
Frontend http_requests_total{method,code} Completed public requests
Frontend http_request_duration_seconds Request duration histogram
Frontend http_response_size_bytes Response size histogram
Frontend http_requests_in_flight Concurrent public requests
CMS http_requests_total{method,code} Requests reaching API/editor instrumentation
CMS http_request_duration_seconds Duration of those instrumented requests
CMS publish_total{result} Publish attempts, success or error
CMS publish_duration_seconds Publish duration histogram
CMS current_release{release} 1 for the currently recorded active release
Controller controller_runtime_*, process/Go metrics Runtime reconciliation/queue and process telemetry; inspect the running endpoint for exact series

Histograms expose _bucket, _sum and _count samples. Frontend/CMS method labels use only GET, HEAD, POST, PUT, DELETE, PATCH, OPTIONS and OTHER. They do not use request URLs or Host as labels. Add namespace/site labels in your scrape configuration instead of assuming they are intrinsic application labels.

Use API counters for requests that reach the editing API; use ingress and identity-provider logs to investigate authentication attempts and login failures. Admin scrapes sit outside ordinary public/API counters. publish_total measures publication attempts; track rollback activity through release status and logs.

Both CMS and frontend use generic HTTP metric names: retain a distinguishing job/component/site label when querying. Scrape each desired pod's internal port using your platform's discovery configuration and monitoring resources. Configure alerting for sustained failed readiness, restarts, reconciliation errors, failed publishing and storage capacity through your existing monitoring stack. Choose thresholds from your workload.

Running application version

The editor shows the CMS version; authenticated GET /api/version returns {"version":"dev"} for an unstamped build. Standard health JSON also includes version. Every listener returns Server: Viking <Component> <version>: CMS, Frontend or Controller, followed by the binary's build-stamped version. This includes successful and rejected requests, OAuth redirects, metrics, probes and unknown paths. A site content release such as 000003 and a theme version such as 1.0.0 are different identifiers.

Check the running image's header, health response or API value when verifying an upgrade. The editor displays the backend version; image tags and the editor's package version are separate identifiers.

Ingress/reverse proxies must preserve the upstream Server header. If an external response advertises a proxy or library, compare a direct response to determine whether the proxy is replacing the upstream header. Configure the proxy to preserve the component identity.

Logs and issue evidence

CMS and controller use JSON production logs; frontend writes JSON access logs to stdout and operational logs to stderr. Access records include method, path, status, duration and other component-specific fields. CMS API logs do not represent every authentication response for the same layering reason as metrics. Use Kubernetes container logs and ingress/provider logs together for login issues.

Frontend --trust-proxy enables forwarded client addresses only when every request comes through a trusted proxy which replaces client-supplied forwarding headers. The CMS does not expose that frontend flag. Never enable proxy trust on a publicly reachable pod merely to improve log addresses.

When escalating, capture component/version, Website namespace/name, time, request path/status, release ID and sanitized relevant logs. Do not include session cookies, access tokens, Secret data or database passwords. Storage snapshots and published-release history require separate retention from logs; see backup/restore.