Skip to main content
SEMastery

How To Deploy a .NET App to Azure Using Neon Postgres and .NET Aspire

A beginner-friendly, step-by-step guide to deploying a .NET 10 web API to Azure Container Apps with a free Neon serverless Postgres database and .NET Aspire.

13 min readUpdated March 6, 2026

How To Deploy a .NET App to Azure Using Neon Postgres and .NET Aspire

Think about ordering food from your favourite restaurant on a delivery app. You do not go to the kitchen. You do not cook. You just tap a button, and a little while later, hot food arrives at your door. Someone else handles the stove, the packing, and the scooter.

Deploying an app to the cloud can feel the same way. You write your code on your laptop. Then you tap a few buttons (well, type a few commands), and your app shows up live on the internet, ready for the whole world. You do not rent servers. You do not set up machines by hand. The cloud handles the boring parts.

In this guide we will do exactly that. We will take a small .NET web API, give it a Neon Postgres database, wrap it with .NET Aspire, and ship it to Azure. By the end, your app will be running online, talking to a real database in the cloud. And the best part: the database is free to start, and Aspire does most of the heavy lifting.

The three tools we will use

Before we cook, let us meet our ingredients. Each one has a simple job.

ToolWhat it isWhy we use it
.NET AspireA toolkit that wires up your services and databasesIt connects everything and creates the cloud setup for you
Neon PostgresA serverless Postgres database in the cloudIt is free to start, sets up in under a minute, and scales to zero
Azure Container AppsA place in Azure to run your containersYou do not manage servers; it scales up and down on its own

Here is how they fit together at a high level.

The big picture: your laptop builds the app, azd ships it to Azure, and the app talks to Neon.

Why Neon for the database?

A normal database server is like keeping a car running in your driveway all day, just in case you need it. It costs money even when nobody is using it.

Neon is different. It is a serverless Postgres database. When nobody is using it, it can "scale to zero" and rest. When a request comes in, it wakes up. You do not pay for the idle time, and you do not install anything. You sign up, click "create database", and you get a connection string in less than a minute.

Because Neon speaks plain Postgres, your normal .NET tools work without any tricks. Entity Framework Core, the Npgsql driver, and Aspire all treat it like any other Postgres database.

Step 1: Install the tools

You need three things on your machine. If you already have them, skip ahead.

  1. The .NET 10 SDK (the current LTS release).
  2. The Aspire CLI, which gives you the aspire command.
  3. The Azure Developer CLI (azd), which ships your app to Azure.

You can install the Aspire CLI and check your setup like this:

// These are shell commands, not C#. Run them in your terminal.
// 1. Install the Aspire CLI
// dotnet tool install -g aspire.cli
 
// 2. Check the .NET version (should be 10.x)
// dotnet --version
 
// 3. Install the Azure Developer CLI (Windows example)
// winget install microsoft.azd
 
// 4. Log in to Azure
// azd auth login

That last command opens your browser and asks you to sign in to Azure. Once it says you are logged in, you are ready.

Step 2: Create a Neon database

Go to the Neon website and sign up for the free tier. You do not need a credit card. After you sign in:

  1. Click Create Project.
  2. Pick a name and a region close to your users.
  3. Neon makes a database and shows you a connection string.

Copy the pooled connection string. It looks something like this (the real one has your own host and password):

// A Neon connection string looks like this.
// Keep it secret. Never commit it to git.
const string neonConnection =
    "Host=ep-cool-name-123456.eu-central-1.aws.neon.tech;" +
    "Database=neondb;" +
    "Username=neondb_owner;" +
    "Password=YOUR_PASSWORD;" +
    "SSL Mode=Require;" +
    "Pooling=true";

The flow of getting your database ready looks like this.

Setting up Neon

Sign up
Create project
Copy string
Save secret

Steps

1

Sign up

Free tier, no card

2

Create project

Pick name and region

3

Copy string

Use the pooled URL

4

Save secret

Keep it out of git

Four small clicks and you have a real cloud database.

Step 3: Build a small web API

Let us make a tiny Products API. It will store products in Neon Postgres. We start with a normal ASP.NET Core minimal API project.

First, our product model and the EF Core DbContext:

