Azure & .NET

A .NET API on Azure Container Apps: identity, readiness, scaling and recovery

Build a release you can explain: qualify health probes, constrain identity, plan revision overlap and verify a practical .NET10 HTTP lab before production.

Conceptual release architecture: API revisions, a readiness boundary, identity and durable dependencies. This is an editorial illustration, not an Azure dashboard.

Conceptual release architecture: API revisions, a readiness boundary, identity and durable dependencies. This is an editorial illustration, not an Azure dashboard. · ImageGen illustration

Start with the release you need to explain

A useful first cloud deployment is one you can explain after something goes wrong. You should know which image accepted a request, which identity accessed its dependencies, which configuration that image received, and how to return to a working state. A successful container start answers only a small part of that story. This guide follows a deliberately small ASP.NET Core API into Azure Container Apps, keeping deployment identity, readiness, scaling and recovery visible instead of hiding them behind a large template.

The example workload accepts a request, validates a small contract and reads a dependency. It has no requirement to remember sessions inside one process. That makes a stateless HTTP service an appropriate starting shape. Durable records belong in an explicitly chosen data service; temporary files and an in-memory dictionary are not silently promoted into a database. Before choosing replica counts, write down the request deadline, expected payload size, dependency connection budget and acceptable behavior during dependency failure.

The downloadable local project demonstrates health transitions and cancellation with the real .NET 10 web host. It does not provision an Azure subscription or emulate managed identity. Use SharpRune to investigate portable language behavior, then use the SDK project for operating-system hosting and HTTP. This distinction prevents a browser example from being mistaken for evidence that an actual cloud deployment has been qualified.

References: Container Apps revisions

Make the image a stable release identity

Build the application once, publish a container image, record its digest and promote that same content through the environments you intend to compare. A friendly tag is convenient for humans, but a mutable tag can refer to different bytes tomorrow. An incident report saying only that production ran latest is weak evidence. Keep the source revision, image digest, target runtime, configuration version and deployment observation together so the release can be reconstructed.

Keep build-time material out of the runtime image. Source repositories, package-manager credentials, developer settings and compiler tools are usually not required to serve requests. A multi-stage build separates restore and compilation from the smaller runtime stage. Pin the intended .NET major version deliberately and update patch versions through a reviewable release. Minimal images still need a plan for security updates; smaller does not mean maintenance-free.

Validate the image locally under realistic process settings before pushing it. Confirm the listening address and port, environment-variable names, graceful termination behavior and writable-directory assumptions. If the app expects a certificate file, configuration mount or native dependency, make that requirement explicit. Azure cannot infer a missing application dependency from a container that exits immediately, and retries will not repair an image built for the wrong architecture or startup command.

References: Container Apps revisions

Give liveness, startup and readiness different jobs

Liveness asks whether this process should continue running. Startup allows initialization to take its expected time before ordinary health decisions begin. Readiness asks whether a replica should receive the traffic that depends on its current capabilities. These questions overlap, but using one endpoint for all three can create an avoidable restart loop. An unavailable database does not necessarily mean restarting every API process will help.

The local lab exposes a simple live endpoint and a ready endpoint backed by explicit readiness state. Its self-test first observes a not-ready response, changes the state through the test harness, then verifies readiness and a normal request. No public administrative route allows a caller to flip that state. In a real service, readiness should represent the dependencies actually required for accepting the relevant work, with bounded checks and a clear initialization boundary.

Choose probe timing from measured startup and failure behavior. A check that performs an expensive query on every probe can add load exactly when a dependency is struggling. A check with no timeout can leave probe work accumulating. Document what failure means, how long it may last before the platform acts, and whether recovery needs a process restart or simply restoration of a dependency. Verify these choices with an intentionally unavailable dependency before sending real traffic.

.NET10 SDK / desktop CoreCLR

app.MapGet("/health/live", () => Results.Ok(new { status = "live" }));
app.MapGet("/health/ready", IResult () => readiness.Ready
    ? Results.Text("ready")
    : Results.StatusCode(503));
// Excerpt: the complete .NET10 web-host project is downloadable below.

References: Health probes

Use identity without confusing authentication and permission

A managed identity supplies an application identity without embedding a long-lived application secret in an image. That is valuable, but identity alone does not authorize every action on every Azure resource. The target service must support the authentication path, the correct identity must be selected, and role assignment must allow the narrowly required operation at the intended scope. Distinguish failure to obtain a token from a successful token being denied by the resource.

Choose system-assigned or user-assigned identity according to lifecycle and deployment requirements. A user-assigned identity can be prepared independently of one app's lifetime; sharing it across unrelated workloads also shares its permissions and attribution boundary. A system-assigned identity follows the app resource's lifecycle. Neither choice removes the need to record which principal the application actually uses. Avoid granting broad administrative roles merely to make an initial request stop returning forbidden.

For local development, use a supported developer authentication flow and keep it separate from the production identity configuration. In production, prefer an explicit intended identity where several are available. Inspect authorization failures using safe metadata such as principal, resource and operation, rather than printing tokens. A readiness failure caused by an incorrect role assignment should lead to an identity investigation, not to disabling authorization or copying a developer credential into a container environment.

