Chapter 2: Managing infrastructure components with containers and integrations
Introduces two ways to connect infrastructure components to Aspire applications and explains why service discovery matters when those components and services connect.
After this chapter you will be able to
- Infrastructure connection options. Describe why application services need infrastructure components and name the two ways this chapter connects them in Aspire: Docker containers and Aspire integration libraries. Explain that security infrastructure is the example, but the principles apply to other components.
- Container packaging and portability. Explain how a container packages an application with its dependencies so it can run consistently across development, test, and production environments.
- Isolation and shared kernel. Distinguish a container’s isolation from a virtual machine’s full-OS approach, and explain why sharing the host OS kernel makes containers lightweight.
- Images and container instances. Describe a Docker image as a template containing what an application needs to run, and state that creating a container instantiates that image.
- Keycloak identity provider role. Explain that Keycloak provides authentication and authorization for applications and services, reducing the need to write custom security code.
- Realms and JSON portability. Explain what Keycloak calls its domains and how exporting and importing a realm's JSON file helps populate a new instance.
- Starter project baseline. Describe the application used as the starting point and why it is based on the Aspire Starter Project.
- Running Docker prerequisite. Explain what must be checked before launching the Aspire host and what happens if that requirement is not met.
- Custom Keycloak resource registration. Explain why the host registers a custom KeycloakResource with AddResource() rather than using AddContainer(), and describe how the container is configured.
- Realm import bind mount. Describe how the local realm JSON file becomes available to Keycloak and when Keycloak imports it.
- Client service references. Explain how the frontend and API receive a reference to Keycloak and why the frontend also receives the client secret.
- Local endpoint dependency. Explain why passing frontend endpoint URLs into Keycloak during host launch is useful locally but creates a deployment-time dependency problem, and state the section's proposed alternative.
- Frontend OIDC client configuration. Trace how the frontend registers and retrieves its HttpClient and uses it to configure OIDC authentication options.
- Backchannel service discovery rationale. Distinguish backchannel communication from browser-based front-channel communication and explain why the backchannel is used for Aspire service discovery here.
- API bearer authentication setup. Describe how the API service is prepared for Keycloak authentication: add an authentication extension, use bearer tokens rather than the frontend’s OIDC settings, and register and invoke the configuration in Program.cs.
- Alternative service registrations. Describe how an Aspire host can reference an externally hosted Docker container or register a service by referring directly to an executable, and note that both options have relatively rare use cases.
- Aspire integrations versus containers. Explain that Aspire integrations are libraries for adding resources such as databases, message brokers, and distributed caches, and that their extension methods make these resources easier to host than working with Docker containers directly.
- Redis output caching benefits. Describe what output caching saves and why storing that cache in separately hosted Redis helps when an app server restarts or several app instances run.
- Aspire integration package roles. Distinguish the hosting package, which lets Aspire host a service instance, from the client package, which lets other Aspire-hosted apps access that service.
- Redis host registration and reference. Describe adding Aspire.Hosting.Redis to the host project, registering Redis as a service in AppHost.cs, and passing its reference to webfrontend through WithReference().
- Hosted Redis output caching. Explain that the hosted web app references the Redis output-caching NuGet package, uses the component’s host-registered name when configuring it, and then caches output in Redis rather than local memory.
- Cache integration dashboard display. Explain that, after launching the playground and waiting for the applications to build, the cache service appears in the Aspire dashboard as a Container because this integration runs locally as a Docker container. Describe how the Source tab provides another clue: its path starts with docker.io.
- Local SMTP email testing. Explain why the section proposes a local emulated SMTP server and identifies MailDev as the tool to wrap.
- MailDev resource definition. Explain why the MailDev resource extends ContainerResource and implements IResourceWithConnectionString, and identify the connection and endpoint information it configures.
- Host registration extension. Explain how the MailDev resource builder extension makes the component available to an Aspire host.
- Custom library project reference. Explain why OnlineShop.AppHost references the MailDev class library with IsAspireProjectResource set to false.
- MailDev launch and verification. Describe the remaining AppHost changes and what confirms that MailDev launched successfully.
What it builds on
- Service discovery in Aspire (chapter 1) · Introduces service discovery in Aspire.
- Getting started with the Aspire Starter Project (chapter 1) · The Aspire Starter Project introduced in chapter 1.
- Orchestrating with Aspire (chapter 1) · Assumes familiarity with registering applications in Aspire orchestration.
- Using service discovery (chapter 1) · Using Aspire service discovery through an HttpClient.
Section by section
Introduction
Introduces two ways to connect infrastructure components to Aspire applications and explains why service discovery matters when those components and services connect.
- Infrastructure connection options: Describe why application services need infrastructure components and name the two ways this chapter connects them in Aspire: Docker containers and Aspire integration libraries. Explain that security infrastructure is the example, but the principles apply to other components.
Why it matters: It frames the two infrastructure approaches used throughout the chapter without treating the security example as a special case. - Service discovery benefit: Explain why the chapter uses service discovery alongside infrastructure components: it avoids repeating configuration and references and helps Aspire-hosted services connect to one another.
Why it matters: It connects infrastructure setup to the practical problem of keeping service connections manageable.
Key terms: infrastructure components, Docker containers, Aspire integration libraries, service discovery, configuration, references
2.1 Docker containers
This section explains what Docker containers and images are, why containers are portable and lightweight, and how images can be obtained and reused.
- Container packaging and portability: Explain how a container packages an application with its dependencies so it can run consistently across development, test, and production environments.
Why it matters: This is why a containerized component can be moved between development and production with fewer compatibility concerns. - Isolation and shared kernel: Distinguish a container’s isolation from a virtual machine’s full-OS approach, and explain why sharing the host OS kernel makes containers lightweight.
Why it matters: Isolation lets components coexist, while the shared kernel explains why containers are cheaper to start and run than traditional virtual machines. - Images and container instances: Describe a Docker image as a template containing what an application needs to run, and state that creating a container instantiates that image.
Why it matters: The image–container distinction helps clarify what is selected or built versus what actually runs. - Docker Hub image reuse: Describe how developers can use images from Docker Hub directly to launch containers or as bases for images with added functionality.
Why it matters: Existing images provide a starting point for running or customizing infrastructure software.
Key terms: Docker container, dependencies, portability, isolation, host OS kernel, virtual machines, Docker image, instantiate, Docker Hub, base images
2.2 Keycloak overview
Introduces Keycloak as an identity provider and explains its security features and portable realm configuration.
- Keycloak identity provider role: Explain that Keycloak provides authentication and authorization for applications and services, reducing the need to write custom security code.
Why it matters: This explains why an application might use Keycloak rather than build its own authentication system. - Keycloak features and extensibility: Describe the kinds of identity features Keycloak offers and how plugins can add features it does not support natively.
Why it matters: Its built-in and extensible features help explain why organizations use Keycloak for identity management. - Realms and JSON portability: Explain what Keycloak calls its domains and how exporting and importing a realm's JSON file helps populate a new instance.
Why it matters: Realm export and import makes it easier to set up an instance with existing identity configuration.
Key terms: Keycloak, identity provider (IdP), authentication, authorization, single sign-on (SSO), multifactor authentication, plugins, customizable login pages, realms, JSON file
2.3 Initial setup
The section identifies the starter application used for the Keycloak example and the Docker prerequisite for running it.
- Starter project baseline: Describe the application used as the starting point and why it is based on the Aspire Starter Project.
Why it matters: Knowing the baseline helps you recognize what the Keycloak addition changes. - Running Docker prerequisite: Explain what must be checked before launching the Aspire host and what happens if that requirement is not met.
Why it matters: A running Docker environment is necessary for the Keycloak container in this example.
Key terms: Aspire Starter Project, Aspire host application, webfrontend, apiservice, Docker environment, Aspire host, Keycloak container
2.4 Adding a Keycloak container to an Aspire host
This section sets up Keycloak as an Aspire-hosted container, imports its realm configuration, connects it to the frontend and API, and explains why endpoint configuration differs between local development and deployment.
- Custom Keycloak resource registration: Explain why the host registers a custom KeycloakResource with AddResource() rather than using AddContainer(), and describe how the container is configured.
Why it matters: It explains why this container needs a custom Aspire resource instead of a simpler container registration. - Realm import bind mount: Describe how the local realm JSON file becomes available to Keycloak and when Keycloak imports it.
Why it matters: Without the mounted file, the locally launched container cannot import the intended realm configuration. - Client service references: Explain how the frontend and API receive a reference to Keycloak and why the frontend also receives the client secret.
Why it matters: The applications need the Keycloak reference to find their identity provider, while the UI needs its client secret for the described OIDC setup. - Local endpoint dependency: Explain why passing frontend endpoint URLs into Keycloak during host launch is useful locally but creates a deployment-time dependency problem, and state the section's proposed alternative.
Why it matters: It prevents a convenient local setup from being mistaken for a workable production startup sequence.
Key terms: KeycloakResource, AddResource(), AddContainer(), WithAnnotation(), client secret, bind mount, ImportRealms(), realm, import.json, /opt/keycloak/data/
2.4.1 Configuring the web frontend app
The section configures the web frontend to use Keycloak for OIDC authentication through a realm-specific authority URL and a service-discovered backchannel client.
- Realm-specific Keycloak URL: Explain why the Keycloak authority URL includes the realm path and how this section constructs that path.
Why it matters: The frontend needs the right realm URL to reach the intended OIDC endpoints. - Frontend OIDC client configuration: Trace how the frontend registers and retrieves its HttpClient and uses it to configure OIDC authentication options.
Why it matters: This wiring lets the frontend's OIDC configuration use its registered IdP client rather than a separately specified endpoint address. - Backchannel service discovery rationale: Distinguish backchannel communication from browser-based front-channel communication and explain why the backchannel is used for Aspire service discovery here.
Why it matters: The distinction explains both how the frontend finds Keycloak and why its internal authority address can differ from the public one.
Key terms: Keycloak realm, realms/OnlineShop/, well-known endpoints, HttpClient, dependency injection, IHttpClientFactory, Backchannel, Authority, Microsoft.AspNetCore.Authentication.OpenIdConnect, front-channel
2.4.2 Configuring API service
This subsection configures the API service to use Keycloak authentication, then checks the Keycloak container and frontend sign-in flow through the Aspire dashboard.
- API bearer authentication setup: Describe how the API service is prepared for Keycloak authentication: add an authentication extension, use bearer tokens rather than the frontend’s OIDC settings, and register and invoke the configuration in Program.cs.
Why it matters: The API needs authentication settings appropriate to bearer tokens, not simply a copy of the frontend’s settings. - Keycloak container readiness: Explain what the Aspire dashboard shows for the Keycloak resource and why its console may not work immediately on first launch.
Why it matters: A temporary console error on first launch does not necessarily mean the Keycloak integration has failed. - Keycloak sign-in verification: Trace how the configured Keycloak realm and test user are found, and what happens when the web frontend endpoint is opened.
Why it matters: Following the redirects provides a visible check that the frontend can sign in through the configured Keycloak realm.
Key terms: OnlineShop.ApiService, AuthExtensions.cs, Bearer token authentication, OIDC, HttpClient, backchannel, idp resource, Container, quay.io/keycloak/keycloak, Aspire dashboard
2.4.3 Other service types Aspire can host
This subsection identifies two less common ways to register services in an Aspire host besides hosting internal Docker containers.
- Alternative service registrations: Describe how an Aspire host can reference an externally hosted Docker container or register a service by referring directly to an executable, and note that both options have relatively rare use cases.
Why it matters: These options help you recognize that Aspire can register services beyond containers it hosts itself.
Key terms: external container reference, executable, Aspire host
2.5 How Aspire integration libraries differ from containers
The section distinguishes Aspire integrations from direct container orchestration and uses Redis output caching to illustrate why a separately hosted integration can help a distributed app.
- Aspire integrations versus containers: Explain that Aspire integrations are libraries for adding resources such as databases, message brokers, and distributed caches, and that their extension methods make these resources easier to host than working with Docker containers directly.
Why it matters: This distinction explains why you would use an Aspire integration instead of configuring every container directly. - Container limits for databases: Explain why an Aspire database integration can connect to a managed instance instead of a container, and why the section cautions against hosting a data-storing SQL Server instance in a container outside development or demonstrations.
Why it matters: It prevents you from assuming that using an integration means putting persistent database data in a container. - Redis output caching benefits: Describe what output caching saves and why storing that cache in separately hosted Redis helps when an app server restarts or several app instances run.
Why it matters: Redis shows the practical benefit of sharing a cache across the services and instances that use it.
Key terms: Aspire integrations, Aspire components, extension methods, Docker containers, managed SQL Server instance, stateless, Redis, output caching, distributed cache, application’s memory
2.6 Configuring Aspire integrations on the host
This section explains the two-package structure of a typical Aspire integration and shows how to host Redis and reference it from an app.
- Aspire integration package roles: Distinguish the hosting package, which lets Aspire host a service instance, from the client package, which lets other Aspire-hosted apps access that service.
Why it matters: The distinction clarifies which package serves the host and which serves an app using the service. - Redis host registration and reference: Describe adding Aspire.Hosting.Redis to the host project, registering Redis as a service in AppHost.cs, and passing its reference to webfrontend through WithReference().
Why it matters: Registering Redis and referencing it from webfrontend sets up that app to consume the integration.
Key terms: Aspire integration, NuGet packages, client package, Aspire.Hosting.Redis, OnlineShop.AppHost, AppHost.cs, webfrontend, WithReference()
2.6.1 Connecting to an integration from a hosted service
This subsection shows how a hosted web app connects to a Redis integration and uses it for output caching.
- Hosted Redis output caching: Explain that the hosted web app references the Redis output-caching NuGet package, uses the component’s host-registered name when configuring it, and then caches output in Redis rather than local memory.
Why it matters: It connects the host’s Redis registration to what the web app must configure and where its cached output goes.
Key terms: Aspire.StackExchange.Redis.OutputCaching, OnlineShop.Web.csproj, service discovery, Redis output caching, local in-memory cache
2.6.2 Viewing integrations in the dashboard
Shows how a locally hosted cache integration appears in the Aspire dashboard and how to identify its container source.
- Cache integration dashboard display: Explain that, after launching the playground and waiting for the applications to build, the cache service appears in the Aspire dashboard as a Container because this integration runs locally as a Docker container. Describe how the Source tab provides another clue: its path starts with docker.io.
Why it matters: The dashboard helps you recognize what Aspire is running locally for an integration.
Key terms: cache service, Container, Aspire integration, Source tab, docker.io
2.7 Building an Aspire integration library
This section explains why a local SMTP test server is useful for Keycloak registration emails and starts a class library for a custom MailDev Aspire component.
- Registration email purposes: Explain the two purposes of a registration email in an app using authentication.
Why it matters: These two purposes explain why the Keycloak setup needs to send email during development. - Local SMTP email testing: Explain why the section proposes a local emulated SMTP server and identifies MailDev as the tool to wrap.
Why it matters: Local email testing makes the registration workflow testable without sending messages to real recipients. - MailDev hosting project setup: Describe the initial project setup for the planned custom Aspire component.
Why it matters: The class library gives the custom MailDev component a project in the existing solution.
Key terms: registration, confirmation link, Keycloak, SMTP server, MailDev Docker image, local environment, OnlineShop solution, OnlineShop.MailDev.Hosting, class library, custom Aspire component
2.7.1 Building an integration component
This subsection builds a MailDev Aspire integration component by defining its container resource, pinning its image version, and exposing an extension method for hosting it.
- MailDev resource definition: Explain why the MailDev resource extends ContainerResource and implements IResourceWithConnectionString, and identify the connection and endpoint information it configures.
Why it matters: This definition gives the hosted MailDev container a way for client apps to find and use its SMTP service. - Pinned container image identity: Describe what the MailDevContainerImageTags constants identify and justify specifying an exact image version instead of omitting the tag or using latest.
Why it matters: An exact image version helps prevent the integration from changing behavior unexpectedly. - Host registration extension: Explain how the MailDev resource builder extension makes the component available to an Aspire host.
Why it matters: The host can add MailDev with a single method call rather than handling its container launch directly.
Key terms: MailDevResource, ContainerResource, IResourceWithConnectionString, connection string, SMTP endpoint, MailDevContainerImageTags, registry, Docker image, version tag, MailDevResourceBuilderExtensions
2.7.2 Launching a custom integration from the Aspire host
This section shows how to reference a custom MailDev integration from the Aspire host, launch it, and confirm it is running.
- Custom library project reference: Explain why OnlineShop.AppHost references the MailDev class library with IsAspireProjectResource set to false.
Why it matters: The host needs the integration’s library without mistakenly treating that library as another service to run. - MailDev launch and verification: Describe the remaining AppHost changes and what confirms that MailDev launched successfully.
Why it matters: The dashboard and inbox give visible confirmation that the custom integration is available through the host.
Key terms: OnlineShop.AppHost, IsAspireProjectResource, class library, Aspire service, AppHost.cs, builder, MailDev container, Aspire dashboard, local email inbox
2.8 Summary
Where this is used later
- Hosting a SQL Server component (chapter 3) · The use of Docker containers for infrastructure components.
- A brief introduction to single sign-on (chapter 8) · Provides context for Keycloak's role in single sign-on.
- Predeployment modifications of Aspire-hosted systems (chapter 9) · Revisits changes needed when moving an Aspire-hosted system toward deployment.
- Enforcing UI authentication (chapter 8) · Frontend authentication builds on having OIDC configured in the web application.
- Using service discovery in SSO (chapter 8) · Service discovery in SSO builds on using a discoverable IdP address.
- Enforcing API authentication (chapter 8) · API authentication builds on the bearer-token configuration introduced here.
- (chapter intro) (chapter 3) · Introduces Aspire integration libraries.
- SQL database components basics (chapter 3) · The earlier distinction between Aspire integration libraries and containers.
Concepts explained in this chapter
- Aspire integration libraryA reusable library that connects an application framework to an external service or infrastructure component.
- AddResourceAddResource is an Aspire builder method that registers a resource with the application orchestrator.
- Keycloak containerA Keycloak container is a containerized Keycloak instance that runs the identity and access management service.
- bind mountA bind mount maps a location in the host filesystem to a location in a container filesystem.
- containerA container is an isolated runtime environment that packages an application with its dependencies while sharing the host operating system kernel.
- DockerDocker is a platform for building, packaging, and running applications in containers.
- infrastructure resourceAn infrastructure resource is a supporting component, such as a database, cache, or message broker, on which an application depends.
- withreferenceWithReference() is an Aspire API method that adds a reference from one application resource to another.
- connection stringA structured configuration value specifying how an application connects to a database.
These guides are generated from the text of Manning books by Manning's knowledge graph and link to the sections they draw on. The full books are available on liveBook and at manning.com.