// Product.cs
public class Product
{
    public Guid Id { get; set; }
    public string Name { get; set; } = string.Empty;
    public decimal Price { get; set; }
}
 
// AppDbContext.cs
using Microsoft.EntityFrameworkCore;
 
public class AppDbContext(DbContextOptions<AppDbContext> options)
    : DbContext(options)
{
    public DbSet<Product> Products => Set<Product>();
}

Now we wire it up in Program.cs. Notice the line AddNpgsqlDbContext. That comes from the Aspire Postgres integration. It reads a connection string by name, sets up retries, health checks, and logging for us. We do not have to write any of that plumbing by hand.

// Program.cs
var builder = WebApplication.CreateBuilder(args);
 
// Aspire service defaults: logging, health checks, tracing.
builder.AddServiceDefaults();
 
// This name "neondb" must match the name used in the AppHost.
builder.AddNpgsqlDbContext<AppDbContext>("neondb");
 
var app = builder.Build();
 
app.MapGet("/products", async (AppDbContext db) =>
    await db.Products.ToListAsync());
 
app.MapPost("/products", async (AppDbContext db, Product product) =>
{
    product.Id = Guid.NewGuid();
    db.Products.Add(product);
    await db.SaveChangesAsync();
    return Results.Created($"/products/{product.Id}", product);
});
 
app.Run();

The connection string name "neondb" is important. It is the secret handshake between the app and Aspire. Whatever name you use here, you must use the same name in the AppHost in the next step.

Step 4: Add .NET Aspire

Now we add the Aspire brain. In Visual Studio you can right-click your API project and choose Add .NET Aspire Orchestrator Support. This creates two new projects:

  • AppHost — the conductor that starts everything and knows how the pieces connect.
  • ServiceDefaults — shared settings for logging, health, and tracing.

Inside the AppHost, we tell Aspire about our Neon database and our API. Because Neon is an outside database (not one Aspire runs for us), we add it as a connection string resource.

// AppHost.cs
var builder = DistributedApplication.CreateBuilder(args);
 
// Reference the Neon database by its connection string.
// The value lives in configuration / secrets, not here.
var neondb = builder.AddConnectionString("neondb");
 
// Our API uses that database.
builder.AddProject<Projects.ProductsApi>("products-api")
       .WithReference(neondb)
       .WaitFor(neondb);
 
builder.Build().Run();

Here is what happens when you press F5 and Aspire starts everything up.

When you run the AppHost, Aspire reads the connection string and hands it to your API.

Where does the connection string live during development?

You should never paste a real password into a file you commit to git. For local work, use user secrets, which store the value on your own machine only.

// Run this in the AppHost project folder (shell command).
// dotnet user-secrets set "ConnectionStrings:neondb" "Host=...;Password=...;SSL Mode=Require"

Aspire picks up ConnectionStrings:neondb automatically because that matches the name "neondb" we used. Run the app now, open the Aspire dashboard, and you should see your API talking to Neon. You can hit /products and add a product. It saves to your real cloud database.

Step 5: Understand how azd deploys Aspire

Before we deploy, let us see what the Azure Developer CLI actually does. It is helpful to know, because then nothing feels like magic.

