Blog Post

Microsoft Developer Community Blog
9 MIN READ

Troubleshooting Azure Container Apps and Jobs for .NET and Django Workloads

BhaktiRath95's avatar
BhaktiRath95
Icon for Microsoft rankMicrosoft
Jun 09, 2026

Troubleshooting Azure Container Apps in Production | Part 1 of 4

Introduction

Deploying to Azure Container Apps feels like a huge step forward — you get serverless containers, automatic scaling, built-in ingress, and managed environments without managing Kubernetes directly. But when something goes wrong and your container refuses to start, or your Container App Job silently fails, it can feel like debugging inside a black box.

This first part of our four-part series walks through the most common deployment and startup failures you will hit when running .NET and Django applications on Azure Container Apps and Container App Jobs. We cover what the real error looks like, why it is happening under the hood, and what you need to do to fix it — step by step.

The Real-World Problem: "My Container App is stuck in a restart loop and I have no idea why"

This is probably the most common thing engineers report when they first move workloads to Azure Container Apps. The deployment finishes successfully, the revision shows as active, but the app never becomes healthy. In the Azure portal it cycles between `Running` and `Degraded`, and in the logs you see cryptic exit codes or — even worse — nothing at all.

The root causes almost always fall into one of these buckets:

  1. The container exits immediately because the process crashes on startup (misconfiguration, missing secrets, unhandled exceptions).
  2. The health probe fails because the app takes too long to start or is listening on the wrong port.
  3. A Container App Job never completes because it times out or the job process exits with a non-zero code.

Let us walk through each of these in detail.

Scenario 1: Your .NET Application Crashes at Startup
What You See

Your Container App revision goes into a restart loop. You check the Log Analytics workspace and see something like this:

ContainerAppConsoleLogs_CL
| where ContainerAppName_s == "my-dotnet-api"
| where TimeGenerated > ago(30m)
| project TimeGenerated, Log_s
| order by TimeGenerated desc

The output shows:

Unhandled exception. System.InvalidOperationException: Unable to resolve service for type 'MyApp.Data.AppDbContext'

   at Microsoft.Extensions.DependencyInjection.ServiceProviderServiceExtensions.GetRequiredService

   ...

Application is shutting down.

 

Or even more commonly with Entity Framework Core:

fail: Microsoft.EntityFrameworkCore.Database.Connection

An error occurred using the connection to database 'mydb' on server 'myserver.database.windows.net'.

System.Net.Sockets.SocketException: Connection refused

Why This Happens

When .NET 6+ applications start up, they run the entire `WebApplication.Build()` pipeline before accepting traffic. If any registered service — like a database context — cannot be constructed or if the connection string is missing or wrong, the application throws an unhandled exception and the process exits with a non-zero code. Container Apps detects this exit and restarts the container. This cycle repeats indefinitely.

The most frequent trigger is missing or incorrectly named environment variables and secrets. In local development you rely on `appsettings.Development.json` or `user secrets`, but in Container Apps those files are not present unless you explicitly copy them into the image (which you should never do for secrets).

Step-by-Step Fix

Step 1 — Verify your secrets and environment variables are configured correctly.

In the Azure portal, navigate to your Container App → Configuration → Secrets and Environment variables. Make sure every value your app reads from IConfiguration is defined here.

From the CLI you can inspect and update them like this:

# Add or update a secret reference
az containerapp secret set 
  --name my-dotnet-api 
  --resource-group my-rg 
  --secrets "connectionstring=Server=myserver.database.windows.net;..."

# Reference that secret as an environment variable
az containerapp update 
  --name my-dotnet-api 
  --resource-group my-rg 
  --set-env-vars "ConnectionStrings__DefaultConnection=secretref:connectionstring"

 

Step 2 — Make sure your .NET app reads configuration correctly.

The naming convention that trips up almost everyone: Azure Container Apps uses double underscores (`__`) to represent the colon (`:`) separator in .NET configuration keys. So `ConnectionStrings:DefaultConnection` becomes `ConnectionStrings__DefaultConnection` as the environment variable name.

// This reads from "ConnectionStrings__DefaultConnection" env var automatically

builder.Services.AddDbContext<AppDbContext>(options =>
    options.UseSqlServer(builder.Configuration.GetConnectionString("DefaultConnection")));

 

Step 3 — Add a startup health check that gives meaningful feedback.

Configure a liveness probe with a generous initial delay to avoid a container being killed before it has had time to start:

# In your Container App YAML configuration