References: Health probes

Treat secrets as versioned dependencies

A secret is more than a string placed somewhere outside Git. Decide who can read it, how its value reaches the process, how updates become effective and which old values must remain valid during a rollout. Key Vault references can reduce direct handling of secret values, but the application still needs a correct identity and access policy. A missing secret reference and an incorrect secret value are different failure cases.

Keep secret names, resource identifiers and configuration references in the deployment record while protecting actual secret values. Verify whether a reference uses a specific secret version or a versionless identifier and how the running application observes changes. Do not assume that reverting an image also reverts every application-scoped configuration change. An older revision can encounter newer shared configuration, so recovery planning must include the configuration and data contract.

Test rotation as an operational sequence. Introduce a new credential or version while the previous path remains usable where the service supports overlap, verify the intended consumers, then retire the old path under the resource's rules. Avoid exposing diagnostic endpoints that echo configuration or environment values. Useful diagnostics identify the missing setting or denied operation without returning the credential that would make the request succeed.

A managed identity needs explicit resource permission at an appropriate scope. The pictured compartments illustrate scope, not a real credential or console.
A managed identity needs explicit resource permission at an appropriate scope. The pictured compartments illustrate scope, not a real credential or console. · ImageGen illustration

References: Managed identities

Configure ingress and trust boundaries deliberately

An application behind managed ingress receives traffic through a platform boundary. Confirm the exposed target port, allowed protocol, authentication expectations and whether the endpoint should be public at all. A private dependency does not become safe merely because the API's public hostname uses HTTPS. Likewise, a container listening on localhost inside its network namespace may be healthy to itself while unreachable through the intended ingress path.

Forwarded headers describe information supplied across a proxy boundary. Configure ASP.NET Core to trust the proxies or networks that actually own that boundary instead of accepting arbitrary client-supplied forwarding metadata. Scheme, host and client-address handling can affect redirects, link generation, authentication and rate-limit attribution. A test that sends only a direct localhost request will not exercise the real reverse-proxy path.

Make one end-to-end request through the public or private endpoint that clients will use. Inspect status, routing, authentication and the application version returned in safe diagnostic metadata. Check an invalid request and an unauthorized request too. A useful release test confirms that the application rejects work it should not accept, rather than treating any HTTP 200 somewhere in the chain as a complete security and readiness result.

References: Managed identities

Understand revisions before shifting traffic

A Container Apps revision records a version of revision-scoped configuration. Running multiple revisions allows a new candidate to be checked alongside a serving version. The crucial operational question is not whether the candidate exists, but whether it is ready for the intended workload and whether routing actually sends the intended traffic to it. Provisioned, running and serving are related states, not interchangeable words.

Use a candidate endpoint or an explicitly controlled traffic allocation to exercise representative requests before moving the rest of the traffic. Include dependency access, a request that touches the current data shape and a failure path. Keep a way to identify the responding revision in traces or safe response metadata. If both old and new versions run together, any shared data and message contracts must support that overlap.

A rollback is a verified action, not merely retaining an old revision name. Confirm that its image remains available, required configuration references still resolve and the older code can read data written by the newer version. Routing back to an old container cannot automatically reverse a destructive schema migration or an external side effect. Use compatible migration stages and keep the release record precise enough to know what remains recoverable.

A candidate revision and a serving revision need separate readiness and routing evidence. The paths are conceptual and do not represent measured traffic percentages.
A candidate revision and a serving revision need separate readiness and routing evidence. The paths are conceptual and do not represent measured traffic percentages. · ImageGen illustration

References: Secret references

Scale against a dependency budget

Horizontal scaling increases the number of places that can accept work. It can also multiply database pools, HTTP connections, memory baselines and retry activity. A replica count chosen only from frontend request concurrency can overwhelm a dependency with a much smaller safe capacity. Start with the slowest constrained dependency and derive an admission and concurrency policy that the whole system can sustain.

Define minimum and maximum replicas intentionally, then choose scale signals appropriate to the workload. An HTTP signal and a queue-length signal describe different pressure. Scale-to-zero can be appropriate for some workloads, while startup delay may violate another workload's latency objective. These choices also affect cost and recovery behavior; consult the current service documentation and measure the intended deployment rather than copying someone else's replica setting as a universal default.

Account for the rollout itself. Overlapping revisions and platform transitions can temporarily run more processes than the steady-state picture suggests. Maintain headroom for that overlap, including connection and memory budgets. If each process owns a pool with a large idle high-water mark, adding replicas may reduce active work per process while increasing total retained memory. The Memory Lab's workload calculator is useful for exposing those assumptions before a real constrained deployment test.

References: Secret references

Observe the request across boundaries

Choose a request correlation and tracing strategy that follows work across the API and its downstream calls. Record deployment identity, route, result category and duration without logging sensitive bodies by default. Structured events are more useful when their fields are stable and actionable. A giant concatenated error string that contains a connection string creates both an investigation problem and a secret-handling problem.

