RunAsEmulator/RunAsContainer are no-ops in publish mode — problematic for docker-compose targets
- Dominant language
- C#
- Stars
- 6.3k
- Forks
- 991
- Avg merge
- 2d 15h
- Merged PRs (30d)
- 196
Description
## Summary
`RunAsEmulator()` and `RunAsContainer()` on Azure resource types (e.g., `AddAzureStorage().RunAsEmulator()`, `AddAzurePostgresFlexibleServer().RunAsContainer()`) check `IsPublishMode` and return early, making them no-ops during `aspire publish` / `aspire deploy`. This makes sense for Azure targets like ACA where you want real provisioned resources, but it creates a gap when publishing to **docker-compose** via `AddDockerComposeEnvironment`.
## Root Cause
The core problem is that **the `IsPublishMode` check conflates "publish mode" with "deploy to Azure."** Publishing doesn't necessarily mean Azure infrastructure is available to provision the real resource — docker-compose is a publish target with no Azure infra.
## Problem
When targeting docker-compose, users need emulator/container resources to appear in the generated `docker-compose.yaml`. For example:
- **Azure Storage**: `RunAsEmulator()` should produce an Azurite container in the compose file, but instead the `AzureStorageResource` is left as a bare Azure provisioning resource that the docker-compose publisher can't handle (Azure resources aren't compute resources, so they're excluded from compose output entirely).
- **Azure PostgreSQL**: `RunAsContainer()` should produce a PostgreSQL container, but the early return leaves it as an Azure Flexible Server resource.
### Current workarounds
- **PostgreSQL**: Use `AddPostgres()` instead of `AddAzurePostgresFlexibleServer()` when targeting docker-compose. This works but requires conditional logic in the AppHost.
- **Azure Storage**: No clean workaround — `AddAzureStorage()` is the only way to get the `AzureBlobStorageResource` type with proper connection string wiring. Users must manually replicate what `RunAsEmulator()` does (adding `EmulatorResourceAnnotation`, `ContainerImageAnnotation`, endpoints, and the `AzureStorageEmulatorResource` surrogate) while skipping the `IsPublishMode` guard, or strip `PipelineStepAnnotation` from the resource after creation.
## Proposed Fix
Make the `IsPublishMode` guard compute-environment-aware. Conceptually:
```csharp
// Current — assumes publish always means "provision real Azure"
if (builder.ApplicationBuilder.ExecutionContext.IsPublishMode)
{
return builder; // skip emulator setup
}
// Proposed — only skip emulator when Azure infra can provision the real resource
if (builder.ApplicationBuilder.ExecutionContext.IsPublishMode
&& builder.ApplicationBuilder.HasAzureComputeEnvironment())
{
return builder;
}
```
This way `AddAzureStorage().RunAsEmulator()` just works for docker-compose — no user-facing API changes, no new methods to learn, no conditional logic in app hosts.
### Scope
This affects every Azure resource type that has `RunAsEmulator()` or `RunAsContainer()`:
- **Azure Storage** (Azurite)
- **Cosmos DB** (Cosmos emulator)
- **Service Bus**
- **Event Hubs**
- **Redis** (Garnet)
- **Azure PostgreSQL** (`RunAsContainer`)
- **Azure SQL** (`RunAsContainer`)
Each of these has the same `IsPublishMode` early-return guard, so the fix could be applied consistently across all of them.
## Reproduction
```csharp
var builder = DistributedApplication.CreateBuilder(args);
builder.AddDockerComposeEnvironment("docker");
var storage = builder.AddAzureStorage("storage")
.RunAsEmulator(); // <-- no-op in publish mode
var blobs = storage.AddBlobs("blobs");
// aspire publish generates a compose file where storage is missing
// and blobs connection string is an unresolved Azure placeholder
```
## Environment
- Aspire 13.2.1
- `Aspire.Hosting.Docker` + `Aspire.Hosting.Azure.Storage`
Contributor guide
Assessment
This issue has not been assessed yet.