probes:
  - type: Liveness
    httpGet:
      path: /health
      port: 8080
    initialDelaySeconds: 30
    periodSeconds: 10
    failureThreshold: 5
  - type: Readiness
    httpGet:
      path: /health/ready
      port: 8080
    initialDelaySeconds: 15
    periodSeconds: 5
    failureThreshold: 3

 

Add the corresponding health endpoint in your .NET app:

// Program.cs

builder.Services.AddHealthChecks()
    .AddSqlServer(
        builder.Configuration.GetConnectionString("DefaultConnection")!,
        name: "database",
        tags: new[] { "ready" });

app.MapHealthChecks("/health");
app.MapHealthChecks("/health/ready", new HealthCheckOptions
{
    Predicate = check => check.Tags.Contains("ready")
});

 

Step 4 — Pull the raw container logs using the CLI to see exactly what happened before the container exited:

az containerapp logs show 
  --name my-dotnet-api 
  --resource-group my-rg 
  --type console 
  --tail 50

 

Scenario 2: Your Django Application Fails to Start
What You See

Your Django app deploys, the container starts, but within seconds it exits. In the logs you see one of these common errors:

django.core.exceptions.ImproperlyConfigured: Set the SECRET_KEY environment variable

 

Or:

django.db.utils.OperationalError: could not connect to server: Connection refused

    Is the server running on host "localhost" (127.0.0.1) and accepting

    TCP/IP connections on port 5432?

 

Or the static files problem that catches almost everyone:

[Errno 2] No such file or directory: '/app/staticfiles'

Why This Happens

Django validates its configuration eagerly when the WSGI/ASGI server starts. If `SECRET_KEY` is not set, if `ALLOWED_HOSTS` does not include the container's hostname or the ingress FQDN, or if `DEBUG=True` is set in a configuration branch that requires a proper database, Django refuses to serve any requests.

 

The static files error comes up because many teams forget to run `python manage.py collectstatic` as part of the container image build process. The `STATIC_ROOT` directory simply does not exist at runtime.

Step-by-Step Fix

Step 1 — Set required Django environment variables in your Container App.

az containerapp secret set 
  --name my-django-app 
  --resource-group my-rg 
  --secrets 
    "django-secret-key=your-very-secret-key-here" 
    "db-password=your-db-password"
az containerapp update 
  --name my-django-app 
  --resource-group my-rg 
  --set-env-vars 
    "DJANGO_SECRET_KEY=secretref:django-secret-key" 
    "DEBUG=False" 
    "ALLOWED_HOSTS=my-django-app.happyfield-abc123.eastus.azurecontainerapps.io" 

    "DATABASE_URL=secretref:db-password"

 

Step 2 — Run `collectstatic` during Docker image build, not at runtime.*

This is a very common mistake. Static files should be baked into the image, not generated when the container starts. Update your `Dockerfile`:

FROM python:3.11-slim
WORKDIR /app
COPY requirements.txt .
RUN pip install --no-cache-dir -r requirements.txt
COPY . .
# Collect static files at build time with a dummy SECRET_KEY
RUN SECRET_KEY=build-time-placeholder python manage.py collectstatic --noinput
EXPOSE 8000
CMD ["gunicorn", "myproject.wsgi:application", "--bind", "0.0.0.0:8000", "--workers", "2", "--timeout", "120"]

 

Step 3 — Make sure Gunicorn is configured correctly for Container Apps.

The most important thing to verify is that Gunicorn is binding to `0.0.0.0` and not `127.0.0.1`. Container Apps expects the application to listen on all interfaces so that the ingress layer can reach it. Also make sure the port matches what you defined in your Container App's ingress target port:

# Set ingress to match Gunicorn's bind port
az containerapp ingress update 
  --name my-django-app 
  --resource-group my-rg 
  --target-port 8000 
  --type external

Step 4 — Handle database migrations safely.

Never run `python manage.py migrate` as part of your container startup command. If you have multiple replicas, all of them will try to run migrations simultaneously, which can corrupt your schema. Instead, use a Container App Job to run migrations as a pre-deployment step:

 

# Create a one-time Container App Job to run migrations

az containerapp job create 
  --name django-migrate-job 
  --resource-group my-rg 
  --environment my-aca-env 
  --trigger-type Manual 
  --replica-timeout 300 
  --image myregistry.azurecr.io/my-django-app:latest 
  --command "python" 
  --args "manage.py" "migrate" 
  --env-vars 
    "DJANGO_SECRET_KEY=secretref:django-secret-key" 
    "DATABASE_URL=secretref:db-password"


