Short answer: treat an uptime check as a rollback signal, not as proof that a pricing rollout is healthy. Keep a stable control path, expose semantic health checks, probe them outside the application boundary, and make rollback depend on an explicit failure policy. A free monitor is useful only when its failure boundary is understood.
That rule matters in a B2B SaaS app rolling out a new pricing rule behind a flag. A process can return HTTP 200 while calculating the wrong invoice, and an uptime monitor can report green while every probe comes from one broken network segment. The design below keeps those mistakes visible. The backend is the system under test; the monitor is only an observer.
The trade-off is deliberate.
No dashboard can fix that.
What must remain true during the rollout?
The architecture starts with invariants. For this scenario, I would record these in the decision record:
- The old pricing rule remains executable until the new rule passes its observation window.
- A request can be tied to the flag variant, tenant class, pricing-rule version, and decision timestamp without logging secrets.
- Liveness, dependency readiness, and semantic pricing correctness are separate signals.
- A rollback changes one controlled decision, and an outside check can confirm it.
- Every probe has a bounded timeout, a known origin, and enough history to distinguish a transient miss from a repeatable failure.
The failure boundaries are distinct. The flag service can be unavailable. The application can serve stale flag data. The pricing calculation can be wrong while transport remains healthy. The monitor can be unable to reach the service. Collapsing all four cases into “down” makes rollback unsafe.
| Option | Useful boundary | Rollback signal | Main limitation |
|---|---|---|---|
| Hosted synthetic check | Outside-in availability and latency | Repeated failed probes from independent locations | It cannot prove tenant-specific price correctness |
| Self-hosted probe | Internal or private-network services | A check close to the dependency | It shares more infrastructure with the system measured |
| Scheduled semantic check | A business operation such as quote calculation | A failed expected result or missing heartbeat | It needs safe test data and cleanup |
For a free setup, these are deployment patterns, not a winner’s podium. A hosted service such as Pingdom is an external observer. Uptime Kuma is a self-hosted monitoring application. Gatus is a configuration-driven health dashboard and alerting tool. Healthchecks.io is oriented toward expected jobs and heartbeats. Those boundaries differ; replacing one with another without changing the probe’s question only changes the name on the alert.
Can free uptime monitoring alternatives to Pingdom protect an app backend?
Use two paths. The liveness path answers whether the process can accept work. The semantic path executes a deterministic, non-billable quote for a fixture tenant and verifies it against a versioned expected value. It must never mutate an account or create an invoice.
from dataclasses import dataclass
from decimal import Decimal
from typing import Literal
@dataclass(frozen=True)
class PricingProbeResult:
status: Literal["pass", "fail"]
rule_version: str
observed_total: Decimal | None
reason: str | None
def run_pricing_probe(pricer, flags, fixture_tenant_id: str) -> PricingProbeResult:
decision = flags.read("pricing_rule", tenant_id=fixture_tenant_id)
expected = {"legacy-7": Decimal("70.00"), "new-7": Decimal("75.00")}
if decision.version not in expected:
return PricingProbeResult("fail", decision.version, None, "unknown_rule_version")
quote = pricer.quote(
tenant_id=fixture_tenant_id,
quantity=7,
rule_version=decision.version,
dry_run=True,
)
if quote.total != expected[decision.version]:
return PricingProbeResult("fail", decision.version, quote.total, "unexpected_total")
return PricingProbeResult("pass", decision.version, quote.total, None)
The fixture is not a customer. It is a stable contract test that makes a wrong rule observable. Expected values should come from reviewed test data, not from the same calculation path being tested; otherwise the probe asks the code to agree with itself. That distinction is the part many “free alternative” comparisons omit: an uptime result is transport evidence unless the check contains a semantic assertion.
The semantic assertion is small.
The endpoint can report failure when this check fails, but the alert policy should not roll back on one request. Use a short confirmation interval and a second signal: structured evidence carries the rule version and probe result, while a metric expresses the rate of failed semantic checks. OpenTelemetry’s signal model is useful here because logs, metrics, and traces answer different questions.
That separation is operationally important during a rollout: a transport failure can call for investigation, a semantic mismatch can call for rollback, and a missing heartbeat can indicate that the checker itself has stopped running. Those outcomes may share an alert channel, but they should not share an automatic action.
Which failure should trigger rollback?
Put the rule in the architecture record, not in an alert recipient’s memory. One defensible policy is two consecutive failed observation intervals, or a flagged-cohort pricing-error rate above its reviewed threshold while a control cohort remains healthy. The threshold is a policy choice and belongs with the release record.
A 503 from the probe means that the observed contract is unavailable or failed; it does not identify the cause. Inspect the attached rule version, dependency state, recent flag changes, and probe origin. One timeout is evidence of a network boundary. The same semantic mismatch from three independent locations is stronger evidence of a bad rollout.
Rollback must be idempotent. If two alert deliveries arrive, both should request the same target state and create an audit event instead of toggling the flag twice. Record the previous version, target version, reason, actor, and timestamp. Later, the team should be able to answer whether it rolled back the pricing rule or only silenced the monitor.
There is a storage trap here. Do not put every successful response body into a long-lived log stream. Store decision metadata in structured events, sample successful checks, retain failures with correlation identifiers, and keep raw responses only when they are safe and necessary. Durable evidence matters, but indefinite retention of customer-shaped payloads creates another liability.
What does “free” change in the design?
Free monitoring changes who operates the control plane, not the laws of failure. A self-hosted monitor may avoid a subscription, yet it still needs persistence, alert delivery, upgrades, access control, and a second place to receive alerts. If it runs on the same host, network, and identity system as the application, its green status is a local opinion.
| Tool or pattern | Best evidence | Operational trade-off |
|---|---|---|
| Pingdom | External synthetic availability and timing | A hosted control plane and external probe policy |
| Uptime Kuma | Checks operated from infrastructure you control | You own persistence, upgrades, and alert delivery |
| Gatus | Declarative endpoint checks and status visibility | Configuration and runtime become part of your platform |
| Healthchecks.io | Heartbeats for expected scheduled work | A heartbeat proves a job ran, not that a quote was correct |
These are examples of different boundaries, not recommendations. Select based on independent failure domains, state retention, and whether the team can rehearse rollback. A low bill does not compensate for an untested control path. For the app backend, the right alternative is the one whose failure mode the on-call team can identify and reverse.
The rejected option and its valid use
The rejected design is one /health endpoint that returns 200 whenever the web process is alive, with rollback wired to any non-200 response. It is valid for a load balancer’s liveness decision. It is insufficient for a pricing rollout because it confuses process state, dependency state, and business correctness, then turns a transport symptom into a deployment action.
Keep it for liveness. Add readiness and a separately protected semantic probe for the rollout. Test stale flag data, an unavailable flag store, a mismatched expected total, a slow dependency, a monitor timeout, duplicate rollback requests, and unavailable monitor storage. Each case needs an owner, an observable signal, and a reversible action.
Rollback safety is the deciding criterion. A free uptime monitor can participate in that system, but it cannot supply the invariants, semantic fixture, or independent failure boundary by itself.
Top comments (0)