Skip to main content

Deploy ASP.NET Core to Azure App Service in 10 Minutes

Learn how to deploy ASP.NET Core to Azure App Service in 10 minutes using the Azure CLI, Visual Studio, and GitHub Actions. Start deploying today!

If you want to deploy ASP.NET Core to Azure App Service, you are looking at the fastest, most battle-tested path to getting a .NET web app into production. Azure App Service is Microsoft's fully managed hosting platform — it handles the web servers, load balancing, TLS certificates, patching, and scaling so you can focus on shipping code. In this tutorial you will take a brand-new ASP.NET Core application from dotnet new to a live public URL in about 10 minutes, using three different methods: the Azure CLI (fastest), Visual Studio (most familiar), and GitHub Actions (the way real teams do it).

More importantly, we will cover why each step matters, the configuration mistakes that cause the dreaded HTTP 500.30 startup error, and the best practices that separate a demo deployment from a production-ready one.

What Is Azure App Service and Why Use It?

Azure App Service is a Platform-as-a-Service (PaaS) offering. That distinction matters. With a virtual machine (IaaS), you own the operating system: patching, IIS or Nginx configuration, certificate renewal, and security hardening are all your problem. With App Service, Microsoft owns the platform and you own only your application code.

For ASP.NET Core developers specifically, App Service is compelling because:

  • First-class .NET support — new .NET releases (including .NET 8 LTS and .NET 9) are available on the platform almost immediately, on both Windows and Linux workers.
  • Free tier to start — the F1 tier costs nothing and is perfect for this tutorial; you can scale to Basic, Premium, or Isolated tiers later without redeploying.
  • Built-in DevOps hooks — deployment slots, GitHub Actions integration, and easy rollbacks are part of the platform, not something you bolt on.
  • Automatic HTTPS — every app gets a free *.azurewebsites.net domain with TLS out of the box.

Prerequisites

  • The .NET SDK (8.0 or later) installed locally
  • An Azure account — the free account includes App Service credits
  • The Azure CLI (az) for Method 1

Verify your tooling before you start. Nothing burns your 10 minutes faster than discovering mid-deployment that az isn't on your PATH:

dotnet --version   // should print 8.0.x or later
az --version       // should print azure-cli 2.60+ or later
az login           // opens a browser to authenticate

Step 1: Create an ASP.NET Core App

We'll use a minimal API so the whole app fits on one screen. If you already have an app, skip ahead — everything else works the same.

// Terminal:
// dotnet new web -o HelloAzure
// cd HelloAzure

// Program.cs
var builder = WebApplication.CreateBuilder(args);
var app = builder.Build();

app.MapGet("/", () => Results.Ok(new
{
    Message = "Hello from Azure App Service!",
    Environment = app.Environment.EnvironmentName,
    ServerTimeUtc = DateTime.UtcNow
}));

app.MapGet("/health", () => Results.Ok("Healthy"));

app.Run();

Two things here are deliberate. First, we expose the environment name so we can prove the app is reading Azure configuration correctly after deployment. Second, we add a /health endpoint — App Service can ping it to detect and auto-heal unhealthy instances, which you will want in production.

Run it locally with dotnet run and confirm you get JSON back at http://localhost:5000. Deploying an app you never ran locally is the number one way to waste an afternoon debugging the wrong layer.

Method 1: Deploy ASP.NET Core to Azure App Service with the Azure CLI

This is the 10-minute path. The az webapp up command creates the resource group, the App Service plan, and the web app, then zips and deploys your code — all in one step.

// From the HelloAzure project folder:
az webapp up `
  --name helloazure-yourname-2026 `
  --resource-group rg-helloazure `
  --location eastus `
  --sku F1 `
  --os-type Linux `
  --runtime "DOTNETCORE:8.0"

A few important details, because the defaults will bite you if you don't understand them:

  • The app name must be globally unique — it becomes your URL: https://helloazure-yourname-2026.azurewebsites.net. If the name is taken, the command fails immediately.
  • --sku F1 is the free tier. It sleeps after 20 minutes of inactivity and has a 60-minutes-per-day CPU quota. Fine for learning; use B1 or P0v3 for anything real.
  • Prefer Linux workers for ASP.NET Core. Kestrel runs directly behind the platform's front end — no IIS/ANCM layer — which means simpler config, slightly better cold starts, and cheaper compute.
  • Pin the runtime explicitly. Letting the CLI guess the runtime version is how apps mysteriously break when the default changes.

After a minute or two, the CLI prints your live URL. Open it and you should see your JSON payload with "Environment": "Production". That's it — you have deployed ASP.NET Core to Azure App Service.

