This Aspire integration runs Floci in a container. Floci is a family of high-performance local cloud emulators — floci/floci (AWS, 65+ services including Lambda, S3, DynamoDB, SQS, SNS), floci/floci-az (Azure — Blob/Queue/Table Storage, Cosmos DB, Functions, Event Hubs, Service Bus), and floci/floci-gcp (GCP — Pub/Sub, Firestore, Datastore, Storage, Secret Manager, Cloud Functions) — each API-compatible with its respective cloud.
Every example below is shown in both C# and TypeScript (polyglot AppHost) form.
AWS
var builder = DistributedApplication.CreateBuilder(args);
var aws = builder.AddFlociAws("floci-aws");
var api = builder.AddProject<MyApi>("api")
.WithReference(aws)
.WaitFor(aws);
builder.Build().Run();const builder = await createBuilder();
const aws = await builder.addFlociAws('floci-aws');
const api = await builder.addProject('api', '../MyApi/MyApi.csproj')
.withFlociAwsReference(aws)
.waitFor(aws);
await builder.build().run();WithReference(aws) / withFlociAwsReference(aws) uses the standard Aspire connection string injection and additionally injects the environment variables the AWS SDKs already read, so no SDK configuration is needed in the dependent:
| Variable | Value |
|---|---|
ConnectionStrings__floci-aws |
http://localhost:{port} (standard Aspire connection string) |
AWS_ENDPOINT_URL |
The emulator endpoint, resolved by Aspire per dependent (see Endpoint resolution) |
AWS_DEFAULT_REGION |
Region passed to AddFlociAws/addFlociAws (default: us-east-1) |
AWS_ACCESS_KEY_ID |
test |
AWS_SECRET_ACCESS_KEY |
test |
Note: In C# each cloud contributes a
WithReferenceoverload, soWithReference(aws)is picked by argument type. The generated TypeScript bindings have no overload resolution, so each cloud gets its own method:withFlociAwsReference,withFlociAzureReference,withFlociGcpReference.
Azure
var azure = builder.AddFlociAzure("floci-az");
builder.AddProject<MyApi>("api")
.WithReference(azure)
.WaitFor(azure);const azure = await builder.addFlociAzure('floci-az');
await builder.addProject('api', '../MyApi/MyApi.csproj')
.withFlociAzureReference(azure)
.waitFor(azure);WithReference(azure) / withFlociAzureReference(azure) injects the following environment variables into the dependent resource:
| Variable | Value |
|---|---|
ConnectionStrings__floci-az |
http://localhost:{port} (standard Aspire connection string) |
AZURE_STORAGE_CONNECTION_STRING |
Development storage connection string pointed at the Floci Azure endpoint, carrying BlobEndpoint, QueueEndpoint and TableEndpoint and the well-known devstoreaccount1 dev credentials |
For Cosmos DB, use WithCosmos() / withCosmos() to model the Cosmos API as a child resource, then reference it through Aspire's standard connection-string flow:
var azure = builder.AddFlociAzure("floci-az");
var cosmos = azure.WithCosmos();
builder.AddProject<MyApi>("api")
.WithReference(azure) // storage variables (optional)
.WithReference(cosmos) // ConnectionStrings__cosmos
.WaitFor(azure);const azure = await builder.addFlociAzure('floci-az');
const cosmos = await azure.withCosmos();
await builder.addProject('api', '../MyApi/MyApi.csproj')
.withFlociAzureReference(azure)
.withReference(cosmos)
.waitFor(azure);App side, this is the standard Aspire flow:
builder.AddAzureCosmosClient("cosmos");| Variable | Value |
|---|---|
ConnectionStrings__{resourceName} (default cosmos) |
AccountEndpoint={scheme}://{host}:{port}/{account}-cosmos/;AccountKey=… — the well-known Cosmos DB emulator key. Resource name and account name (default devstoreaccount1) are configurable. |
The Cosmos child resource is additive, so combine WithReference(cosmos) with WithReference(azure) when you also want the base endpoint / storage variables. (Talking to the floci Cosmos emulator over HTTP from the .NET SDK still needs the usual client-side settings — Gateway mode, and HTTP/1.1 — which are the app's concern, as with any local Cosmos emulator.)
For Service Bus, use WithServiceBus() / withServiceBus() to model the AMQP data plane as a child resource, then reference it with WithReference() / withFlociAzureServiceBusReference():
var azure = builder.AddFlociAzure("floci-az")
.WithDockerSocket();
var serviceBus = azure.WithServiceBus();
builder.AddProject<MyApi>("api")
.WithReference(serviceBus) // ConnectionStrings__servicebus
.WaitFor(azure);const azure = (await builder.addFlociAzure('floci-az')).withDockerSocket();
const serviceBus = await azure.withServiceBus();
await builder.addProject('api', '../MyApi/MyApi.csproj')
.withFlociAzureServiceBusReference(serviceBus)
.waitFor(azure);App side, this is the standard Aspire flow:
builder.AddAzureServiceBusClient("servicebus");| Variable | Value |
|---|---|
ConnectionStrings__{resourceName} (default servicebus) |
Endpoint=sb://{host}:{amqpPort};SharedAccessKeyName=RootManageSharedAccessKey;SharedAccessKey=SAS_KEY_VALUE;UseDevelopmentEmulator=true; — {host} is localhost for host processes and the container runtime's host gateway for containers |
WithServiceBus sets FLOCI_AZ_SERVICES_SERVICE_BUS_MOCKED=false and FLOCI_AZ_SERVICES_SERVICE_BUS_START_ON_BOOT=true. Aspire models the sidecar's AMQP and AMQPS host ports as proxyless endpoints and allocates them by default; pass amqpPort / amqpTlsPort to use fixed ports. The management plane (for example, ServiceBusAdministrationClient) remains on the base endpoint from WithReference(azure). Requires WithDockerSocket() and floci-az 0.12.0 or later.
AMQP and AMQPS must use different host ports. Repeated calls on the same emulator return the existing child only when the resource name and any specified ports match.
For container consumers, the Service Bus reference helper adds floci-servicebus-host.internal:host-gateway to the container's host mappings and uses the sidecar's published AMQP port. This requires a container runtime supporting Docker's --add-host=...:host-gateway option. Use the Service Bus reference helper instead of passing the child's raw endpoint or connection string expression to a container.
Service Bus support is run-only. WithServiceBus() throws in publish mode because the Artemis sidecar has no deployable backing resource in the Aspire model. In an AppHost that also publishes, call it inside if (builder.ExecutionContext.IsRunMode) and reference a deployable Service Bus resource in the publish branch.
When running multiple Floci Azure emulators on the same Docker host, set a different FLOCI_AZ_DOCKER_RESOURCE_NAMESPACE environment variable on each emulator to keep their sidecar container names separate. Also pass a globally unique child name to each additional WithServiceBus call, for example secondAzure.WithServiceBus("second-servicebus"). The first can keep the default servicebus connection name; references to the second inject ConnectionStrings__second-servicebus. Port allocation alone does not isolate resource or sidecar names.
GCP
var gcp = builder.AddFlociGcp("floci-gcp", defaultProjectId: "my-project");
builder.AddProject<MyApi>("api")
.WithReference(gcp)
.WaitFor(gcp);const gcp = await builder.addFlociGcp('floci-gcp', {
defaultProjectId: 'my-project',
});
await builder.addProject('api', '../MyApi/MyApi.csproj')
.withFlociGcpReference(gcp)
.waitFor(gcp);WithReference(gcp) / withFlociGcpReference(gcp) injects the following environment variables into the dependent resource:
| Variable | Value |
|---|---|
ConnectionStrings__floci-gcp |
http://localhost:{port} (standard Aspire connection string) |
PUBSUB_EMULATOR_HOST |
{host}:{port} (see Endpoint resolution) |
FIRESTORE_EMULATOR_HOST |
{host}:{port} |
DATASTORE_EMULATOR_HOST |
{host}:{port} |
STORAGE_EMULATOR_HOST |
http://{host}:{port} — the Storage SDK expects a full URL here, unlike the others |
SECRET_MANAGER_EMULATOR_HOST |
{host}:{port} |
GOOGLE_CLOUD_PROJECT |
Project ID passed to AddFlociGcp/addFlociGcp (default: floci-local) |
CLOUDSDK_CORE_PROJECT |
Same project ID, for tools that read the gcloud CLI's config var instead |
Each emulator needs access to the Docker socket to launch sibling containers for its container-backed services (AWS Lambda, Azure Functions, GCP Cloud Run/Cloud SQL):
var aws = builder.AddFlociAws("floci-aws")
.WithDockerSocket();
var azure = builder.AddFlociAzure("floci-az")
.WithDockerSocket();
var gcp = builder.AddFlociGcp("floci-gcp")
.WithDockerSocket();const aws = await builder.addFlociAws('floci-aws');
await aws.withDockerSocket();
const azure = await builder.addFlociAzure('floci-az');
await azure.withDockerSocket();
const gcp = await builder.addFlociGcp('floci-gcp');
await gcp.withDockerSocket();On non-standard Docker installations (e.g. Podman, Rancher Desktop), pass the socket path explicitly — this works the same way on all three clouds:
var aws = builder.AddFlociAws("floci-aws")
.WithDockerSocket("/run/user/1000/podman/podman.sock");const aws = await builder.addFlociAws('floci-aws');
await aws.withDockerSocket({ socketPath: '/run/user/1000/podman/podman.sock' });By default each emulator stores all state in memory. Use WithDataVolume/withDataVolume to persist state across restarts — available on all three clouds:
var aws = builder.AddFlociAws("floci-aws")
.WithDataVolume("floci-data");
var azure = builder.AddFlociAzure("floci-az")
.WithDataVolume("floci-az-data");
var gcp = builder.AddFlociGcp("floci-gcp")
.WithDataVolume("floci-gcp-data");const aws = await builder.addFlociAws('floci-aws');
await aws.withDataVolume('floci-data');
const azure = await builder.addFlociAzure('floci-az');
await azure.withDataVolume('floci-az-data');
const gcp = await builder.addFlociGcp('floci-gcp');
await gcp.withDataVolume('floci-gcp-data');Or use a host bind mount:
var aws = builder.AddFlociAws("floci-aws")
.WithDataBindMount("/path/to/data");const aws = await builder.addFlociAws('floci-aws');
await aws.withDataBindMount('/path/to/data');var aws = builder.AddFlociAws("floci-aws",
defaultRegion: "eu-west-1",
defaultAccountId: "123456789012");
var gcp = builder.AddFlociGcp("floci-gcp",
defaultProjectId: "my-project");const aws = await builder.addFlociAws('floci-aws', {
defaultRegion: 'eu-west-1',
defaultAccountId: '123456789012',
});
const gcp = await builder.addFlociGcp('floci-gcp', {
defaultProjectId: 'my-project',
});Run the Floci UI web console alongside an emulator to browse its hosted resources:
var floci = builder.AddFlociAws("floci")
.WithFlociUI();const floci = await builder.addFlociAws('floci');
await floci.withFlociUI();Customize the container name or pin the host port:
var floci = builder.AddFlociAws("floci")
.WithFlociUI(ui => ui.WithHostPort(14500), containerName: "my-floci-ui");const floci = await builder.addFlociAws('floci');
await floci.withFlociUI({
containerName: 'my-floci-ui',
configureContainer: async (ui) => {
await ui.withHostPort({ port: 14500 });
},
});Note: Floci also has a built-in mechanism to launch the UI as a sidecar container on demand, but that relies on Floci itself talking to the Docker socket and self-discovered endpoints, which does not play well with Aspire's DCP-managed container networking.
WithFlociUI/withFlociUIruns the UI as a first-class Aspire resource instead.
A single UI console can attach to any combination of clouds — call WithFlociUI/withFlociUI on whichever cloud creates the console, then attach the others with WithReference/withCloudReference*:
var aws = builder.AddFlociAws("floci-aws");
var azure = builder.AddFlociAzure("floci-az");
var gcp = builder.AddFlociGcp("floci-gcp");
aws.WithFlociUI(configureContainer: ui =>
{
ui.WithReference(azure);
ui.WithReference(gcp);
});const aws = await builder.addFlociAws('floci-aws');
const azure = await builder.addFlociAzure('floci-az');
const gcp = await builder.addFlociGcp('floci-gcp');
await aws.withFlociUI({
configureContainer: async (ui) => {
await ui.withFlociAzureReference(azure);
await ui.withFlociGcpReference(gcp);
},
});The UI container (floci/floci-ui) is added as a child resource of whichever cloud resource created it, wired to each attached cloud's endpoint over the container network (FLOCI_ENDPOINT/FLOCI_AZURE_ENDPOINT/FLOCI_GCP_ENDPOINT), and is excluded from the deployment manifest (it is a local development tool only).
Note: In C# these are
WithReferenceoverloads on the UI resource builder — the compiler picks the right one from the argument type. In TypeScript there is no overload resolution on the generated bindings, so each cloud gets its own method:withFlociAwsReference,withFlociAzureReference,withFlociGcpReference.
Mount a hand-crafted application.yml to tune any Floci setting that does not have an extension method. The file is injected read-only at /deployments/config/application.yml — the standard Quarkus Docker config override location.
var floci = builder.AddFlociAws("floci")
.WithConfigFile("./floci.yml");const floci = await builder.addFlociAws('floci');
await floci.withConfigFile('./floci.yml');A minimal floci.yml that enables debug logging and disables signature validation:
floci:
auth:
validate-signatures: false
quarkus:
log:
level: DEBUGAll Floci settings can also be set via FLOCI_-prefixed environment variables — WithConfigFile/withConfigFile is only needed for settings that do not have a dedicated extension method. This is currently only available for the AWS emulator.
Both images serve HTTP and HTTPS on the same port, so enabling TLS never changes the port — only the scheme handed to dependents.
The integration hooks Aspire's own certificate plumbing, so the idiomatic Aspire APIs just work: configure a certificate and the emulator picks it up. Nothing Floci-specific to call.
var aws = builder.AddFlociAws("floci-aws")
.WithHttpsDeveloperCertificate(); // Aspire API — provisions and mounts the key pair
var azure = builder.AddFlociAzure("floci-az")
.WithHttpsDeveloperCertificate();
builder.AddProject<MyApi>("api")
.WithReference(aws) // AWS_ENDPOINT_URL = https://...
.WithReference(azure); // AZURE_STORAGE_CONNECTION_STRING = DefaultEndpointsProtocol=https;...const aws = await builder.addFlociAws('floci-aws');
await aws.withHttpsDeveloperCertificate();Under the hood the integration registers a WithHttpsCertificateConfiguration callback that maps Aspire's provisioned paths onto the image's own settings, and switches the primary endpoint to https before start:
| Aspire-provided value | AWS | Azure |
|---|---|---|
| — | FLOCI_TLS_ENABLED=true |
FLOCI_AZ_TLS_ENABLED=true |
| — | FLOCI_TLS_SELF_SIGNED=false |
FLOCI_AZ_TLS_SELF_SIGNED=false |
context.CertificatePath |
FLOCI_TLS_CERT_PATH |
FLOCI_AZ_TLS_CERT_PATH |
context.KeyPath |
FLOCI_TLS_KEY_PATH |
FLOCI_AZ_TLS_KEY_PATH |
Because the ASP.NET Core development certificate is already in your machine's trust store, host-process dependents validate it with no extra client configuration. For container dependents, add Aspire's WithDeveloperCertificateTrust(true) to install the trust bundle. Any other Aspire certificate source (WithHttpsCertificate(...), WithCertificatesFromFile, WithCertificatesFromStore) is honoured the same way.
Reach for this when a client refuses plain HTTP — the Cosmos DB Java SDK, or the azurerm Terraform/OpenTofu provider, which discovers the cloud over https://<host>/metadata/endpoints.
Plain HTTP stays the default. Unlike HTTPS-first resources, merely having a trusted development certificate on the machine does not flip an existing AppHost to
https— a certificate has to be asked for explicitly.The Floci UI console keeps using the emulator's plain-HTTP listener even when TLS is on. It reaches the emulator by container-network name, which neither the development certificate (SAN
localhostonly) nor a host-issued certificate covers, so HTTPS there would fail hostname validation regardless of trust. Since both protocols share the port, the console connects normally.The GCP emulator (
floci/floci-gcp) has no HTTPS listener, so no certificate callback is registered for it and a configured certificate has no effect there.
Available on all three cloud resource types:
var endpoint = floci.PrimaryEndpoint;
var host = floci.Host;
var port = floci.Port;
var connectionString = floci.ConnectionStringExpression;const endpoint = await floci.primaryEndpoint();
const host = await floci.host();
const port = await floci.port();
const connectionString = await floci.connectionStringExpression();connectionStringExpression is an unresolved endpoint expression — see below.
Every environment variable this integration injects carries an Aspire endpoint expression rather than a literal address, so Aspire resolves it against the network the dependent is on:
| Dependent | Resolves to |
|---|---|
| Project / executable (host process) | localhost:{hostPort} |
| Sibling container | {flociResourceName}:{targetPort} on the container network |
Nothing is hard-coded to host.docker.internal, so this works on Docker Desktop, plain Linux Docker, Podman and Rancher Desktop alike.
The scheme is http unless a certificate has been configured, in which case it becomes https on the same port.