Deploys are the most dangerous routine moment in your SaaS's week. The ritual feels safe — you have done it a hundred times, it usually works, and the whole thing takes minutes. That comfort is exactly the risk. Across industry postmortems, a majority of outages trace back to a change: a deploy, a config edit, a migration. A deploy is the moment new code, new settings, and a new runtime environment meet all of your customers at once. And the minutes right after it are when your product is most likely to be broken and least likely to be watched, because by then you have already switched to the next task.
The gap between "deployed" and "confirmed working" is where small-SaaS outages live. A billing webhook that stops processing does not page anyone; it quietly skips renewals until a customer emails you. A database pool that never recovers after a redeploy does not crash the app; it adds seconds to every request until people give up. Health checks and monitoring after deployment exist to close that gap — to tell you within minutes, not days, whether the release you just shipped is actually serving customers.
This guide is written for a solo founder or a two-person team with no on-call rotation. You will get a plain-language model of health checks (liveness versus readiness) and what a good health endpoint verifies, a 10-minute post-deploy watch routine you can run alone, a minimum viable monitoring set, alert rules that will not train you to ignore alerts, and a deploy-day routine that turns all of it into habit.
Why Health Checks and Monitoring After Deployment Matter
Change causes most outages, and deploys are the most frequent change you make. Almost everything else in production — disk growth, certificate renewal, a dependency deprecating an endpoint — moves slowly and predictably. A deploy moves everything at once: application code, library versions, configuration, sometimes a database migration, all within the same hour.
The asymmetry is what makes the first ten minutes after a deploy so valuable. If a broken release reaches customers and you notice in two minutes, the damage is a handful of failed requests and one rollback. If you notice in two days, you have lost signups, renewals, and the trust of every customer who hit the problem and told you about it. Discovery time, not incident count, is what usually separates a non-event from a bad week.
There is also a blunt fact about small teams: you have no on-call rotation, so your monitoring is your on-call. Customers are the most expensive monitoring system you can rely on — they detect outages with their credit cards. The plan in this article has three layers: health checks so a broken release never receives traffic, a watch routine so you catch the failures health checks cannot see, and a small set of monitored signals and alerts for everything that happens between deploys.
Health Checks Explained: Liveness vs. Readiness
A health check is a small endpoint your application exposes — usually /healthz or /health — that answers one question about itself. The trick is that there are two different questions, and confusing them causes real incidents.
Liveness asks: "is it running?" If liveness fails, the process is wedged or dead, and the right response is to restart it.
Readiness asks: "is it actually working?" The process is up, but it cannot do its job — the database is unreachable, the queue is unavailable, a critical dependency is failing. Restarting does not fix that. The right response is to stop sending traffic to the instance until it recovers.
- : Plain-language question — Liveness: Is it running? — Readiness: Is it actually working?
- : What failure means — Liveness: The process is dead or wedged — Readiness: The process is up but cannot do its job
- : Typical checks — Liveness: The process answers an HTTP request — Readiness: Database connectivity, critical dependencies
- : Right response to failure — Liveness: Restart the container — Readiness: Remove the instance from load balancing
- : Wrong response — Liveness: Restarting your way through a database outage — Readiness: Routing customer traffic to a broken release
The two classic mistakes come from mixing these up. If you restart on a readiness failure, you get a crash loop that makes an outage worse: the app boots, fails its database check, dies, and repeats. If you only check liveness and treat it as proof of health, you route customers to an app that boots fine and fails on every real request.
What does a good health endpoint actually verify? Three things: that the process can serve requests, that it can reach the database with a real query, and that the small set of dependencies your core flows need are reachable. Keep it fast (a couple of seconds at most), leave it unauthenticated or protect it with a token your platform knows, and never include connection strings or stack traces in the response.
// Node/Express example — fictional values; adapt to your stackapp.get("/healthz", async (req, res) => { const checks = { process: "ok", database: "fail", job_queue: "fail", }; try { // A real query against a table you own beats SELECT 1: await db.query("SELECT 1 FROM plans LIMIT 1"); checks.database = "ok"; } catch (err) { // Log the detail server-side; do not leak it in the response console.error("healthz: database check failed", err.message); } try { await queue.ping(); // fictional stub for your queue library checks.job_queue = "ok"; } catch (err) { console.error("healthz: queue check failed", err.message); } const healthy = Object.values(checks).every((v) => v === "ok"); res.status(healthy ? 200 : 503).json({ status: healthy ? "healthy" : "degraded", checks, release: process.env.RELEASE_ID || "unknown", });});
One judgment call: be careful about failing readiness on third-party dependencies you cannot fix. If your health endpoint turns red whenever a payment provider has a blip, every instance gets pulled from traffic and you have converted their brief incident into your full outage. Check the dependencies that block your core flows and that you can act on; degrade gracefully for the rest.
Why Health-Gated Rollouts Matter
Health checks do their real work at deploy time. In a health-gated rollout, the new version starts in a standby slot, runs its health checks, and only receives customer traffic once it has proven healthy. The old version keeps running until the new one passes. If the new release never goes healthy, traffic never moves — a bad deploy becomes a failed deploy instead of an outage.
Without that gate, every deploy is all-or-nothing: the moment you switch, 100% of your customers are on unproven code. With it, the deploy itself becomes your first and fastest smoke test.
The catch is that gating is only as honest as the endpoint it gates on. A /healthz that returns "ok" without touching the database is a rubber stamp: it will happily pass a release whose every real request fails. This is the part no platform can decide for you. You define what "healthy" means for your app — which tables to query, which dependencies matter, what counts as degraded — and the gate enforces your definition, whatever it is.
Know what health checks will never catch, too: failures that only appear under real traffic or real time. A webhook consumer that silently stopped processing, a worker that deadlocks after ten minutes, a checkout flow that breaks on one specific plan — those pass a health check and ship to customers anyway. That is exactly what the next section is for.
The Founder's 10-Minute Post-Deploy Watch Routine
Run this after every production deploy, in the same order, with a timer. It is deliberately boring. The routine's value is that it happens every time — especially on the deploys that feel trivial.
- Minute: 0–1 — Check: The new release is actually live — How: Hit /healthz and compare the release ID to what you shipped — What "bad" looks like: Old release still answering; health endpoint red
- Minute: 1–3 — Check: Signup and login work — How: Create a test account in a private window; log out and back in — What "bad" looks like: Signup 500s, login loops, verification email never arrives
- Minute: 3–5 — Check: Error rate and latency — How: Watch your error tracker and platform dashboard against the pre-deploy baseline — What "bad" looks like: 5xx rate above normal; p95 latency 2–3x baseline
- Minute: 5–6 — Check: Background jobs still run — How: Trigger one harmless job (an export, a report) and watch it finish — What "bad" looks like: Queue depth climbing, jobs stuck in "processing"
- Minute: 6–8 — Check: Logs since the deploy — How: Filter application logs to the deploy timestamp; scan for stack traces — What "bad" looks like: Repeating stack traces, "connection refused", auth errors
- Minute: 8–10 — Check: Billing webhook still processes — How: Send a test event from your payment provider's sandbox, or read recent webhook logs — What "bad" looks like: Signature failures, zero deliveries since the deploy
If anything is wrong at minute two, roll back first and diagnose after. The instinct under pressure is to fix forward — patch, redeploy, patch again — but every fix-forward attempt keeps customers on a broken release while you experiment. A rollback returns you to a known-good state in seconds; you can debug the failed release calmly, in staging, with no audience.
Two habits make this routine faster. First, put a release ID in the health response (the snippet above does), so "is the new version live?" is a curl, not a guess. Second, keep a staging or non-production project where you deploy first and run the same ten minutes — most issues die there and never reach customers.
A Monitoring Minimum Viable Set for a Tiny SaaS
Between deploys, you need visibility, not an observability platform. Six signals cover the failure modes that actually hit a small SaaS. Any tool that surfaces the signal is fine — the vendor matters far less than the fact that someone (you) gets told when a signal crosses a line.
- Signal: Uptime — What it tells you: Customers can reach the app at all — Tool example: External uptime checker hitting / and /healthz (e.g., UptimeRobot, Better Stack) — Alert threshold guidance: Page after 3 consecutive failures over 5 minutes
- Signal: Error rate — What it tells you: How often requests fail — Tool example: 5xx counts from access logs, or an error tracker such as Sentry — Alert threshold guidance: Page at > 2% of requests over 5 minutes, tuned to your baseline
- Signal: Latency — What it tells you: The app is slow, not just down — Tool example: p95 from access logs or your platform's dashboard — Alert threshold guidance: Warn at 2x baseline for 10 minutes; page only if errors accompany it
- Signal: Disk and memory — What it tells you: The quiet killers: full disks and out-of-memory restarts — Tool example: Platform metrics; on a VPS, a cron df check or node_exporter — Alert threshold guidance: Page disk > 85% for 15 minutes; memory > 90% sustained
- Signal: Certificate expiry — What it tells you: The browser-padlock outage you can prevent for free — Tool example: SSL monitoring service or a cron openssl expiry check — Alert threshold guidance: Page when fewer than 14 days remain
- Signal: Job queue depth — What it tells you: Background work is backing up — Tool example: Queue dashboard (BullMQ, Sidekiq Web) or a row count on your jobs table — Alert threshold guidance: Page when the oldest job is > 15 minutes old or depth grows for 30 minutes
Two notes on the list. Uptime checks should run from outside your platform — a monitor running on the same server as the app shares its failures. And certificate expiry is on the list because even automated SSL issuance, which most platforms including Deployxa handle, deserves one monitor: DNS gets misconfigured, renewals fail silently, and the padlock disappears for every visitor before you hear about it any other way.
Alerting Without Noise: Page on Symptoms, Not Blips
The fastest way to destroy your own monitoring is to alert on everything. After two weeks of false alarms you will mute the channel — and then the one alert that mattered will arrive into a muted channel. The rule that prevents this: alert on customer-impacting symptoms, not on every blip. "Disk is at 80%" is a blip with a trend; "checkout has been failing for five minutes" is a symptom.
Split alerts into two tiers and route them differently.
Page you (push or SMS — keep it to five rules or fewer):
- Uptime: /healthz failing 3 consecutive checks — first action: check platform status and last deploy
- Errors: 5xx rate above 2% for 5 minutes — first action: read logs since the last deploy, roll back if the deploy is recent
- Billing: any webhook processing errors in the last 10 minutes — first action: check payment provider dashboard
- Queue: oldest job older than 15 minutes — first action: check worker logs for a crash loop
- Disk: above 85% for 15 minutes — first action: clear old logs and build artifacts
Email only (review daily or weekly):
- Latency at 2x baseline for 10 minutes
- Memory above 90% sustained
- Certificate under 21 days remaining (escalate to a page at 14)
- A recurring exception in logs that customers are not yet reporting
"Who gets paged" has a short answer when there is no team: you do. That constraint should shape the rules, not become an excuse to skip alerting. Keep the page list small enough that a page always means "look now." Put one clear sentence and the first action into every alert, because at 11 p.m. you will not want to reconstruct context from scratch. If you have a cofounder, agree on who owns which alerts instead of both of you half-owning all of them. And once a quarter, trigger one alert on purpose, so you learn the channel works before an outage teaches you it does not.
Logs vs. Metrics vs. Traces, in Plain Words
These three words get used interchangeably and mean different things.
Metrics are numbers over time: error rate, latency, queue depth. They answer "how much" and "how fast," they are cheap to keep, and they are what alerts fire on.
Logs are individual events with detail: this request failed, with this stack trace, at this timestamp. They answer "what exactly happened." If metrics tell you the fire alarm went off, logs tell you which room started the fire.
Traces follow one request as it moves across services — app to database to queue to external API — and show where the time went. They pay off once you have multiple services or asynchronous handoffs. For a single-app SaaS with a database, they are usually premature.
For a tiny SaaS, structured logs plus the six signals above are enough. Use JSON with a request ID so you can filter one request's journey out of the noise, and never log secrets, tokens, or payment details — logs outlive the incidents they describe.
Where Deployxa Fits — and Where It Doesn't
Most of this article is deliberately platform-agnostic, so it is fair to ask where a deployment platform changes the picture. On Deployxa, three pieces of it are handled for you.
Health-gated blue/green releases. Each release deploys into a standby slot and is health-verified before traffic switches. The previous release stays warm for a short rollback window, and rolling back within that window can be sub-second — which is the "roll back first, diagnose after" move from the 10-minute routine, minus the panic.
Platform health checks and application logs in one place. The dashboard shows the health of your running apps and the logs they produce, so the "scan logs since the deploy" step does not require SSH or a log-piping setup you never got around to building.
A status page for customers. When something does break, the status page lets you tell customers what is wrong and what you are doing about it — before they open support tickets or assume the worst.
What Deployxa does not do — and no platform can — is decide what "healthy" means for your application. You write the health endpoint, you choose which dependencies it checks, you set alert thresholds that fit your traffic, and you own the response when something pages you at night. Monitoring tools beyond platform health checks and logs — external uptime checks, error tracking — are yours to add and wire up. A platform can gate traffic on your definition of healthy and put your logs one click away; it cannot watch your revenue for you.
Your Deploy-Day Routine
Here is the whole article compressed into something you can run this week.
Checklist — before every deploy:
- Confirm last night's database backup ran, and your last restore test is recent
- Note the current release ID and the exact rollback command
- Pick an off-peak window, never five minutes before your busiest hour
Deploy-day routine:
- Verify backups are current and you know the rollback path before you touch anything.
- Deploy to a staging or non-production project first if one exists, and run the 10-minute routine there.
- Deploy to production off-peak, and watch the release go healthy before you tab away.
- Run the 10-minute watch routine with a timer, in order, every deploy.
- Anything wrong? Roll back within the warm window, then diagnose in safety.
- Anything right? Write one line in a deploy journal — what shipped, when, what you saw.
- File tickets for anything you noticed and deprioritized, so it lives on a list, not in your head.
- Once a week, review which alerts fired and which should have but did not, and adjust the rules.
The routine takes ten minutes on a normal day. On a bad day, it is the difference between a two-minute rollback and an apology email to every customer. Write your own version of the 10-minute routine — five checks, timed, built around your product's most critical flows — and run it on your next deploy to a non-production project. You can set that project up in minutes on Deployxa and rehearse the full deploy-day routine there, so the habit exists before the day you actually need it.