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.
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.
| Tool | What it is | Why we use it |
|---|---|---|
| .NET Aspire | A toolkit that wires up your services and databases | It connects everything and creates the cloud setup for you |
| Neon Postgres | A serverless Postgres database in the cloud | It is free to start, sets up in under a minute, and scales to zero |
| Azure Container Apps | A place in Azure to run your containers | You do not manage servers; it scales up and down on its own |
Here is how they fit together at a high level.
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.
- The .NET 10 SDK (the current LTS release).
- The Aspire CLI, which gives you the
aspirecommand. - 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 loginThat 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:
- Click Create Project.
- Pick a name and a region close to your users.
- 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
Steps
Sign up
Free tier, no card
Create project
Pick name and region
Copy string
Use the pooled URL
Save secret
Keep it out of git
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.
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
Steps
Manifest
Aspire describes the app
Provision
Bicep creates Azure resources
Build image
dotnet publish makes a container
Push
Image goes to the registry
Run on ACA
Container Apps hosts it
This table sums up the two main commands you will use.
| Command | What it does | When you run it |
|---|---|---|
| azd init | Looks at your Aspire app and sets up the project | Once, at the start |
| azd up | Provisions Azure and deploys your app | Every time you want to ship |
| azd provision | Creates or updates Azure resources only | When you change infrastructure |
| azd deploy | Builds and pushes your app only | When 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 upThe 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.
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 updateA 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 loginagain 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:
- Never commit secrets. Connection strings and passwords belong in user secrets locally and in Azure secrets when deployed. Add your secret files to
.gitignore. - 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 downThis 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
- Deploy Aspire projects to Azure Container Apps (Microsoft Learn)
- Deploy a .NET Aspire project to Azure Container Apps using azd — in-depth (Microsoft Learn)
- Get started with the PostgreSQL integrations (Aspire)
- Connect a .NET (C#) application to Neon Postgres (Neon Docs)
- How To Deploy .NET Application to Azure using Neon Postgres and .NET Aspire (Anton Martyniuk)
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
AddConnectionStringin the AppHost andAddNpgsqlDbContextin 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
.NET Aspire: A Game Changer for Cloud-Native Development
A beginner-friendly guide to .NET Aspire, the cloud-native stack that orchestrates your services, databases, and dashboards with one simple command.
Building a Multitenant Cloud Application With Azure Functions and Neon Postgres
A beginner-friendly guide to building a multitenant cloud app with Azure Functions and Neon serverless Postgres, using a database-per-tenant design in .NET.
Getting Started With .NET Aspire 13: Building and Deploying an App
A beginner-friendly guide to .NET Aspire 13: build a small app with PostgreSQL and Redis, watch it run on the dashboard, then deploy with Docker Compose.
How .NET Aspire Simplifies Service Discovery for Your Apps
Learn how .NET Aspire service discovery lets your services find each other by name, with no hardcoded URLs, ports, or environment headaches.
Using .NET Aspire With the Docker Publisher: A Beginner Guide
Learn how to turn a .NET Aspire app into a ready-to-run docker-compose.yaml with one command using the Docker publisher. Simple, step-by-step guide.
Streamlining .NET 9 Deployment With GitHub Actions and Azure
A friendly, step-by-step guide to deploying a .NET 9 app to Azure App Service using GitHub Actions, with secure OIDC login, build, test, and release.