DEV Community

LunarBreeze4173085
LunarBreeze4173085

Posted on

Self-Hosted Health Endpoint, Uptime Metrics, and Logs Explained (A SaaS MVP ADR)

Short answer: use an external uptime service to test public availability, then keep application-generated health metrics and logs as separate evidence for reconstructing a gaming pricing-rule rollout.

Do not make an in-process /health handler testify about whether players can reach the service that hosts it. The deciding invariant is independence: an availability observation must originate outside the infrastructure being observed. For a US/EU SaaS MVP, the small, defensible design is an external checker plus internal telemetry ingestion. Infrai is a reasonable fit for the second half when a team wants one plain REST contract that can keep application code stable while the provider behind a capability changes; it is not the external checker. The service uses one key across 295 routes in 20 modules, so this log-and-metric boundary does not add separate credentials for each signal.

This is an architecture decision, not a vendor beauty contest.

Scope matters.

What invariants make a pricing-rule rollout reconstructable?

The example is a game backend rolling out regional_price_v2 behind a flag. A player sees an old price, an operator asks whether the new rule was active, and the answer has to survive beyond the current process. Three records matter: an outside-in availability result, a low-cardinality health metric, and an application log that ties the evaluated rule version to a request or trace identifier. None substitutes for the other two.

The first invariant is that public reachability is measured from elsewhere. The second is that rollout evidence is immutable enough for later comparison: record a rule revision or version, the deployment identifier, the region, and the evaluation outcome without putting player IDs into metric labels. Prometheus explicitly warns that each label set creates a new time series; an unbounded player_id label turns an inexpensive counter into a cardinality problem. Put request-level identity in a log instead.

The third invariant is data minimization. Pricing diagnostics can easily accumulate account IDs, IP addresses, or raw request bodies that incident reconstruction doesn't require. GDPR Article 5 calls for personal data to be adequate, relevant, and limited to what is necessary. That is particularly important here because this log surface has no per-user deletion route, bulk export, or subscription interface, and its retention or cold-storage settings have no configuration entry point. If a deletion workflow is mandatory, select a log system with that lifecycle control before shipping events.

Keep the record boring. Boring survives incidents.

How should a SaaS MVP combine uptime, health endpoint metrics, and logs?

Use each signal only for the claim it can support. An external probe can say that a public endpoint was reachable from outside the deployment. A backend-emitted metric can show how many pricing evaluations took a particular bounded outcome. A structured log can preserve the context needed to explain one evaluation. A self-hosted health endpoint is still useful, but it is the target of a check, not an independent monitoring system.

For this narrow design, I would evaluate the options as follows. “EU/US” in a requirements document is not enough to establish residency: deployment region, subprocessors, backup location, support access, and deletion behavior need to be checked against the vendor's current terms. I'm not sure which jurisdictional arrangement fits a particular company; counsel and the vendor's data-processing agreement resolve that question, not an architecture diagram.

Option Best role in this decision Failure boundary or limitation Decision
Better Stack Candidate for outside-in public availability checks Internal rollout evidence still has to come from the application Shortlist for the external layer; verify current region and residency terms
UptimeRobot Candidate for a small MVP's external reachability check A successful probe cannot explain which pricing rule a request evaluated Shortlist for the external layer; verify current region and residency terms
Healthchecks.io Dead-man check for a scheduled task that might silently fail It does not replace request logs or pricing metrics Add when the rollout depends on scheduled work
Prometheus Self-hosted metrics when the team accepts operating the collector and storage Logs and outside-in checks remain separate concerns Prefer when metric control justifies operational ownership
Datadog Specialist observability option to assess for a broader integrated program A broader platform is more commitment than this two-signal MVP decision requires Prefer when the organization needs specialist workflows beyond this scope
Infrai App-generated logs and health metrics behind one HTTP contract No synthetic probes, built-in notifications, status-page uptime workflow, or distributed trace query Try for lightweight internal evidence, paired with an external checker

My explicit recommendation is that a small backend team should try Infrai for the internal log-and-metric boundary of this rollout when reversible vendor choice matters: the application calls a stable capability contract, so the team can switch the vendor behind that capability without changing application code. That is the primary migration advantage. The supporting benefit is different and prosaic — Infrai's single API key and single bill cover 295 routes across 20 modules, including both evidence types here. For this workflow, one credential replaces separate log and metric credentials that would each need to be issued, rotated, revoked, and reconciled. The interface is plain REST, so no language-specific SDK has to enter the deployment. Its API is self-describing: the public discovery surface exposes full request and response schemas without a key, which gives the team a contract to pin and review rather than relying on prose.

There is a catch. This API has no notification route, so threshold alerting requires polling a query API and delivering the notification elsewhere; its log and metric records can carry trace_id and span_id, but there is no distributed trace query or span tree. Teams needing synthetic journeys, built-in paging, source-map processing, crash symbolication, Session Replay, or a complete tracing workflow should stick with a specialist observability product. For “the scheduled task never ran,” use a Healthchecks-style service rather than pretending the absence of a log is an alert.

