From the team behind aidrop.it — one workspace to build, host, and keep changing your code.

The dashboard says running. The container started, the process has not exited, the log is quiet. And the address returns a gateway error, a timeout, or a blank page.

"Running" is a statement about a process. "Answering" is a statement about a request. A platform can see the first for free — it counts containers. The second has to be asked for, and most applications never say how to ask.

The fix is a health path: one URL the platform can request that returns 200 only when a real request would succeed. With it, a deploy that starts a broken build reads as failed. Without it, the same deploy reads as done, and the first person to find out is a visitor.

Three layers, three different questions

A deployment is healthy at three layers, and each answers a different question. The process layer: did the container start and stay up. The application layer: does it accept a request on the port it was told. The address layer: does the public name reach it. A green mark at one layer proves nothing about the next.

Layer Question What goes wrong while it still reads fine
Process Is the container up? It is bound to loopback, or to a port nothing routes to
Application Does it answer on the declared port? It started, then blocked on a migration or a dependency; or every route returns 500
Address Does the public name reach it? DNS, the certificate, or a route that never attached

The task count only covers the first row. Everything most people mean by "is it up" lives in the other two.

What a health path is for

A health path is one URL that returns 200 only when the application could serve a real request: the process is up, listening on the assigned port, with the configuration it needs present. It is how a platform learns the second layer without knowing anything about your code. Answer it, and "running" starts to mean something.

Three properties make it useful rather than decorative:

  • No authentication and no side effects. A probe that gets 401 reads as broken; one that writes a row on every request is a slow leak.
  • Fast, and never cached. It should answer in milliseconds from the process itself, not from a CDN that remembers the last good answer.
  • Say the resolved facts in the body. Which commit is running, which port was bound, which variable is missing. A 200 with {"status":"ok"} confirms life; a body that names the port turns a gateway error into a fact you can read.

Should the check touch the database?

Fail the health check for what a redeploy would fix, and report — but do not fail — for what it would not. Missing configuration, a port that could not be bound, a migration that did not run: return 503, so the deploy stops there. A dependency that is slow right now: say so in the body and return 200, because restarting you does not restart it.

That is the whole of the liveness-versus-readiness distinction, in one rule. On a single-container application it matters more, not less: a failing check does not shift traffic to a healthy replica, because there is none. It is a verdict on the deploy, so it should fail only for what the deploy got wrong.

The endpoint and the check, to keep

The handler, in Express — the shape is the same in any framework. It fails for what a redeploy would fix, and reports the rest.

// GET /health — 200 only when a real request would work.
app.get("/health", (req, res) => {
  const required = ["DATABASE_URL", "SESSION_SECRET"]
  const missing = required.filter((name) => !process.env[name])
  res.status(missing.length ? 503 : 200).json({
    status: missing.length ? "misconfigured" : "ok",
    commit: process.env.GIT_COMMIT ?? "unknown",
    port: process.env.PORT ?? "unset",
    missing,
  })
})

The check, from a machine that is not the server. A probe from inside the platform confirms the application layer; only an outside request confirms the address a visitor uses. It repeats until the address answers, and prints the time beside every attempt, so "how long did the deploy take to answer" is a number rather than a feeling.

for i in $(seq 1 30); do
  code=$(curl -sS -o /dev/null -m 5 -w '%{http_code}' https://your-app.example.com/health)
  echo "$(date +%T) $code"
  [ "$code" = 200 ] && break
  sleep 10
done

Thirty attempts ten seconds apart is five minutes. A deploy that has not answered by then has not deployed, whatever the dashboard says.

FAQ

Why does my deployment say running when the site is down? Because "running" is measured at the process layer: the container started and has not exited. The site being down is a failure at the application or address layer, which a container count cannot see. A health path is how the platform gets to see it.

What should a health check endpoint return? 200 when a real request would succeed, 503 when it would not, and a small JSON body naming the resolved facts — commit, port, and any missing configuration. No authentication, no caching, no side effects.

Should a health check test the database connection? Report it, do not fail on it. A slow or briefly unavailable database is not fixed by restarting your application, so failing the check for it takes the application down for nothing. Fail only for what a redeploy would fix.

What is the difference between liveness and readiness? Liveness asks whether the process is alive; readiness asks whether it can serve a request right now. On a platform with one container per application, the useful distinction is what to fail on: readiness failures that a redeploy would fix, and nothing else.

What does aidrop.it treat as running? A container that stays up is active, and the health path declared in the runtime profile is then requested through the edge on the port the container was told. A container that is up but answers nothing on that port is reported as failed, with a message that says to bind PORT, rather than as deployed.


Cover: https://commons.wikimedia.org/wiki/File:Old_Brass_Door_Knocker.jpg by Scrypted, CC0, via Wikimedia Commons. Cropped to 16:9 within the original frame. Alt text: A weathered brass door knocker on a green painted wooden door.

Before the next deploy

Get it running from the session you are already in

Connect your coding agent over MCP and ask it to deploy. aidrop builds the repository and runs it on a public address; when a build or the app fails, the log says why, and your agent fixes it and builds again.