Maique Moraes

Senior full stack engineer
based in Brazil

Skip to content
Back to field notes

Delivery · Containers

A green pipeline, an unhealthy app

Build passed, tests passed, image published — and the new revision never became ready. A port mismatch between a NestJS service and Azure Container Apps, and what it taught me about what a pipeline actually proves.

Published 3 min read

Every check in the pipeline was green. The build passed, the tests passed, and the container image was published. By every signal the pipeline could give, the release was done. But on Azure Container Apps, the new revision of the NestJS service never became ready.

Nothing was wrong with the code. The problem lived in the gap between the application and the platform that runs it — a gap no step of the pipeline was looking at.

What “ready” means on Azure Container Apps

On Azure Container Apps, deploying a new image creates a new revision of the app. When ingress is enabled, it forwards traffic to one target port on the container, and the platform checks that port before it treats the revision as healthy. If you don’t define health probes of your own, it adds default TCP probes against that same port.

So “ready” is a runtime agreement: the process has to be listening where the platform expects it. If it isn’t, the image can be perfectly fine and the revision will still never become ready.

Following the evidence

A failed rollout makes it tempting to suspect the newest code change. The faster route was to read what the platform itself was reporting: the container’s logs and the revision’s events.

Together they told a clear story. The application had started — NestJS was listening on port 3000. The ingress was configured to send traffic to port 8080. The platform kept checking a port where nothing was listening, so the revision never became ready. Two settings, each reasonable on its own, simply disagreed.

Why the pipeline couldn’t see it

A pipeline answers the questions you give it. Does the code compile? Do the tests pass? Can the image be built and published? All of those were yes.

But the port isn’t a property of the code alone. It’s a contract between the application and the environment that runs it, and it’s configured in two places: where the app decides which port to bind, and where the platform decides which port to call. Nothing in the pipeline started the image with the platform’s settings and asked it to answer on the expected port, so the mismatch stayed invisible until the platform tried to use it.

The fix, and the question after it

I aligned the configuration so the application and the ingress agreed on the port, then verified that the health checks passed and the endpoints responded in the deployed environment.

The fix itself was small. The more useful question came next: if this drifted here, where else could it have drifted? I reviewed the other environments for the same mismatch, so the next deployment wouldn’t hit the same failure somewhere else.

Guardrails worth adding

This kind of failure is cheap to prevent. A few checks close the gap between “the build passed” and “the service is running”:

  • One source for the port. Bind to a configured PORT instead of a hard-coded number, and set it alongside the ingress target port — one decision instead of two assumptions.
  • Explicit health probes. Point startup and readiness probes at a real health endpoint, so “ready” means the app can serve requests, not just that a socket is open.
  • A smoke check after every rollout. Call the health endpoint through the real ingress once the revision is up, and fail the release if it doesn’t answer.
  • Environment configuration as code. Keep ports, probes, and ingress settings versioned and reviewed, and compare them across environments instead of fixing each one by hand.
// main.ts
const app = await NestFactory.create(AppModule);

// The platform decides the port; the app follows it.
const port = Number(process.env.PORT ?? 3000);
await app.listen(port, "0.0.0.0");
TypeScript · Illustrative: a NestJS entry point that binds to the configured port

What I took from it

A green pipeline is evidence about the artifact: the code compiles, the tests pass, the image exists. It says nothing about whether the service is healthy where it runs. A deployment is finished when the running service answers — not when the last job turns green.

TakeawayA successful pipeline proves the build, not the running service.

Contact

Building something that has to be right?

I do my best work with teams that care about the details — clear interfaces, dependable services, and AI that fits real business rules. If that sounds like your team, email is the fastest way to reach me.

Based in Brazil (UTC−3), with flexible hours for overlap with teams in the Americas and Europe.