Fix Azure App Service Issues: Setup, Config & Deployment Errors
Why This Is Happening
You've pushed your code, clicked Deploy, watched the progress bar crawl to 100% , and then your app either throws a blank page, returns a 500 error, or flat-out refuses to start. I've seen this exact scenario play out on dozens of Azure App Service deployments, and I want you to know it's almost never your code that's broken. It's almost always a configuration gap between what your app expects and what Azure App Service has been told to provide.
Azure App Service is designed to take the infrastructure burden off your hands. It handles the underlying VMs, the OS patching, the load balancing , all of it. You bring the app, Azure runs it. That sounds simple, but that abstraction also means there's a layer of platform configuration sitting between your code and the internet. When something goes wrong, the error messages you get are often maddeningly vague: Application Error :( An error occurred while starting the application, or a generic HTTP 503, or a deployment that succeeds in the portal but produces a dead app at the URL.
The root causes I see most often break down into four buckets. First, runtime stack mismatches, you deployed a Node.js 20 app but the App Service plan is configured for Node 16, or you're running Python but the startup command is wrong. Second, missing or misconfigured application settings, your app reads environment variables at startup, but those variables don't exist in Azure's Application Settings blade, so the app crashes before it ever serves a single request. Third, App Service plan tier limitations, the Free and Shared tiers have real restrictions around CPU quotas, custom domains, and always-on behavior that catch people off guard. Fourth, deployment method conflicts, mixing GitHub Actions with Kudu ZIP deployments can leave stale files behind that cause unpredictable behavior.
For enterprise teams, there's a fifth category that gets complicated fast: Virtual Network integration and outbound connectivity failures. Your app can't reach a private database, a Key Vault, or an internal API because the VNet routing hasn't been configured properly, and the app just times out or throws a connection refused error with no clear pointer to the network layer.
The good news is that nearly all of these are fixable without escalating to Microsoft Support. You just need to know where to look. Browse all Microsoft fix guides →
The Quick Fix, Try This First
Before you go digging through logs or changing configuration, do this one thing: check the Diagnose and Solve Problems blade in the Azure portal. It's the single most underused feature in all of App Service, and it has saved me hours of manual log spelunking.
Here's exactly how to get there:
- Open the Azure Portal and navigate to your App Service resource.
- In the left-hand menu, scroll down to the Diagnose and solve problems option and click it.
- In the search box at the top, type the symptom you're seeing, for example "application crashes", "deployment failures", or "slow performance".
- Click on the most relevant detector. Azure's SRE Agent will run automated diagnostics against your app and surface specific findings.
- Read the Observations section carefully. It will often pinpoint the exact problem, a missing startup command, a crashed worker process, an exceeded memory limit.
If the detector points to a crash, jump straight to Application Event Logs within the same blade. These are the same logs you'd find in Event Viewer on a Windows VM, but surfaced directly in the portal without you needing to RDP anywhere. Look for Event IDs in the 1000–1026 range, these are application crash events and they'll usually include the exception type and the faulting module name.
If you're on a Linux-based App Service, the equivalent is the Log Stream feature (also in the left menu under Monitoring). Turn it on and then hit your app URL in a browser. You'll see stdout/stderr output in near real-time. A Python app failing to import a dependency, a Node app missing a required env var, it all shows up here immediately.
This single diagnostic pass resolves about 60% of Azure App Service issues I encounter without touching a single configuration setting. Try it before anything else.
The most common Azure App Service configuration error is a mismatch between the runtime your app was built for and the runtime your App Service is configured to run. Azure won't automatically detect this, it will just fail silently or with a generic 500.
Navigate to your App Service in the portal. In the left menu, go to Configuration → General settings. Look at the Stack settings section. You'll see dropdowns for the major stack (e.g., .NET, Node, Python, Java, PHP) and the specific version. Cross-reference this with what your app actually requires.
For a Node.js app, if your package.json specifies "engines": { "node": ">=20.0.0" } but the portal shows Node 16, that's your problem. Change the version in the portal and save. This forces a restart of your app workers.
For Python apps on Linux, the startup command field matters enormously. If it's blank, Azure runs a default Gunicorn command that may not match your app's entry point. The correct format looks like this:
gunicorn --bind=0.0.0.0 --timeout 600 myapp:app
Replace myapp with your module name and app with your Flask/FastAPI app object. For Django, it's typically:
gunicorn --bind=0.0.0.0 myproject.wsgi
After saving, go to Overview and click Restart. Then check Log Stream immediately. You should see Gunicorn workers starting up cleanly. If you see an ImportError or ModuleNotFoundError, that means your requirements.txt wasn't picked up during deployment, move to Step 2.
I can't count how many times I've seen a perfectly functional local app fail on Azure App Service because it was reading configuration from a local .env file that was (correctly) excluded from the deployment package. Azure App Service has its own environment variable system, and it completely replaces .env files in a production context.
Go to Configuration → Application settings. Every key-value pair you add here gets injected as an environment variable into your app's process at startup. If your app calls process.env.DATABASE_URL (Node) or os.environ.get("DATABASE_URL") (Python), that value needs to exist here, not in a file.
Click + New application setting and add each variable. Be precise with the names, these are case-sensitive. Once you've added all your settings, click Save at the top. This triggers a restart, so do this during a low-traffic window if you're editing a live app.
For database connection strings specifically, use the Connection strings section below Application settings, not Application settings. Azure treats these differently for some SDKs (particularly ADO.NET and Entity Framework), and placing a SQL connection string in the wrong section can cause subtle authentication failures.
To verify your settings are being picked up correctly, use the Kudu console. Navigate to https://<your-app-name>.scm.azurewebsites.net/Env (replace with your actual app name). This page lists every environment variable visible to your app process. Confirm your variables appear there after saving. If they don't, check whether your app is in a deployment slot, each slot has its own Application Settings, and settings aren't automatically inherited from production.
One more thing: if you're using Azure Key Vault references in your App Settings (the @Microsoft.KeyVault(...) syntax), make sure your App Service has a system-assigned managed identity enabled under Identity → System assigned, and that the Key Vault access policy grants your app's identity Get permissions on secrets.
Deployment succeeded but the app is still broken? This is often a stale artifact problem. When you deploy via ZIP deploy or GitHub Actions, Azure's Oryx build system may cache old dependencies, particularly node_modules or Python's site-packages, and your new deployment inherits an environment that's partially the old version and partially the new one.
The cleanest fix is a fresh deployment with the remote build cache cleared. In the Azure portal, go to Configuration → Application settings and add (or verify) this setting:
SCM_DO_BUILD_DURING_DEPLOYMENT = true
This tells the Kudu build system to run a full dependency install (npm install, pip install -r requirements.txt, etc.) on the server side after each deployment. Then also add:
WEBSITE_RUN_FROM_PACKAGE = 0
This ensures Azure unpacks your deployment and runs it from the file system rather than a read-only package mount, which gives Oryx the ability to write the build output properly.
After saving those settings, trigger a fresh deployment. If you're using GitHub Actions, go to your Actions workflow and click Re-run all jobs. If you're using the Azure CLI, redeploy with:
az webapp deploy --resource-group myRG --name myAppName \
--src-path ./dist.zip --type zip --clean true
The --clean true flag tells Azure to wipe the wwwroot directory before unpacking, eliminating stale files.
After deployment completes, check the Kudu deployment log at https://<your-app-name>.scm.azurewebsites.net/api/deployments. Each deployment entry has a detailed log. Expand the latest one and look for any error lines in the build output, these are the real failure messages, far more informative than the portal's generic "Deployment failed" notification.
A lot of Azure App Service troubleshooting questions turn out to be tier problems in disguise. The Free (F1) and Shared (D1) tiers have hard CPU quotas, 60 and 240 CPU minutes per day respectively. When you hit that quota, your app returns a 403 with the message "The server is busy" until the quota resets at midnight UTC. This looks like an outage but it's actually a billing/tier issue.
To check your current quota usage, go to your App Service plan (not the App Service itself, look for the App Service plan resource in your resource group) and click Quotas in the left menu. You'll see a bar chart showing CPU time, memory, and bandwidth consumption against your daily limits.
If you're hitting CPU quotas regularly, you need to scale up. Go to your App Service plan, click Scale up (App Service plan), and move to at least the Basic B1 tier. Basic removes the CPU quota cap entirely and adds the Always-On feature I mentioned earlier. For production workloads, I'd recommend at minimum the B2 (2 vCores, 3.5 GB RAM) to give yourself headroom.
For enterprise scenarios with predictable high-density workloads, the P*mv3 tier family (Premium v3, memory-optimized) gives you the best cost-per-performance ratio. According to Microsoft's own documentation, these memory-optimized instances let you run more applications on fewer VMs, which matters significantly when you're managing dozens of App Service instances across an organization. Combined with Azure savings plans or reserved instances, you can cut predictable workload costs by up to 55%.
One often-missed limitation on the Free tier: custom domains are not supported. Your app only gets the default *.azurewebsites.net hostname. If you've configured a custom domain mapping and your app is on Free tier, the domain binding will simply fail silently. Upgrade to at least the Shared tier for custom domain support, or Basic tier for custom domain plus SSL.
If you're running a containerized app on Azure App Service (as opposed to a code-based deployment), the failure surface shifts. Container-specific issues almost always come down to one of three things: the image can't be pulled, the container starts and immediately exits, or the container starts but isn't listening on the port Azure expects.
Azure App Service always routes traffic to your container on port 8080 by default on Linux. If your container is listening on port 3000, 5000, or any other port, you need to tell App Service. Go to Configuration → Application settings and set:
WEBSITES_PORT = 3000
(Replace 3000 with whatever port your container actually binds to.) Without this, Azure's health check proxy will get no response and mark your container as unhealthy, restarting it in an infinite loop.
For private container registries (Azure Container Registry, Docker Hub private repos), go to Deployment Center in the left menu. Under Registry settings, make sure the correct registry URL, username, and password (or managed identity) are configured. A common mistake is configuring the right credentials for the App Service but forgetting to also update the credentials in Configuration → Application settings under DOCKER_REGISTRY_SERVER_URL, DOCKER_REGISTRY_SERVER_USERNAME, and DOCKER_REGISTRY_SERVER_PASSWORD, these must match.
To see the raw container pull and start logs, go to Monitoring → App Service logs, enable Application logging (Filesystem), set the level to Verbose, and save. Then go to Log stream. Trigger a restart from Overview, and watch the stream. You'll see the exact Docker pull attempt, any authentication errors, and the container's own stdout output. Look for lines like Container <name> didn't respond to HTTP pings on port 8080, that's the port mismatch symptom.
Advanced Troubleshooting
When the standard fixes don't resolve your Azure App Service issues, it's time to go deeper. These techniques are for persistent problems, domain-joined enterprise environments, and scenarios that require direct inspection of what's happening inside the App Service infrastructure.
Kudu Advanced Tools Console
Kudu is the deployment engine behind every App Service, and it exposes an SSH-like console you can use to inspect the live file system. Get to it via Development Tools → Advanced Tools → Go (or directly at https://<appname>.scm.azurewebsites.net). Under the Debug console menu, choose CMD (Windows) or Bash (Linux). From here you can navigate to D:\home\site\wwwroot (Windows) or /home/site/wwwroot (Linux) to verify that your deployment files landed correctly and your directory structure looks as expected.
Application Insights Integration
If you're not using Application Insights yet, enable it now. Go to Application Insights in the App Service left menu and click Turn on Application Insights. This instruments your app with automatic request tracking, dependency monitoring, exception capture, and performance counters, all without code changes. Once enabled, check the Failures blade in Application Insights. Look at the Exceptions tab. You'll see the full stack trace for every unhandled exception your app has thrown, with the exact line number and timestamp. This is infinitely more useful than a generic 500 error page.
Managed Instance on App Service (Preview) for Legacy Apps
For enterprise teams running legacy Windows workloads that depend on COM objects, Windows registry keys, MSI installers, or drive mappings, the standard App Service model hits a wall. Microsoft's Managed Instance on App Service (currently in preview) addresses exactly this. It introduces plan-scoped isolation with startup PowerShell scripts that can execute COM registration, registry updates, ACL configuration, and MSI installation at plan startup time. Registry key definitions can reference secrets stored in Azure Key Vault, keeping sensitive values out of your scripts. If you're running a .NET Framework app that calls a legacy COM component or needs to read from HKLM\Software\..., this preview feature is worth evaluating before you default to migrating to an App Service Environment.
VNet Integration and Outbound Connectivity
If your app can't reach a private database or internal service, VNet Integration is almost certainly the fix, but it has to be configured correctly. Go to Networking → VNet Integration and verify the subnet is delegated to Microsoft.Web/serverFarms (Azure validates this but it's easy to miss). Also check that the subnet's route table doesn't have a 0.0.0.0/0 route pointing to a network appliance that drops Azure management traffic, this breaks App Service's internal health checks and causes seemingly random restarts. For outbound calls to Azure services like Key Vault or Storage, use the Private Endpoints path rather than service endpoints where possible; it's more reliable for App Service's networking model.
Windows Event Viewer via Remote Diagnostics
For Windows-based App Services, Event Viewer data is accessible through the Kudu process explorer. Navigate to https://<appname>.scm.azurewebsites.net/ProcessExplorer/ to see all running processes and their resource consumption. If your app worker process (typically w3wp.exe for .NET or node.exe for Node) is consuming abnormal CPU or memory, it will show here. You can also download event logs as a ZIP package from Tools → Diagnostic Dump in Kudu for offline analysis.
Prevention & Best Practices
The best Azure App Service troubleshooting is the kind you never have to do. After years of watching teams hit the same walls repeatedly, here are the practices that consistently prevent the most common issues before they happen in production.
Use deployment slots, not direct production deployments. Every App Service on Standard tier and above supports deployment slots. Your workflow should be: deploy to a staging slot, run your smoke tests, then use the Swap operation to promote staging to production. Swaps are near-instant (no cold start, because the new slot's app is already warm) and completely reversible if something goes wrong. Teams that deploy directly to production slots and then scramble to roll back are making life unnecessarily hard for themselves.
Set up health check probes. In Monitoring → Health check, configure a path in your app that returns HTTP 200 when the application is genuinely healthy (not just when the web server is up). Azure uses this path to automatically remove unhealthy instances from the load balancer rotation and attempt recovery. Without a health check configured, Azure only detects total process crashes, it won't catch cases where your app is running but not actually serving requests correctly.
Pin your runtime version. Don't leave the runtime version at "Latest" in your App Service configuration. When Microsoft updates the platform runtime (Node 18 → 20, Python 3.10 → 3.11), apps set to "Latest" get upgraded automatically, and that can break dependency compatibility overnight. Pin to a specific version and upgrade intentionally as part of your release process.
Enable diagnostic logging from day one. Go to App Service logs and enable Application Logging and Web Server Logging to blob storage before you ever deploy production traffic. Retrospective debugging without logs is an exercise in frustration. Storage-based logging is persistent across restarts (unlike filesystem logging which clears on restart) and costs almost nothing for typical log volumes.
- Enable Always-On in Configuration → General settings on any Basic tier or higher app to eliminate cold-start false-outages
- Set
WEBSITE_TIME_ZONEin Application Settings to match your team's timezone, default is UTC and it causes log timestamp confusion during incidents - Configure Auto-heal rules under Diagnose and solve problems → Auto Heal to automatically restart workers that exceed memory thresholds or return too many 5xx responses
- Use managed identity (system-assigned or user-assigned) instead of storing connection strings and API keys in Application Settings, connect directly to Azure services like Storage, Key Vault, and SQL without credentials in config
Frequently Asked Questions
Why should I use Azure App Service instead of a regular VM or Azure Kubernetes Service?
Azure App Service removes the overhead of managing the underlying infrastructure entirely, OS patching, VM sizing, load balancer configuration, SSL termination, all of that is handled for you. For teams that want to focus on building and shipping applications rather than managing servers, App Service is genuinely the right call. It supports .NET, Java (SE, Tomcat, and JBoss), Node.js, Python, and PHP on both Windows and Linux, plus containerized workloads. AKS makes sense when you need fine-grained container orchestration across many services, but for most web applications and REST APIs, App Service is simpler to operate and often more cost-effective at scale, especially with the memory-optimized P*mv3 tiers.
My Azure App Service deployed successfully but I'm getting a blank page or HTTP 500, what's wrong?
This almost always comes down to a startup failure that's being swallowed silently. Start with Diagnose and solve problems in the portal, then check Log Stream under Monitoring while you refresh your app URL, you'll see the actual exception in real time. The most common causes are a missing startup command for Python/Node apps, environment variables that exist locally but weren't added to Azure's Application Settings, or a runtime version mismatch between what your app needs and what the App Service is configured to use. Fix those three things first before assuming the problem is in your code.
Why does my Azure App Service keep restarting on its own?
Spontaneous restarts usually have one of four causes: your app is crashing (check Application Insights or the event log for the exception), you're hitting memory limits for your App Service plan tier and Azure is recycling the worker process, an Auto-heal rule is triggering (check Diagnose and solve problems → Auto Heal), or Azure is performing a platform maintenance operation on the underlying infrastructure. In the last case, you'll see a brief restart with no corresponding application error, this is expected behavior and the solution is zone redundancy on Premium tier rather than trying to prevent restarts entirely. For crash-triggered restarts, Application Insights exception tracking will give you the stack trace you need.
Can I use Azure App Service for free as a student or to learn Azure?
Yes, there's a genuine free tier (F1) that gives you 1 GB storage and 60 CPU minutes per day, which is plenty for learning and prototyping. Microsoft also offers the Azure for Students Starter program on top of the standard free tier, specifically targeting students without a credit card requirement. For development tooling, App Service has purpose-built deployment integrations for Visual Studio, VS Code, IntelliJ, and Eclipse. Just be aware of the Free tier's limitations: no custom domains, no SSL certificates, no Always-On, and the daily CPU quota. For anything you're showing to real users, upgrade to at least the Basic B1 tier.
Users are getting an error downloading attachments in Outlook Web App (OWA), is this related to my App Service?
OWA attachment download errors are a separate issue from Azure App Service itself, OWA is part of Exchange Online and Microsoft 365, not a workload running on your App Service instance. That said, if you've built a custom web application on App Service that proxies or serves files, the most common cause of download failures is a missing or incorrect Content-Disposition header in your HTTP response (it should be attachment; filename="yourfile.ext"), or a request being blocked by CORS policy if the download is initiated from a different origin. For pure OWA service incidents at the Microsoft 365 infrastructure level, check the Microsoft 365 Service Health dashboard in the admin center, these are usually service-wide incidents tracked by Microsoft's engineering teams, not something you can fix on your own infrastructure.
How do I set up continuous deployment to Azure App Service from GitHub?
Go to your App Service in the portal and click Deployment Center in the left menu. Select GitHub as your source, authorize Azure to access your GitHub account, then choose your organization, repository, and branch. Azure will automatically generate a GitHub Actions workflow file and commit it to your repo's .github/workflows/ directory. From that point on, every push to the selected branch triggers a build-and-deploy pipeline. If you want more control over the workflow (custom build steps, environment-specific variables, deployment slot targeting), download the generated YAML file, edit it in your repo, and commit, Azure will use your modified version on the next push. Make sure your GitHub repository's Actions secrets include the publish profile secret that the generated workflow references.