When you run azd up, a chain of steps happens. Aspire describes your app in a manifest file. The azd tool reads that manifest and writes Bicep files (Azure's infrastructure language). It provisions the Azure resources, builds your app into a container image, pushes that image to a registry, and finally runs it on Azure Container Apps.

What azd does on deploy

Manifest
Provision
Build image
Push
Run on ACA

Steps

1

Manifest

Aspire describes the app

2

Provision

Bicep creates Azure resources

3

Build image

dotnet publish makes a container

4

Push

Image goes to the registry

5

Run on ACA

Container Apps hosts it

One command runs this whole pipeline for you.

This table sums up the two main commands you will use.

CommandWhat it doesWhen you run it
azd initLooks at your Aspire app and sets up the projectOnce, at the start
azd upProvisions Azure and deploys your appEvery time you want to ship
azd provisionCreates or updates Azure resources onlyWhen you change infrastructure
azd deployBuilds and pushes your app onlyWhen you only changed code

Step 6: Deploy to Azure

Now the fun part. Open a terminal in your AppHost folder and run two commands.

// Shell commands. Run from the AppHost project folder.
 
// 1. Set up the Azure project files (run once).
// azd init
 
// 2. Provision Azure resources and deploy the app.
// azd up

The first time, azd will ask you a few questions: which Azure subscription to use, which region, and an environment name (like products-prod). Pick a region near your users.

Then azd up goes to work. It can take a few minutes. It creates a resource group, a container registry, a Container Apps environment, and your app. When it finishes, it prints a public URL. Open that URL and add /products to the end. Your API is now live on the internet.

Telling Azure about the Neon secret

Your app still needs the Neon connection string in the cloud. You set it as a secret for your environment so it is never stored in code:

// Shell command. Stores the Neon string as an azd environment secret.
// azd env set ConnectionStrings__neondb "Host=...;Password=...;SSL Mode=Require"

Notice the double underscore __. In environment variables, __ means the same thing as : in JSON config. So ConnectionStrings__neondb maps to ConnectionStrings:neondb, which is the exact name our app and AppHost expect. Run azd up again after setting it, and your live app will connect to Neon.

Here is the full picture of the deployed system.

The live system: visitors hit Azure Container Apps, which talks to Neon Postgres.

Step 7: Run database migrations

Your tables need to exist in Neon before the app can save data. EF Core uses migrations to create them. You create a migration once, then apply it.

// Shell commands. Run from the API project folder.
 
// 1. Create the first migration.
// dotnet ef migrations add InitialCreate
 
// 2. Apply it to the Neon database.
// dotnet ef database update

A common beginner mistake is forgetting this step and then wondering why the app crashes with "relation does not exist". That error just means the table is missing. Run database update, and it goes away.

For a real app, many teams run migrations automatically on startup or as a small one-off job during deploy. For learning, running the command by hand is perfectly fine.

Common problems and easy fixes

Everyone hits a few bumps the first time. Here are the usual ones.

  • "SSL connection is required." Neon needs SSL. Make sure your connection string ends with SSL Mode=Require.
  • "The name neondb does not match." The name in AddConnectionString, AddNpgsqlDbContext, and your secret key must all be the same word.
  • azd cannot find a subscription. Run azd auth login again and make sure you picked the right Azure account.
  • App starts but has no data. You likely forgot the migration. Run dotnet ef database update.
  • Slow first request. Neon scales to zero when idle, so the very first call after a quiet period wakes it up. This is normal and only takes a moment.

A note on cost and safety

Both Neon and Azure have free or low-cost tiers that are great for learning. Still, when you deploy real things to the cloud, keep two habits:

  1. Never commit secrets. Connection strings and passwords belong in user secrets locally and in Azure secrets when deployed. Add your secret files to .gitignore.
  2. Clean up when done practising. If you made resources only to learn, you can remove them with one command so nothing keeps running.
// Shell command. Deletes the Azure resources azd created.
// azd down

This tears down the Azure resources for your environment. Your Neon database stays unless you delete it from the Neon console. Cleaning up means you do not get surprise charges later.

Why this combo is great for beginners

This setup hides a lot of hard cloud work. You did not write a single Dockerfile. You did not click around the Azure portal creating servers. You did not set up networking by hand. Aspire described your app, Neon gave you a database in seconds, and azd did the deploy in one command.

That is the real lesson here. Modern .NET tooling lets a beginner ship a real, database-backed API to the cloud in an afternoon. The same skills scale up: when your app grows from one service to ten, Aspire and azd handle that growth too. You learn the small version now, and the big version later feels familiar.

References and further reading

Quick recap

  • Neon Postgres gives you a free, serverless database in under a minute, and it speaks plain Postgres so EF Core just works.
  • .NET Aspire connects your API to the database with AddConnectionString in the AppHost and AddNpgsqlDbContext in the app, using one shared name.
  • Keep the Neon connection string in user secrets locally and in Azure secrets when deployed, never in git.
  • The Azure Developer CLI (azd up) builds your container, provisions Azure, and runs your app on Azure Container Apps with one command.
  • Run EF Core migrations (dotnet ef database update) so your tables exist before the app saves data.
  • Use azd down to clean up Azure resources when you are done practising, so you avoid surprise costs.

Related Posts