# Execute the migration job before deploying the new revision
az containerapp job start 
  --name django-migrate-job 
  --resource-group my-rg
Scenario 3: Your Container App Job Fails Silently or Times Out
What You See

You trigger a Container App Job — maybe it is a nightly data processing job, a scheduled report generator, or a cleanup task — and in the Azure portal the execution shows as Failed with no helpful error message. Or it shows as Running for an unusually long time and then transitions to Failed with a timeout error.

 

Why This Happens

Container App Jobs have a `replicaTimeout` property. If your job process does not complete within that window, Azure Container Apps kills it and marks the execution as failed. This is different from Container Apps (services) where the container keeps running. Jobs are expected to run to completion and exit with code `0`.

The silent failure happens when your job process exits with a non-zero exit code but does not write anything to `stdout` or `stderr`. Container Apps records the exit code but has no log content to show you.

Step-by-Step Fix

Step 1 — Make your job emit logs to stdout explicitly.

Every print statement, every log line should go to `stdout` or `stderr`. In Python:

import sys
import logging
logging.basicConfig(
    level=logging.INFO,
    format="%(asctime)s %(levelname)s %(message)s",
    handlers=[logging.StreamHandler(sys.stdout)]
)
logger = logging.getLogger(__name__)

def main():
    logger.info("Job starting")
    try:
        # your job logic here
        process_data()
        logger.info("Job completed successfully")
        sys.exit(0)

    except Exception as e:
        logger.error(f"Job failed with error: {e}", exc_info=True)
        sys.exit(1)

 

In .NET:

// Use ILogger which writes to stdout by default in containers
public class MyJob
{
    private readonly ILogger<MyJob> _logger;
    public MyJob(ILogger<MyJob> logger)
    {
        _logger = logger;
    }

    public async Task RunAsync(CancellationToken cancellationToken)
    {
        _logger.LogInformation("Job starting at {Time}", DateTimeOffset.UtcNow);
        try
        {
            await ProcessDataAsync(cancellationToken);
            _logger.LogInformation("Job completed successfully");
        }
        catch (Exception ex)
        {
            _logger.LogError(ex, "Job failed");
            throw; // Let the process exit with non-zero code
        }
    }
}

 

Step 2 — Set an appropriate replica timeout and retry count.

Be realistic about how long your job takes in production, then add a buffer:

az containerapp job update 
  --name my-processing-job 
  --resource-group my-rg 
  --replica-timeout 1800    # 30 minutes
  --replica-retry-limit 2    # Retry twice before marking as failed

 

Step 3 — Check job execution history and logs.

# List recent job executions and their status
az containerapp job execution list 
  --name my-processing-job 
  --resource-group my-rg 
  --output table

# Get logs for a specific execution
az containerapp job execution show 
  --name my-processing-job 
  --resource-group my-rg 
  --job-execution-name my-processing-job-abc123

 

From Log Analytics:

ContainerAppConsoleLogs_CL
| where ContainerAppName_s == "my-processing-job"
| where TimeGenerated > ago(24h)
| project TimeGenerated, Log_s, ContainerName_s
| order by TimeGenerated desc

Summary: Your Startup Troubleshooting Checklist

Before you dig into complex diagnostics, run through this checklist whenever a Container App or Job fails to start:

  • Are all required environment variables and secrets defined and correctly referenced?
  • Is the application listening on `0.0.0.0` and on the port that matches the ingress target port?
  • Does the Dockerfile copy everything needed for the app to run (migrations, static files, etc.)?
  • Are health probes configured with enough initial delay for the app to start?
  • For jobs: is the replica timeout long enough, and does the process exit with code 0 on success?
  • Is the container registry accessible from the Container Apps environment (managed identity or registry credentials configured)?
  • Are the resource allocations (CPU and memory) sufficient for the application to start without OOM-killing?

References and Sample Resources

Use these resources for deeper implementation details and production-ready patterns.

Azure Container Apps docs (core)
.NET and Django references
Sample repositories

What's Next

In Part 2 of this series, we move past startup failures and look at what happens after your app is running — the frustrating world of cold starts, scaling delays, and startup latency spikes that make your application feel slow under real production traffic.

 

Part of the series: Troubleshooting Azure Container Apps in Production 

Next: Part 2 — From Slow to Snappy: Performance Tuning Cold Starts and Scaling Delays in Azure Container Apps

Updated May 18, 2026
Version 1.0
No CommentsBe the first to comment