ZopDay Observability Now Reads From a Backend You Choose
A VM’s Observability tab used to answer one question: is the container still running. Not what it did five minutes ago. ZopDay adds that history: a VM space, and a ZopCloud shared space, can now send its metrics to a backend the customer already owns: VictoriaMetrics, Grafana Mimir, or Prometheus. It reads that backend back into a new Usage section: CPU, memory, network, and disk activity, charted over windows from 15 minutes to 24 hours.
Monitoring only earns its name once the data behind a status badge can answer what happened, not just whether something is currently broken. An agent now scrapes every 30 seconds. Connecting a backend is opt-in per space: register it once in Settings → Integrations, attach it to a space, install its agent, and the Usage charts start filling in.
A Backend Isn’t Trusted Until It Proves It Can Answer
Saving a metrics integration doesn’t just check that a URL and a credential parse. Both the test action and the actual save run the identical probe: write one sample to the backend, then immediately try to read it back with a range query. The write has to succeed outright. A read that comes back well-formed but doesn’t yet show the sample, because most metrics stores hold back roughly the newest 30 seconds before it’s queryable, is treated as a note rather than a failure. A read that’s outright rejected is a warning. Either way, the integration only saves once the write side is proven.
The probe client that runs this check is built to be SSRF-safe rather than merely functional: HTTPS only, no HTTP redirects followed, DNS re-resolved at the moment of the actual connection rather than trusted from an earlier lookup, private IP ranges refused outright, and every allowed address tried IPv4 first, so a backend reachable over both protocols doesn’t fail just because the path lacks IPv6. Deleting an integration is refused with a 409 response that names the exact spaces still pointing at it, rather than silently orphaning them.
One Agent Component, Three States
Attaching a backend to a space installs a new deployer component, observability-agent: a pinned OpenTelemetry Collector build, capped resource use at a quarter of a CPU core and 512 MB of memory with its own internal 400 MiB limiter, scraping host and container metrics every 30 seconds and remote-writing them to whichever backend the space has attached. That component carries labels for the org, the space, the VM, the cloud provider, the container, and the service on every point it ships, so a query can filter down to exactly one container without guessing at naming conventions.
Attach, switch to a different backend, and detach are the same underlying operation from the deployer’s point of view: one re-apply of the observability-agent component with a different configuration rendered into it. With nothing attached, that configuration sets the agent’s mode to remove, which tears the component back out cleanly rather than leaving it running and pointed at nothing. Deploys are pull-first with staged rollback if anything in the re-apply fails, so switching backends never leaves a VM stuck mid-transition.
| Space state | What observability-agent does |
|---|---|
| No backend attached | Mode set to remove; component torn out, VM behaves exactly as before this feature |
| Backend attached | Scrapes host and container metrics every 30s, remote-writes with full label set |
| Backend switched | One re-apply with new config; no intermediate unconfigured state |
Reading Live Data Without Trusting a Stale Cache
The read side follows a backend-first path. A request for a VM’s metrics first resolves which backend that space has attached, a lookup cached for 60 seconds, then runs a single PromQL range query filtered to that VM and, when asked, to one container. A host-level data point newer than 90 seconds counts as a liveness signal alongside the actual metric values. Only if that whole path comes up empty does the request fall back to the older exec-based method of asking the VM directly.
The part worth noticing is what never gets cached and what does. A successful backend answer is never cached at all, since the backend is the customer’s own store and is assumed to be fast enough to query live. The exec-path fallback is cached, but its cache key carries a flag for whether a backend is currently attached. That one detail prevents two specific failures: an exec answer cached moments before a backend was attached would otherwise keep being served after attach, and an exec answer taken only because the backend briefly looked stale is capped at a 60-second cache life so it can’t quietly become the permanent answer once the backend recovers.
The Numbers Behind the Usage Charts
A dedicated per-container endpoint backs the Usage charts directly: roughly 120 data points per requested range, whether that range is 15 minutes or 24 hours, covering CPU (scaled against its own cap), memory, network, and block I/O, with the underlying counters converted into a rate expressed in bytes per second rather than left as raw cumulative totals. The endpoint only ever serves from a connected backend; a VM with none attached gets a 404, not an empty or estimated chart.
The whole path was verified live on a GCP VM against three real backends: VictoriaMetrics 1.150.0, Grafana Mimir 2.14 (with its tenant header and bearer auth configured), and Prometheus 2.55. Across all three, the sequence of register, test, attach, install, samples arriving with the full label set, and the tab actually reading that data back, completed in 0.3 to 0.9 seconds end to end. Detaching removed the agent and dropped the tab cleanly back to the exec path, with the VM’s own container runtime, reverse proxy, and scale-to-zero behavior undisturbed the entire time.
| Backend verified against | Auth configuration tested |
|---|---|
| VictoriaMetrics 1.150.0 | Default, no tenant header |
| Grafana Mimir 2.14 | Tenant header (X-Scope-OrgID) plus bearer auth |
| Prometheus 2.55 | Default |
Shipping the Fix for the Rough Edges the Same Week
A follow-up release, shipped the same week, closed three interface issues that only showed up once the feature was actually used. The install dialog had stayed open with a spinner for the full length of the deployer’s install run, which takes minutes; it now closes as soon as the space’s own update lands, with the install itself continuing in the background and its progress moved onto the Components card’s own button instead. A failed update still keeps the dialog open with an error, so a failed attempt is never silently lost. The backend picker’s dropdown panel could render below the install dialog’s own bottom edge on smaller screens; it’s now positioned to flip above its trigger and stay within the visible viewport instead. And the Usage charts show a loading skeleton while data is being fetched, instead of a blank space with nothing to indicate anything was happening.
128 tests pass across the affected Observability and infrastructure settings suites, 5 of them written specifically to pin these three fixes in place. This works when a space already has a backend attached and just needs its agent reinstalled. It falls straight back to the exec path, by design, the moment that backend is detached, so nothing about the Usage tab depends on a backend staying reachable forever. Shipping a same-week correction isn’t a sign the first release was rushed. It’s what watching real usage immediately after a release is supposed to produce. Connect a backend in Settings → Integrations and try it on your own VM space.