What Actually Happened?

Understanding the machinery helps when things go wrong. az webapp up ran dotnet publish implicitly, zipped the output, and pushed it to the Kudu deployment engine, which extracted it into /home/site/wwwroot. App Service then launched your app with dotnet HelloAzure.dll, and the platform's front end started routing HTTPS traffic to Kestrel on the port specified by the PORT environment variable — which ASP.NET Core picks up automatically. You wrote zero configuration for any of that, and that's the point of PaaS.

Method 2: Publish from Visual Studio

If you live in Visual Studio 2022, right-click the project → Publish → Azure → Azure App Service (Linux). Sign in, click Create new, accept the defaults (or pick the free tier), and hit Publish. Visual Studio builds in Release mode, publishes, and opens your browser at the live URL.

This is genuinely convenient for solo projects and prototypes. But treat right-click publish as a learning tool, not a workflow. Deployments from a developer laptop are unrepeatable — they depend on whatever code, SDK version, and local changes happen to be on that machine. Which brings us to the method real teams use.

Method 3: Continuous Deployment with GitHub Actions

The best practice for any team (and honestly, any solo project you care about) is deploying from CI. Every push to main builds, tests, and deploys — reproducibly, with an audit trail.

In the Azure Portal, open your App Service → Deployment Center → select GitHub, authorize, and pick your repository. Azure generates a workflow file automatically, but here is a clean, modern version using OpenID Connect (no stored publish-profile secrets):

# .github/workflows/deploy.yml
name: Deploy to Azure App Service

on:
  push:
    branches: [ main ]

permissions:
  id-token: write
  contents: read

jobs:
  build-and-deploy:
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v4

      - uses: actions/setup-dotnet@v4
        with:
          dotnet-version: '8.0.x'

      - name: Publish
        run: dotnet publish -c Release -o ./publish

      - name: Azure Login
        uses: azure/login@v2
        with:
          client-id: ${{ secrets.AZURE_CLIENT_ID }}
          tenant-id: ${{ secrets.AZURE_TENANT_ID }}
          subscription-id: ${{ secrets.AZURE_SUBSCRIPTION_ID }}

      - name: Deploy
        uses: azure/webapps-deploy@v3
        with:
          app-name: helloazure-yourname-2026
          package: ./publish

Why OIDC instead of a publish profile? Publish profiles are long-lived credentials sitting in your repo secrets — if they leak, an attacker can deploy code to your app until you rotate them. OIDC federated credentials are short-lived tokens issued per workflow run, scoped to your specific repo and branch. Since 2023 this has been Microsoft's recommended approach, and it takes five extra minutes to set up with az ad app create and a federated credential.

Configuration: The Part Everyone Gets Wrong

Your appsettings.json should never contain production secrets. App Service provides Application Settings (Portal → Configuration), which are injected as environment variables and override your JSON config thanks to ASP.NET Core's layered configuration system. The mapping rule trips everyone up at least once: a nested JSON key uses a double underscore in App Service.

// appsettings.json (local dev value)
// { "Smtp": { "ApiKey": "dev-key" } }

// In App Service Application Settings, set:
//   Name:  Smtp__ApiKey        <-- double underscore, not a colon
//   Value: prod-key

// Your code never changes:
var apiKey = builder.Configuration["Smtp:ApiKey"];

For connection strings and real secrets, go one step further: enable the app's managed identity and pull secrets from Azure Key Vault with a reference like @Microsoft.KeyVault(SecretUri=...). Your app then has zero stored credentials anywhere — the identity is issued by the platform itself.

Best Practices and Common Pitfalls

Best Practices

  • Use deployment slots (Basic tier and up). Deploy to a staging slot, smoke-test it on its own URL, then swap to production. The swap is a routing change, so it's near-instant — and if something breaks, swapping back is your one-click rollback.
  • Turn on "Always On" for paid tiers. Without it, your app is unloaded after 20 idle minutes and the next visitor pays the cold-start cost.
  • Enable Application Insights. One checkbox gives you request traces, dependency timings, and exception reports. Debugging production without it is guesswork.
  • Configure a health check path. Point App Service at /health (Portal → Health check) so the platform removes and restarts unhealthy instances automatically when you scale out.
  • Set ASPNETCORE_ENVIRONMENT deliberately. App Service defaults to Production, which is correct — but set it explicitly on staging slots so the right appsettings.{Environment}.json layer loads.