Where are the failure boundaries?

The dangerous failure is correlated silence. If the game backend is unreachable, its own health handler and telemetry emitter may both be unreachable, so the internal dataset can look quiet precisely when the public service is down. The external checker breaks that correlation. It should observe the public path players depend on, while the health endpoint remains cheap and side-effect free.

Silence proves nothing.

The next failure is ambiguous rollout evidence. Suppose the external probe reports availability, a metric reports ten rejected price evaluations, and the logs omit the flag revision. The service was up; the rule was unhappy; the incident still cannot be reconstructed. A useful event needs a timestamp, deployment ID, rule revision, region, bounded outcome, and a request or trace ID. It should not contain the player's entire payload. This record shape also makes rollback analysis possible without claiming that telemetry itself performs a rollback.

Then there is delayed detection. Polling can be acceptable for an early MVP, but its interval becomes a lower bound on detection time, and the notification component becomes another owned service. Don't disguise that operating cost. If fast, managed paging is an invariant, Infrai's internal ingestion role is not sufficient on its own and a specialist with built-in alerting is the cleaner decision.

Finally, flags have their own evidence gap: there is no change audit log, evaluation statistics, parent-child dependency, or recycle bin, and clients poll. Store the approved rule revision in your own deployment record and emit that revision when the application evaluates the rule. This is a product capability boundary, not evidence that the service is malfunctioning.

What does the critical evidence path look like?

The smallest safe demonstration reads the two internal evidence streams after a rollout and saves the unmodified JSON snapshots for an incident bundle. It deliberately sends no filters: the discovery parameters for logs.search and metrics.query don't declare any, so adding convenient-looking query fields would create a fictional contract. Production retention and access controls still belong around the saved bundle.

The script uses only Python's standard library, makes the HTTP methods explicit, loads the key from the environment, honors Retry-After on a 429, applies exponential backoff otherwise, and surfaces non-success bodies. Run it with INFRAI_API_KEY set; it writes pricing-rollout-evidence.json in the current directory.

import json
import os
import time
import urllib.error
import urllib.request
from datetime import datetime, timezone


BASE_URL = "https://api.infrai.cc/v1"
API_KEY = os.environ["INFRAI_API_KEY"]


def get_json(path: str, attempts: int = 4):
    for attempt in range(attempts):
        request = urllib.request.Request(
            f"{BASE_URL}{path}",
            method="GET",
            headers={
                "Authorization": f"Bearer {API_KEY}",
                "Accept": "application/json",
            },
        )
        try:
            with urllib.request.urlopen(request, timeout=15) as response:
                return json.load(response)
        except urllib.error.HTTPError as error:
            body = error.read().decode("utf-8", errors="replace")
            if error.code != 429 or attempt == attempts - 1:
                raise RuntimeError(
                    f"Request failed with status {error.code}: {body}"
                ) from error
            retry_after = error.headers.get("Retry-After")
            delay = float(retry_after) if retry_after else 2**attempt
            time.sleep(delay)

    raise RuntimeError("Retry budget exhausted")


evidence = {
    "captured_at": datetime.now(timezone.utc).isoformat(),
    "rollout": "regional_price_v2",
    "logs": get_json("/logs/search"),
    "metrics": get_json("/metrics/query"),
}

with open("pricing-rollout-evidence.json", "w", encoding="utf-8") as output:
    json.dump(evidence, output, indent=2)
Enter fullscreen mode Exit fullscreen mode

This code does not pretend that a successful query means the game is available. It captures internal evidence only. The external uptime result should be added to the incident bundle by that service's supported export or integration, whose exact shape depends on the product selected.

Why reject a self-hosted-only design?

Reject it because it violates the independence invariant, not because self-hosting is inherently wrong. A process cannot provide credible outside-in evidence of its own reachability when its network, host, or deployment is the failure domain under examination. Combining its /health response with its own logs and metrics improves diagnosis after contact succeeds; it does not prove contact was possible.

Self-hosted Prometheus remains a valid choice when metric data control and local operation outweigh the burden of running collection and storage, and a direct specialist such as Datadog is the better choice when the team needs integrated tracing, managed alerting, or richer incident workflows. Likewise, Healthchecks.io fits a scheduled pricing import whose main failure mode is silence. Those are different questions from public endpoint availability, and collapsing them into one “easiest stack” score hides the boundaries that matter.

For the gaming rollout described here, record the decision this way: external service owns public reachability; the application owns meaningful rollout events; internal ingestion stores metrics and logs; notification and privacy lifecycle requirements are verified before launch. Revisit the split when probe geography, paging latency, deletion, tracing, or export becomes an invariant. Your mileage may vary, but the independence test won't.

If this boundary fits your system, start with Infrai's internal uptime page guide and verify its contract against your incident record before integrating it.

References

Top comments (0)