Set up Azure Database for PostgreSQL in the AppHost
This article is the reference for the Aspire Azure PostgreSQL Hosting integration. It enumerates the AppHost APIs — with examples for both AppHost.cs and apphost.mts — that you use to model Azure Database for PostgreSQL flexible server and database resources in your AppHost project.
If you’re new to the Azure PostgreSQL integration, start with the Get started with Azure Database for PostgreSQL integrations guide. For how consuming apps read the connection information this page exposes, see Connect to Azure PostgreSQL. For the PostgreSQL Entity Framework Core (EF Core) client integration, see Get started with the PostgreSQL Entity Framework Core integrations.
Installation
Section titled “Installation”To start building an Aspire app that uses Azure Database for PostgreSQL, install the 📦 Aspire.Hosting.Azure.PostgreSQL NuGet package:
aspire add azure-postgresLearn more about aspire add in the
command reference.
Or, choose a manual installation approach:
#:package Aspire.Hosting.Azure.PostgreSQL@*<PackageReference Include="Aspire.Hosting.Azure.PostgreSQL" Version="*" />aspire add azure-postgresLearn more about aspire add in the
command reference.
This updates your aspire.config.json with the Azure PostgreSQL hosting integration package:
{ "packages": { "Aspire.Hosting.Azure.PostgreSQL": "13.3.0" }}Add an Azure Database for PostgreSQL resource
Section titled “Add an Azure Database for PostgreSQL resource”Once you’ve installed the hosting integration in your AppHost project, you can add a PostgreSQL flexible server resource and then add a database resource:
var builder = DistributedApplication.CreateBuilder(args);
var postgres = builder.AddAzurePostgresFlexibleServer("postgres");var postgresdb = postgres.AddDatabase("postgresdb");
var exampleProject = builder.AddProject<Projects.ExampleProject>("apiservice").WaitFor(postgresdb).WithReference(postgresdb);
// After adding all resources, run the app...
builder.Build().Run();import { createBuilder } from './.aspire/modules/aspire.mjs';
const builder = await createBuilder();
const postgres = await builder.addAzurePostgresFlexibleServer("postgres");const postgresdb = await postgres.addDatabase("postgresdb");
await builder.addNodeApp("api", "./api", "index.js") .waitFor(postgresdb) .withReference(postgresdb);
// After adding all resources, run the app...The preceding code adds an Azure PostgreSQL flexible server resource named postgres with a database named postgresdb. The withReference / WithReference call injects the connection information into the consuming project.
Add a database to an existing server
Section titled “Add a database to an existing server”You can add multiple named databases to the same flexible server resource:
var builder = DistributedApplication.CreateBuilder(args);
var postgres = builder.AddAzurePostgresFlexibleServer("postgres");var catalogDb = postgres.AddDatabase("catalogdb");var ordersDb = postgres.AddDatabase("ordersdb");
builder.AddProject<Projects.CatalogService>("catalog").WithReference(catalogDb).WaitFor(catalogDb);
builder.AddProject<Projects.OrdersService>("orders").WithReference(ordersDb).WaitFor(ordersDb);
// After adding all resources, run the app...import { createBuilder } from './.aspire/modules/aspire.mjs';
const builder = await createBuilder();
const postgres = await builder.addAzurePostgresFlexibleServer("postgres");const catalogDb = await postgres.addDatabase("catalogdb");const ordersDb = await postgres.addDatabase("ordersdb");
await builder.addNodeApp("catalog", "./catalog", "index.js") .withReference(catalogDb) .waitFor(catalogDb);
await builder.addNodeApp("orders", "./orders", "index.js") .withReference(ordersDb) .waitFor(ordersDb);
// After adding all resources, run the app...You can also customize the underlying database name (as distinct from the Aspire resource name) by passing databaseName in the options:
var postgresdb = postgres.AddDatabase("postgresdb", databaseName: "app_catalog");const postgresdb = await postgres.addDatabase("postgresdb", { databaseName: "app_catalog" });Run as a local container during development
Section titled “Run as a local container during development”The Azure PostgreSQL hosting integration supports running the PostgreSQL server as a local Docker container. This is beneficial for local development and testing — you get a real PostgreSQL server without needing an Azure subscription or connection to an existing Azure PostgreSQL instance.
var builder = DistributedApplication.CreateBuilder(args);
var postgres = builder.AddAzurePostgresFlexibleServer("postgres").RunAsContainer();
var postgresdb = postgres.AddDatabase("postgresdb");
var exampleProject = builder.AddProject<Projects.ExampleProject>("apiservice").WithReference(postgresdb).WaitFor(postgresdb);
// After adding all resources, run the app...
builder.Build().Run();import { function createBuilder(): IDistributedApplicationBuilder
Creates a new distributed application builder
createBuilder } from './.aspire/modules/aspire.mjs';
const const builder: IDistributedApplicationBuilder
builder = await function createBuilder(): IDistributedApplicationBuilder
Creates a new distributed application builder
createBuilder();
const const postgres: AzurePostgresFlexibleServerResource
postgres = await const builder: IDistributedApplicationBuilder
builder.IDistributedApplicationBuilder.addAzurePostgresFlexibleServer(name: string): AzurePostgresFlexibleServerResource
Adds an Azure PostgreSQL Flexible Server resource to the application model.
addAzurePostgresFlexibleServer("postgres");await const postgres: AzurePostgresFlexibleServerResource
postgres.AzurePostgresFlexibleServerResource.runAsContainer(configureContainer?: (obj: PostgresServerResource) => Promise<void>): AzurePostgresFlexibleServerResource
Configures an Azure PostgreSQL Flexible Server resource to run locally in a container.
runAsContainer();
const const postgresdb: AzurePostgresFlexibleServerDatabaseResource
postgresdb = await const postgres: AzurePostgresFlexibleServerResource
postgres.AzurePostgresFlexibleServerResource.addDatabase(name: string, options?: { databaseName?: string;}): AzurePostgresFlexibleServerDatabaseResource (+1 overload)
Adds an Azure PostgreSQL database to the application model.
addDatabase("postgresdb");
await const builder: IDistributedApplicationBuilder
builder.IDistributedApplicationBuilder.addNodeApp(name: string, appDirectory: string, scriptPath: string): NodeAppResource
Adds a node application to the application model. Node should be available on the PATH.
addNodeApp("api", "./api", "index.js") .ExecutableResource.withReference(source: EndpointReference | string | uri, options?: { connectionName?: string; optional?: boolean; name?: string;} | undefined): NodeAppResource (+1 overload)
Adds a reference to another resource
withReference(const postgresdb: AzurePostgresFlexibleServerDatabaseResource
postgresdb) .ExecutableResource.waitFor(dependency: IResource | IResourceWithConnectionString, waitBehavior?: WaitBehavior): NodeAppResource
Waits for another resource to be ready
waitFor(const postgresdb: AzurePostgresFlexibleServerDatabaseResource
postgresdb);
// After adding all resources, run the app...When RunAsContainer / runAsContainer is called, Aspire uses the docker.io/library/postgres container image and generates credentials automatically. The same consuming app code and environment variable names work in both local-container and Azure modes.
Cross-reference: local-container vs Azure PostgreSQL
Section titled “Cross-reference: local-container vs Azure PostgreSQL”If you are developing against a local container and want to switch to Azure PostgreSQL for staging or production, change AddPostgres to AddAzurePostgresFlexibleServer in the AppHost. The connection information injected into consuming apps uses the same environment variable names, so no application code changes are required.
For a comparison of the two hosting integrations, see PostgreSQL Hosting integration.
Connect to an existing Azure PostgreSQL flexible server
Section titled “Connect to an existing Azure PostgreSQL flexible server”If you have an existing Azure Database for PostgreSQL Flexible Server that you want to connect to rather than provisioning a new one, use AsExisting / asExisting:
var builder = DistributedApplication.CreateBuilder(args);
var existingPostgresName = builder.AddParameter("existingPostgresName");var existingPostgresResourceGroup = builder.AddParameter("existingPostgresResourceGroup");
var postgres = builder.AddAzurePostgresFlexibleServer("postgres").AsExisting(existingPostgresName, existingPostgresResourceGroup);
builder.AddProject<Projects.ExampleProject>("apiservice").WithReference(postgres);
// After adding all resources, run the app...
builder.Build().Run();import { function createBuilder(): IDistributedApplicationBuilder
Creates a new distributed application builder
createBuilder } from './.aspire/modules/aspire.mjs';
const const builder: IDistributedApplicationBuilder
builder = await function createBuilder(): IDistributedApplicationBuilder
Creates a new distributed application builder
createBuilder();
const const existingPostgresName: ParameterResource
existingPostgresName = await const builder: IDistributedApplicationBuilder
builder.IDistributedApplicationBuilder.addParameter(name: string, options?: { value?: string; publishValueAsDefault?: boolean; secret?: boolean;}): ParameterResource (+1 overload)
Adds a parameter resource
addParameter("existingPostgresName");const const existingPostgresResourceGroup: ParameterResource
existingPostgresResourceGroup = await const builder: IDistributedApplicationBuilder
builder.IDistributedApplicationBuilder.addParameter(name: string, options?: { value?: string; publishValueAsDefault?: boolean; secret?: boolean;}): ParameterResource (+1 overload)
Adds a parameter resource
addParameter("existingPostgresResourceGroup");
const const postgres: AzurePostgresFlexibleServerResource
postgres = await const builder: IDistributedApplicationBuilder
builder.IDistributedApplicationBuilder.addAzurePostgresFlexibleServer(name: string): AzurePostgresFlexibleServerResource
Adds an Azure PostgreSQL Flexible Server resource to the application model.
addAzurePostgresFlexibleServer("postgres");await const postgres: AzurePostgresFlexibleServerResource
postgres.AzureBicepResource.asExisting(name: string | ParameterResource, resourceGroup?: string | ParameterResource): IAzureResource
Marks the resource as an existing resource in both run and publish modes.
asExisting(const existingPostgresName: ParameterResource
existingPostgresName, const existingPostgresResourceGroup: ParameterResource
existingPostgresResourceGroup);
await const builder: IDistributedApplicationBuilder
builder.IDistributedApplicationBuilder.addNodeApp(name: string, appDirectory: string, scriptPath: string): NodeAppResource
Adds a node application to the application model. Node should be available on the PATH.
addNodeApp("api", "./api", "index.js") .ExecutableResource.withReference(source: EndpointReference | string | uri, options?: { connectionName?: string; optional?: boolean; name?: string;} | undefined): NodeAppResource (+1 overload)
Adds a reference to another resource
withReference(const postgres: AzurePostgresFlexibleServerResource
postgres);
// After adding all resources, run the app...For more information on treating Azure resources as existing, see Use existing Azure resources.
Configure password authentication
Section titled “Configure password authentication”By default, the Azure PostgreSQL flexible server is configured to use Microsoft Entra ID authentication — no username or password is stored in your app configuration. If you need password authentication (for example, to connect with a legacy client that doesn’t support Entra tokens), call WithPasswordAuthentication / withPasswordAuthentication:
var builder = DistributedApplication.CreateBuilder(args);
var username = builder.AddParameter("username", secret: true);var password = builder.AddParameter("password", secret: true);
var postgres = builder.AddAzurePostgresFlexibleServer("postgres").WithPasswordAuthentication(username, password);
var postgresdb = postgres.AddDatabase("postgresdb");
builder.AddProject<Projects.ExampleProject>("apiservice").WithReference(postgresdb).WaitFor(postgresdb);
// After adding all resources, run the app...
builder.Build().Run();import { function createBuilder(): IDistributedApplicationBuilder
Creates a new distributed application builder
createBuilder } from './.aspire/modules/aspire.mjs';
const const builder: IDistributedApplicationBuilder
builder = await function createBuilder(): IDistributedApplicationBuilder
Creates a new distributed application builder
createBuilder();
const const username: ParameterResource
username = await const builder: IDistributedApplicationBuilder
builder.IDistributedApplicationBuilder.addParameter(name: string, options?: { value?: string; publishValueAsDefault?: boolean; secret?: boolean;}): ParameterResource (+1 overload)
Adds a parameter resource
addParameter("username", { secret?: boolean | undefined
secret: true });const const password: ParameterResource
password = await const builder: IDistributedApplicationBuilder
builder.IDistributedApplicationBuilder.addParameter(name: string, options?: { value?: string; publishValueAsDefault?: boolean; secret?: boolean;}): ParameterResource (+1 overload)
Adds a parameter resource
addParameter("password", { secret?: boolean | undefined
secret: true });
const const postgres: AzurePostgresFlexibleServerResource
postgres = await const builder: IDistributedApplicationBuilder
builder.IDistributedApplicationBuilder.addAzurePostgresFlexibleServer(name: string): AzurePostgresFlexibleServerResource
Adds an Azure PostgreSQL Flexible Server resource to the application model.
addAzurePostgresFlexibleServer("postgres");await const postgres: AzurePostgresFlexibleServerResource
postgres.AzurePostgresFlexibleServerResource.withPasswordAuthentication(options?: { keyVaultBuilder?: IAzureKeyVaultResource; userName?: string | ParameterResource; password?: string | ParameterResource;}): AzurePostgresFlexibleServerResource (+1 overload)
Configures password authentication for Azure PostgreSQL Flexible Server
withPasswordAuthentication({ userName?: string | ParameterResource | undefined
userName: const username: ParameterResource
username, password?: string | ParameterResource | undefined
password });
const const postgresdb: AzurePostgresFlexibleServerDatabaseResource
postgresdb = await const postgres: AzurePostgresFlexibleServerResource
postgres.AzurePostgresFlexibleServerResource.addDatabase(name: string, options?: { databaseName?: string;}): AzurePostgresFlexibleServerDatabaseResource (+1 overload)
Adds an Azure PostgreSQL database to the application model.
addDatabase("postgresdb");
await const builder: IDistributedApplicationBuilder
builder.IDistributedApplicationBuilder.addNodeApp(name: string, appDirectory: string, scriptPath: string): NodeAppResource
Adds a node application to the application model. Node should be available on the PATH.
addNodeApp("api", "./api", "index.js") .ExecutableResource.withReference(source: EndpointReference | string | uri, options?: { connectionName?: string; optional?: boolean; name?: string;} | undefined): NodeAppResource (+1 overload)
Adds a reference to another resource
withReference(const postgresdb: AzurePostgresFlexibleServerDatabaseResource
postgresdb) .ExecutableResource.waitFor(dependency: IResource | IResourceWithConnectionString, waitBehavior?: WaitBehavior): NodeAppResource
Waits for another resource to be ready
waitFor(const postgresdb: AzurePostgresFlexibleServerDatabaseResource
postgresdb);
// After adding all resources, run the app...When password authentication is enabled, the Username and Password connection properties are also injected into consuming apps. See Connect to Azure PostgreSQL for the full property reference.
Provisioning-generated Bicep
Section titled “Provisioning-generated Bicep”If you’re new to Bicep, it’s a domain-specific language for defining Azure resources. With Aspire, you don’t need to write Bicep by hand — the provisioning APIs generate it for you. When you publish your app, the generated Bicep is output alongside the manifest file. When you add an Azure PostgreSQL resource, the following Bicep is generated:
@description('The location for the resource(s) to be deployed.')param location string = resourceGroup().location
resource postgres_flexible 'Microsoft.DBforPostgreSQL/flexibleServers@2024-08-01' = { name: take('postgresflexible-${uniqueString(resourceGroup().id)}', 63) location: location properties: { authConfig: { activeDirectoryAuth: 'Enabled' passwordAuth: 'Disabled' } availabilityZone: '1' backup: { backupRetentionDays: 7 geoRedundantBackup: 'Disabled' } highAvailability: { mode: 'Disabled' } storage: { storageSizeGB: 32 } version: '16' } sku: { name: 'Standard_B1ms' tier: 'Burstable' } tags: { 'aspire-resource-name': 'postgres-flexible' }}
resource postgreSqlFirewallRule_AllowAllAzureIps 'Microsoft.DBforPostgreSQL/flexibleServers/firewallRules@2024-08-01' = { name: 'AllowAllAzureIps' properties: { endIpAddress: '0.0.0.0' startIpAddress: '0.0.0.0' } parent: postgres_flexible}
output connectionString string = 'Host=${postgres_flexible.properties.fullyQualifiedDomainName}'
output name string = postgres_flexible.nameAdditionally, role assignments are created for the Azure resource in a separate module:
@description('The location for the resource(s) to be deployed.')param location string = resourceGroup().location
param postgres_flexible_outputs_name string
param principalType string
param principalId string
param principalName string
resource postgres_flexible 'Microsoft.DBforPostgreSQL/flexibleServers@2024-08-01' existing = { name: postgres_flexible_outputs_name}
resource postgres_flexible_admin 'Microsoft.DBforPostgreSQL/flexibleServers/administrators@2024-08-01' = { name: principalId properties: { principalName: principalName principalType: principalType } parent: postgres_flexible}The roles Bicep creates an administrator record on the flexible server, granting the deploying principal role-based access. This is the mechanism that underpins Entra ID (managed identity) authentication — no password is required because the principal’s identity is used to obtain short-lived tokens at runtime.
The generated Bicep is a starting point and is influenced by changes to the provisioning infrastructure in C#. Customizations to the Bicep file directly will be overwritten, so make changes through the C# provisioning APIs to ensure they are reflected in the generated files.
Customize provisioning infrastructure
Section titled “Customize provisioning infrastructure”All Aspire Azure resources are subclasses of the AzureProvisioningResource type. This type enables the customization of the generated Bicep by providing a fluent API to configure the Azure resources using the ConfigureInfrastructure API:
var builder = DistributedApplication.CreateBuilder(args);
builder.AddAzurePostgresFlexibleServer("postgres") .ConfigureInfrastructure(infra => { var flexibleServer = infra.GetProvisionableResources() .OfType<PostgreSqlFlexibleServer>() .Single();
flexibleServer.Sku = new PostgreSqlFlexibleServerSku { Tier = PostgreSqlFlexibleServerSkuTier.Burstable, }; flexibleServer.HighAvailability = new PostgreSqlFlexibleServerHighAvailability { Mode = PostgreSqlFlexibleServerHighAvailabilityMode.ZoneRedundant, StandbyAvailabilityZone = "2", }; flexibleServer.Tags.Add("ExampleKey", "Example value"); });
// After adding all resources, run the app...
builder.Build().Run();The preceding code:
- Chains a call to the
ConfigureInfrastructureAPI:- The
infraparameter is an instance of theAzureResourceInfrastructuretype. - The provisionable resources are retrieved by calling the
GetProvisionableResourcesmethod. - The single
PostgreSqlFlexibleServeris retrieved. - The
skuis set withPostgreSqlFlexibleServerSkuTier.Burstable. - The high availability properties are set with
PostgreSqlFlexibleServerHighAvailabilityMode.ZoneRedundantin standby availability zone"2". - A tag is added to the flexible server with a key of
ExampleKeyand a value ofExample value.
- The
There are many more configuration options available to customize the PostgreSQL resource. For more information, see Azure.Provisioning customization.
Database creation after initial deployment
Section titled “Database creation after initial deployment”When you call AddDatabase / addDatabase on an Azure PostgreSQL resource, Aspire provisions the database during the first deployment. On subsequent deployments with azd up, only the server-level Bicep is redeployed — databases that already exist are not recreated or deleted.
If you need to add a new database to an existing server, redeploy your application with the updated AppHost code or manually create the database by using the Azure portal or the PostgreSQL CLI:
az postgres flexible-server db create \ --resource-group <resource-group> \ --server-name <server-name> \ --database-name <new-database-name>