Files
Stephon Brown 4dece669ea Migrate Web Frontend to Official Aspire JavaScript Hosting (#7731)
* chore(deps): remove Node.js community plugin

* refactor(AppHost): integrate web frontend with Aspire JS hosting

* docs(AppHost): update web frontend and Aspire documentation

* feat(AppHost): configure Azurite emulator with persistent data volume

* docs(AppHost): fix ClientsPath description to reference worktrees section

* fix(Apphost): run dotnet format
2026-05-28 13:33:47 -04:00
..

Aspire AppHost

.NET Aspire orchestrates the entire Bitwarden server local development environment — supporting infrastructure and the Bitwarden application services — from a single command, replacing the manual docker-compose workflow.

Prerequisites

Requirement Notes
.NET SDK 10 Required by Aspire
Docker Desktop Runs the supporting infrastructure containers
PowerShell (pwsh) Used by migration and secrets scripts
Completed server setup dev/secrets.json must exist — follow the setup guide first

Quick Start

cd AppHost
dotnet run

The Aspire dashboard opens automatically in your browser. All resources start in dependency order; the services wait for the database and secrets setup to finish before launching.

What Gets Started

Resource Type Purpose
setup-secrets Executable Runs dev/setup_secrets.ps1 — applies dev/secrets.json to all projects
mssql SQL Server 2022 container Persistent data volume, port 1433
run-db-migrations Executable Runs dev/migrate.ps1 against vault_dev (or self_host_dev)
azurite Azure Storage emulator Blob :10000 · Queue :10001 · Table :10002 · persistent data volume
azurite-setup Executable Runs dev/setup_azurite.ps1 after Azurite is ready
mailcatcher Container SMTP :10250 · Web UI :1080
redis Container Redis with AOF persistence, port 6379
idp SimpleSAMLphp container SAML IdP for SSO testing (explicit start from the dashboard)
admin .NET project Admin portal
api .NET project Main API (waits for Azurite)
billing .NET project Billing service
events .NET project Events service (waits for Azurite)
eventsProcessor .NET project Events processor (waits for Azurite)
icons .NET project Icons service
identity .NET project Identity / auth service
notifications .NET project Notifications service (waits for Azurite)
scim .NET project SCIM provisioning service
sso .NET project SSO service

Configuration

All configuration lives in appsettings.Development.json(see Security).

Service Ports

Each service's BasePort is pre-filled in appsettings.Development.json to match the port defined in each service's own Properties/launchSettings.json. No action is needed unless a port conflicts with something else on your machine — in that case override it via user secrets:

dotnet user-secrets set "Services:api:BasePort" "4001"

Database Password

dotnet user-secrets set "Database:Password" "<your-sa-password>"

Full Configuration Reference

Key Default Description
SelfHost false Switch to self-hosted mode (see Self-Hosted Mode)
ClientsPath ../../clients/apps Path to the clients repo's apps/ directory (see Git Worktrees)
WorkingDirectory ../dev Directory where dev scripts are resolved
Services:<name>:BasePort see appsettings.Development.json HTTP port for each service; pre-filled to match each service's launchSettings.json
Database:Image mssql/server:2022-latest Docker image for SQL Server
Database:Port 1433 Host port mapped to the SQL Server container
Database:Password (empty) SA password for the SQL Server container
Database:SelfHostPassword (empty) SA password used in self-hosted mode
Scripts:DbMigration migrate.ps1 Migration script filename (relative to WorkingDirectory)
Scripts:AzuriteSetup setup_azurite.ps1 Azurite setup script filename
Scripts:SecretsSetup setup_secrets.ps1 Secrets setup script filename
MailCatcher:Image sj26/mailcatcher:latest Docker image for MailCatcher
MailCatcher:SmtpPort 10250 Host SMTP port
MailCatcher:WebPort 1080 MailCatcher web UI port
NgrokAuthToken (empty) ngrok auth token (used only when ngrok plugin is enabled)
WebFrontend:Port 8080 Web frontend port
WebFrontend:Url https://bitwarden.test:8080 Web frontend URL shown in the dashboard

Optional Features

Web Frontend

Runs the web client alongside the server services. Requires the Bitwarden clients repo cloned as a sibling to server.

  1. If the clients repo is not at ../../clients/apps, override the path:

    dotnet user-secrets set "ClientsPath" "<path/to/clients/apps>"
    
  2. Run dotnet run as normal. The web-frontend resource starts with explicit start — open the Aspire dashboard and start it manually when you're ready.

Ngrok (Billing Webhook Tunneling)

Exposes the billing service through a public ngrok tunnel, useful for testing Stripe webhooks locally.

  1. Create an AppHost.csproj.user file next to AppHost.csproj (it is covered by .gitignore):

    <Project>
      <PropertyGroup>
        <EnableNgrokCommunityPlugin>true</EnableNgrokCommunityPlugin>
      </PropertyGroup>
    </Project>
    
  2. Set your ngrok auth token:

    dotnet user-secrets set "NgrokAuthToken" "<your-ngrok-auth-token>"
    
  3. The billing-webhook-ngrok-endpoint resource starts with explicit start — launch it from the Aspire dashboard when you need the tunnel active.

Dynamic Additional Projects

Load an extra project into the orchestration without touching source files — useful for temporary integrations or in-progress work:

# Add a project
dotnet user-secrets set "AdditionalProjects:<name>:Path" "<relative/path/to/Project.csproj>"

# Optionally wire it as a reference into an existing service
dotnet user-secrets set "AdditionalProjects:<name>:ReferencedBy:0" "api"

Replace <name> with any identifier you choose. Multiple ReferencedBy entries are indexed (0, 1, 2, …).

Self-Hosted Mode

Switch to a self-hosted database configuration:

dotnet user-secrets set "SelfHost" "true"
dotnet user-secrets set "Database:SelfHostPassword" "<password>"

In self-hosted mode:

  • The database name changes from vault_dev to self_host_dev
  • The migration script receives the -self-hosted flag
  • Each service's effective port becomes BasePort + 1

Aspire Dashboard

The dashboard opens automatically when you run the AppHost. You can also navigate to it directly:

Profile URL
HTTPS (default) https://localhost:17271
HTTP http://localhost:15055

The dashboard shows live resource status, structured logs, distributed traces, and environment variables for every resource.

Security — Do Not Commit Local Settings or Tokens

Warning: Never commit local configuration values or secrets to the repository.

  • appsettings.Development.json is checked in with intentionally empty defaults. Local overrides belong in user secrets, not in that file — edits to it will appear in git diff and risk accidental commit.
  • Database:Password, Database:SelfHostPassword, and NgrokAuthToken are sensitive — always store them with dotnet user-secrets, never in any appsettings.*.json file.
  • User secrets are stored outside the repo in your OS profile, keyed by the UserSecretsId in AppHost.csproj, and are never tracked by git.
  • If you create an appsettings.local.json, add it to .gitignore before writing any values to it.

Git Worktrees

Path-based settings resolve relative to where you run dotnet run. If your worktree lives in a different location than the main checkout, those paths won't resolve correctly. Use absolute paths for any such setting:

  • ClientsPath defaults to ../../clients/apps — override if your worktree is not alongside the clients repo:

    dotnet user-secrets set "ClientsPath" "<absolute/path/to/clients/apps>"
    
  • AdditionalProjects:<name>:Path — use an absolute path when adding projects via user secrets in a worktree:

    dotnet user-secrets set "AdditionalProjects:<name>:Path" "<absolute/path/to/Project.csproj>"
    

Troubleshooting

Symptom Fix
Secrets not applied to services Re-run setup-secrets from the Aspire dashboard, or verify dev/secrets.json exists
SQL Server container won't start Confirm Docker Desktop is running and port 1433 is free
Migrations fail immediately Ensure pwsh (PowerShell) is on your $PATH
Port conflicts on startup Set the conflicting Services:<name>:BasePort to a free port via user secrets
Services stuck waiting Check the dashboard logs for setup-secrets or run-db-migrations errors