Skip to content
DocsTry Aspire
DocsTry

Set up Azure Database for PostgreSQL in the AppHost

Azure Database for PostgreSQL logo

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.

To start building an Aspire app that uses Azure Database for PostgreSQL, install the 📦 Aspire.Hosting.Azure.PostgreSQL NuGet package:

Terminal
aspire add azure-postgres

Learn more about aspire add in the command reference.

This updates your aspire.config.json with the Azure PostgreSQL hosting integration package:

aspire.config.json
{
"packages": {
"Aspire.Hosting.Azure.PostgreSQL": "13.5.3"
}
}

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:

apphost.mts
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.

You can add multiple named databases to the same flexible server resource:

apphost.mts
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:

apphost.mts
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.

apphost.mts
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:

apphost.mts
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.

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:

apphost.mts
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.

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:

Generated Bicep — postgres.bicep
@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.name

Additionally, role assignments are created for the Azure resource in a separate module:

Generated Bicep — postgres-flexible-roles.bicep
@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.

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:

AppHost.cs
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 ConfigureInfrastructure API:
    • The infra parameter is an instance of the AzureResourceInfrastructure type.
    • The provisionable resources are retrieved by calling the GetProvisionableResources method.
    • The single PostgreSqlFlexibleServer is retrieved.
    • The sku is set with PostgreSqlFlexibleServerSkuTier.Burstable.
    • The high availability properties are set with PostgreSqlFlexibleServerHighAvailabilityMode.ZoneRedundant in standby availability zone "2".
    • A tag is added to the flexible server with a key of ExampleKey and a value of Example value.

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:

Create database manually
az postgres flexible-server db create \
--resource-group <resource-group> \
--server-name <server-name> \
--database-name <new-database-name>