Separate request latency from dependency latency, queue wait and time spent obtaining admission. A slow response can be caused by a small downstream pool even when the API has spare CPU. Compare successful and failed request paths under the same workload phase. Averages alone can hide a minority of very slow requests, and an increase in rejected requests can make average latency look better while user-visible reliability gets worse.

Use OpenTelemetry and the platform's supported monitoring integration where they fit the environment, but verify that a trace actually arrives with the expected service and deployment attributes. Instrumentation configuration is not evidence of useful telemetry until a real request can be followed. Keep sampling and volume under control, and preserve enough error context to distinguish an authorization problem from a network timeout or an application contract failure.

References: Scaling

Make cancellation and shutdown part of deployment testing

A deployment can terminate processes while requests are active. Application shutdown should stop accepting new owned work, allow bounded completion where appropriate and release resources when work is cancelled. Decide which operations can be retried and which require idempotency or reconciliation. Returning a client error does not prove a downstream side effect did not happen before the connection closed.

Pass request cancellation to operations that support it, but do not equate a cancellation request with confirmed rollback. A database transaction, outgoing message and external payment each have their own completion semantics. Keep disposable resource ownership around the actual asynchronous consumption interval. Returning a rented buffer while a downstream write still uses it is incorrect even if the surrounding request has already been abandoned.

Exercise graceful termination in the local SDK host and again in a representative deployment environment. Set an explicit bound for shutdown, observe in-flight outcomes and verify that retries cannot create duplicate business effects. The local example demonstrates host lifecycle and health transitions, while the Azure release test must additionally exercise the platform's routing and termination behavior. Neither substitutes for the other.

References: Scaling

A concrete release exercise

Write a small release worksheet before the first rollout. Include image digest, source revision, runtime, intended identity, configuration references, target port, health endpoints, dependency budget and recovery conditions. For each field, name the evidence that establishes it. A value copied from a template is a proposed configuration; a readback from the intended environment is an observation. Keep that distinction visible during review.

Run the included SDK project's self-test and inspect its exact readiness transition. Then introduce one deliberate configuration fault in a disposable environment: wrong dependency endpoint, missing permission or incorrect target port. Predict which probe and telemetry should reveal it. Restore the intended configuration and verify the normal path again. Do not weaken the production health or authorization contract to make the deliberate fault disappear.

Before shifting traffic, verify one new request, one repeat request, one invalid request and one cancelled request under the intended release. Record the result and the responding deployment identity. Finally, rehearse routing back to the retained version with compatible data. This exercise turns an abstract checklist into evidence you can use when a real rollout behaves differently from its local test.

References: Traffic splitting

Keep learning connected to the production boundary

SharpRune is useful for the parts of this workflow that depend on understanding code: reference lifetimes, cancellation propagation, collection behavior, validation and small executable contracts. Use its debugger to explain a portable program statement by statement, and use the academy's architecture and load lessons to question your design assumptions. A compact runnable example makes an ownership mistake easier to see before it becomes one field among thousands in a production dump.

Production hosting adds a separate layer of evidence: real sockets, cloud identity, ingress, replica coordination, dependency quotas and durable recovery. Keep those experiments in the appropriate SDK and deployment environments. The downloadable local project is a bridge into that layer, with a clear boundary around what it proves. It is intentionally small enough to inspect rather than an opaque framework that claims to solve every service concern.

When the API is running, retain a reproducible release path instead of treating the first deployment as the finish line. Patch the runtime, rehearse secret rotation, verify probes after dependency changes and review the capacity model as traffic changes. The practical goal is a service whose behavior you can explain and recover, with measurements that support the next decision rather than a collection of green badges with unknown meaning.

References: Traffic splitting

Triage a failed revision without changing everything

When a candidate fails, preserve its original result and choose the narrowest next question. If the image cannot be pulled, inspect image identity, registry access and the configured identity for that operation. If the process exits, inspect its startup command and bounded application logs. If the process runs but is not ready, compare the target port, readiness path and required dependency state. If the replica is ready but a client request fails, inspect ingress, authentication, routing and the request contract. These branches need different evidence.

Avoid the common response of changing port, identity, replica count and probes in one attempt. Even when that produces a green state, it leaves you unable to explain which change mattered. Keep a small record of each attempted revision, its expected change and its observed result. Use provider tools to read the current revision and its logs, but redact credentials and private payloads before sharing diagnostic output. A failed attempt can remain valuable evidence without being retried under a new name until its outcome is understood.

Once the cause is established, change one relevant configuration or code path and repeat the same acceptance request. Confirm that the original security and readiness constraints still apply. Recovery that works only after deleting those constraints is not equivalent to repairing the deployment you intended to operate.

References: ASP.NET Core health checks

Run the complete SDK project

.NET10 SDK / desktop CoreCLR

Download the complete project, read README.md and execute in a separate local SDK process. Cloud hosting and production load behavior require independent qualification.

dotnet run -c Release --project ApiLab.csproj -- --self-test
Download complete project ZIP

Continue with SharpRune

Sources & further reading

Technical references checked against primary documentation. Browser playground examples target the SharpRune .NET 9 profile; production server guidance describes a separate hosting environment.

Illustration created with ImageGen for SharpRune. Article and examples are educational; validate behavior in your target runtime.
Put an idea into practice