Common Pitfalls

  • HTTP 500.30 / "Application failed to start." Almost always a runtime mismatch (app targets .NET 8, App Service configured for .NET 6) or a startup exception. Check Log stream in the portal, or run az webapp log tail — the real exception is printed there.
  • Deploying Debug builds. az webapp up and the GitHub Actions workflow publish Release builds, but hand-rolled scripts often forget -c Release. Debug builds are slower and leak detailed error pages.
  • Hardcoding ports or URLs. Never call app.Run("http://0.0.0.0:5000"). App Service assigns the port via environment variable, and Kestrel binds to it automatically. Fighting the platform here causes container start failures on Linux plans.
  • Forgetting the free tier's limits. F1 apps sleep, have no custom domain TLS, and share compute. If your demo "randomly" takes 15 seconds to respond, that's a cold start, not a bug.
  • Locale and time-zone assumptions. App Service instances run in UTC. If your app displays local times, convert with TimeZoneInfo rather than trusting DateTime.Now.

Conclusion: Deploy ASP.NET Core to Azure App Service the Right Way

You can deploy ASP.NET Core to Azure App Service in under 10 minutes with a single az webapp up command — and that speed is real, not marketing. But the difference between a quick demo and a production system is everything around the deployment: CI/CD instead of laptop publishes, environment variables and Key Vault instead of secrets in JSON, deployment slots instead of deploy-and-pray, and Application Insights instead of flying blind.

Key takeaways:

  • az webapp up is the fastest path from code to a live URL — use the free F1 tier to experiment at zero cost.
  • Prefer Linux App Service plans for ASP.NET Core: simpler, cheaper, and Kestrel-native.
  • Move to GitHub Actions with OIDC as soon as the project matters — repeatable deployments with no long-lived credentials.
  • Configuration belongs in App Service Application Settings (remember the double underscore for nested keys), and secrets belong in Key Vault via managed identity.
  • When startup fails, az webapp log tail shows you the real exception — start there, not with guesswork.

Next steps: try adding a custom domain with a free App Service managed certificate, or wire up a staging slot and experience a zero-downtime swap for yourself. Once you have deployed to Azure App Service the right way once, you will never want to FTP files to a server again.

About csharp-coder.com
Your go-to resource for C#, .NET, and modern software development. Follow along for daily tutorials, tips, and real-world examples.

Comments

Popular posts from this blog

Angular 14 : 404 error during refresh page after deployment

In this article, We will learn how to solve 404 file or directory not found angular error in production.  Refresh browser angular 404 file or directory not found error You have built an Angular app and created a production build with ng build --prod You deploy it to a production server. Everything works fine until you refresh the page. The app throws The requested URL was not found on this server message (Status code 404 not found). It appears that angular routing not working on the production server when you refresh the page. The error appears on the following scenarios When you type the URL directly in the address bar. When you refresh the page The error appears on all the pages except the root page.   Reason for the requested URL was not found on this server error In a Multi-page web application, every time the application needs to display a page it has to send a request to the web server. You can do that by either typing the URL in the address bar, clicking on the Me...

Angular 14 CRUD Operation with Web API .Net 6.0

How to Perform CRUD Operation Using Angular 14 In this article, we will learn the angular crud (create, read, update, delete) tutorial with ASP.NET Core 6 web API. We will use the SQL Server database and responsive user interface for our Web app, we will use the Bootstrap 5. Let's start step by step. Step 1 - Create Database and Web API First we need to create Employee database in SQL Server and web API to communicate with database. so you can use my previous article CRUD operations in web API using net 6.0 to create web API step by step. As you can see, after creating all the required API and database, our API creation part is completed. Now we have to do the angular part like installing angular CLI, creating angular 14 project, command for building and running angular application...etc. Step 2 - Install Angular CLI Now we have to install angular CLI into our system. If you have already installed angular CLI into your system then skip this step.  To install angular CLI ope...

Send an Email via SMTP with MailKit Using .NET 6

How to Send an Email in .NET Core This tutorial show you how to send an email in .NET 6.0 using the MailKit email client library. Install MailKit via NuGet Visual Studio Package Manager Console: Install-Package MailKit How to Send an HTML Email in .NET 6.0 This code sends a simple HTML email using the Gmail SMTP service. There are instructions further below on how to use a few other popular SMTP providers - Gmail, Hotmail, Office 365. // create email message var email = new MimeMessage(); email.From.Add(MailboxAddress.Parse("from_address@example.com")); email.To.Add(MailboxAddress.Parse("to_address@example.com")); email.Subject = "Email Subject"; email.Body = new TextPart(TextFormat.Html) { Text = "<h1>Test HTML Message Body</h1>" }; // send email using var smtp = new SmtpClient(); smtp.Connect("smtp.gmail.com", 587, SecureSocketOptions.StartTls); smtp.Authenticate("[Username]", "[Password]"); smtp.Se...