Appearance
Are you an LLM? You can read better optimized documentation at /deployment/overview.md for this page in Markdown format
Deployment overview
Note: This application is no longer officially maintained as of January 2023. Feel free to use it, fork it, and patch it for your own needs.
This document provides a comprehensive overview of the deployment strategies, supported platforms, and critical configuration considerations for the TheExampleApp application. It is intended for developers responsible for deploying and maintaining the application in various environments.
Deployment options
TheExampleApp is a standard ASP.NET Core 2.1 application, which allows for a high degree of flexibility in deployment. The architecture is stateless, sourcing its content from the Contentful headless CMS, which simplifies scaling and deployment. The primary deployment methods are Platform-as-a-Service (PaaS) providers, containerization via Docker, or self-hosting on dedicated infrastructure.
Supported platforms
The application is pre-configured for several popular deployment targets.
Heroku
Heroku is a fully supported platform, offering a streamlined deployment process. The repository includes an app.json file, enabling one-click deployments. A hosted demo version is available at https://the-example-app-csharp.herokuapp.com/.
json
{
"name": "The example app .NET",
"description": "This is \"The Example App\", a reference for building your own applications using Contentful.",
"repository": "https://github.com/contentful/the-example-app.csharp",
"keywords": [ "aspnet", "core", "contentful", "example" ],
"stack": "heroku-16",
"website": "https://www.contentful.com/",
"logo": "https://the-example-app-nodejs.herokuapp.com/images/logo-node.svg",
"success_url": "/",
"buildpacks": [
{
"url": "https://github.com/jincod/dotnetcore-buildpack"
}
]
}1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
2
3
4
5
6
7
8
9
10
11
12
13
14
15
Key points for Heroku deployment:
- Buildpack: The deployment uses a custom .NET Core buildpack (
jincod/dotnetcore-buildpack) to compile and run the application on the Heroku platform. - Configuration: All configuration, especially Contentful credentials, must be set as Heroku "Config Vars". These will be injected as environment variables at runtime.
- Environment: The provided
DockerfilesetsASPNETCORE_ENVIRONMENT=Herokuby default, though this is not directly used by the buildpack deployment. It's a convention that could be used for environment-specific logic if needed.
For a step-by-step guide, see the Heroku Deployment Documentation.
Azure
Azure is a first-class platform for hosting ASP.NET Core applications. The README.md includes a "Deploy to Azure" button, indicating pre-existing support for Azure App Service.
Deployment to Azure can be achieved through multiple mechanisms:
- Azure App Service: A fully managed PaaS offering that can be deployed to directly from Visual Studio, VS Code, or via a Git-based workflow.
- CI/CD Pipelines: Using Azure DevOps or GitHub Actions to automate the build and deployment process.
- Container Apps/AKS: Deploying the application as a Docker container to Azure Container Apps or Azure Kubernetes Service for more complex, scalable scenarios.
For detailed instructions, refer to the Azure Deployment Documentation.
Docker containers
The project includes a Dockerfile that defines a multi-stage build process to create an optimized, production-ready container image. This is the most portable deployment method, allowing the application to run on any platform that supports Docker.
dockerfile
# Stage 1: Build the application using the full .NET SDK
FROM microsoft/dotnet:2.1-sdk AS builder
WORKDIR /src
COPY . .
RUN dotnet restore
RUN dotnet publish -c Release -o /app/
# Stage 2: Create the final, smaller runtime image
FROM microsoft/aspnetcore:2.1
WORKDIR /app
ENV ASPNETCORE_ENVIRONMENT=Heroku
COPY --from=builder /app .
ENTRYPOINT ["dotnet", "TheExampleApp.dll"]1
2
3
4
5
6
7
8
9
10
11
12
13
14
2
3
4
5
6
7
8
9
10
11
12
13
14
Dockerfile Analysis:
- Build Stage: The
builderstage uses thedotnet:2.1-sdkimage, which contains all the tools necessary to restore dependencies and publish the application inReleasemode. - Runtime Stage: The final stage uses the much smaller
aspnetcore:2.1base image, which only contains the runtime necessary to execute the compiled application. This reduces the final image size significantly. - Entrypoint: The container is configured to launch the application by executing
dotnet TheExampleApp.dll.
For a complete guide on building and running the container, see the Docker Deployment Documentation.
Self-hosted options
As a standard ASP.NET Core application, TheExampleApp can be self-hosted on both Windows and Linux servers that have the .NET Core 2.1 Runtime installed. This approach offers maximum control over the hosting environment.
A typical self-hosted setup involves:
- Publishing the application (
dotnet publish -c Release). - Copying the published artifacts to the server.
- Running the application behind a reverse proxy like Nginx (on Linux) or IIS (on Windows). The reverse proxy handles incoming HTTP/S requests and forwards them to the Kestrel web server running the application.
Deployment considerations
Proper configuration is crucial for a successful deployment. Pay close attention to the following areas.
Local development
For local development, the application runs on http://localhost:3000 by default. To enable editorial features (such as "Edit" buttons and draft/pending status indicators), append the ?editorial_features=enabled query parameter to the URL when accessing the application.
Environment configuration
The application follows the standard ASP.NET Core configuration model, layering settings from appsettings.json and overriding them with environment variables. Production secrets must never be committed to appsettings.json.
Contentful credentials
The core configuration is for the Contentful client, defined in appsettings.json.
json
{
// ... other settings
"ContentfulOptions": {
"DeliveryApiKey": "df2a18b8a5b4426741408fc95fa4331c7388d502318c44a5b22b167c3c1b1d03",
"PreviewApiKey": "10145c6d864960fdca694014ae5e7bdaa7de514a1b5d7fd8bd24027f90c49bbc",
"SpaceId": "qz0n5cdakyl9",
"UsePreviewApi": false
}
}1
2
3
4
5
6
7
8
9
2
3
4
5
6
7
8
9
In any production or staging environment, these values must be overridden using environment variables. The mapping follows the ASP.NET Core convention (using a double underscore __ to denote hierarchy).
appsettings.json Key | Environment Variable Name |
|---|---|
ContentfulOptions:DeliveryApiKey | ContentfulOptions__DeliveryApiKey |
ContentfulOptions:PreviewApiKey | ContentfulOptions__PreviewApiKey |
ContentfulOptions:SpaceId | ContentfulOptions__SpaceId |
ContentfulOptions:UsePreviewApi | ContentfulOptions__UsePreviewApi |
For a complete list of configurable variables, see the Environment Variables Documentation.
Application environment
The ASPNETCORE_ENVIRONMENT variable (e.g., Production, Staging, Development) controls application behavior. The application recognizes three standard environments:
- Development: Enables detailed error pages and browser link functionality.
- Staging: Supports redirecting Contentful API calls to a custom staging host (see below).
- Production (or any non-Development environment): Enables HTTPS redirection and a generic error handler.
Staging environment support
When ASPNETCORE_ENVIRONMENT is set to Staging, the application can redirect all Contentful API requests to a custom staging server. This is controlled by the StagingHost environment variable.
csharp
if (CurrentEnvironment.IsStaging())
{
var host = Environment.GetEnvironmentVariable("StagingHost");
if (!string.IsNullOrEmpty(host))
{
services.AddSingleton((ip) =>
{
var stagingHandler = new StagingMessageHandler
{
StagingHost = host
};
return new HttpClient(stagingHandler);
});
}
}1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
2
3
4
5
6
7
8
9
10
11
12
13
14
15
The StagingMessageHandler replaces the first occurrence of "contentful" in API request URLs with the value of StagingHost, allowing you to test against a staging instance of the Contentful CMS.
| Environment Variable | Purpose | Example Value |
|---|---|---|
ASPNETCORE_ENVIRONMENT | Set to Staging to enable staging mode | Staging |
StagingHost | Custom host to replace "contentful" in API URLs | staging.example.com |
HTTPS redirection
For example, in non-Development environments, the application enables an HTTPS redirection rule and a generic exception handler.
csharp
// This logic is inside the Configure method
if (env.IsDevelopment())
{
app.UseDeveloperExceptionPage();
app.UseBrowserLink();
}
else
{
// This rule is critical for deployments behind a reverse proxy that terminates SSL.
options.Add((c) => {
var request = c.HttpContext.Request;
if(request.Headers.ContainsKey("X-Forwarded-Proto") && request.Headers["X-Forwarded-Proto"] == "http")
{
var response = c.HttpContext.Response;
response.StatusCode = StatusCodes.Status301MovedPermanently;
c.Result = RuleResult.EndResponse;
response.Headers[HeaderNames.Location] = "https://" + request.Host + request.Path + request.QueryString;
}
} );
app.UseExceptionHandler("/Error");
}1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
This code inspects the X-Forwarded-Proto header, commonly added by load balancers and reverse proxies, to ensure all traffic is served over HTTPS in production.
Performance and scaling
The application's stateless nature makes it well-suited for horizontal scaling (i.e., running multiple instances behind a load balancer). However, there is one critical consideration:
Session state
The application uses ASP.NET Core sessions, which are configured with an unusually long idle timeout.
csharp
services.AddSession(options => {
// IdleTimeout is set to a high value to confirm to requirements for this particular application.
// In your application you should use an IdleTimeout that suits your application needs or stick to the default of 20 minutes.
options.IdleTimeout = TimeSpan.FromDays(2);
});1
2
3
4
5
2
3
4
5
Gotcha: By default, AddSession() uses an in-memory session provider. This will not work in a multi-instance (scaled-out) environment, as a user's session would only exist on the specific server instance they first connected to. For any scaled deployment, you must configure a distributed session state provider, such as:
- Distributed SQL Server Cache:
services.AddDistributedSqlServerCache(...) - Distributed Redis Cache:
services.AddStackExchangeRedisCache(...)
Failure to configure a distributed session store will lead to inconsistent behavior and apparent "lost" sessions for users in a load-balanced environment.
Choosing a deployment method
The best deployment method depends on your team's expertise, infrastructure, and scalability requirements.
| Method | Ease of Use | Flexibility & Control | Cost Model | Best For |
|---|---|---|---|---|
| Heroku | ★★★★★ | ★★☆☆☆ | Usage-based (PaaS) | Quickstarts, simple projects, teams preferring a managed platform. |
| Azure App Service | ★★★★☆ | ★★★☆☆ | Usage-based (PaaS) | .NET-centric teams, projects needing integration with other Azure services. |
| Docker | ★★★☆☆ | ★★★★★ | Infrastructure-based (IaaS) | CI/CD pipelines, multi-cloud/hybrid deployments, microservices. |
| Self-hosted | ★★☆☆☆ | ★★★★★ | Infrastructure-based (IaaS) | Teams with existing infrastructure, requirements for maximum control and customization. |
- For rapid prototyping and simplicity, Heroku is an excellent choice due to its one-click deployment capability.
- For robust, scalable .NET applications, Azure App Service provides a powerful, well-integrated environment.
- For maximum portability and modern DevOps practices, Docker is the recommended approach, as it decouples the application from the underlying infrastructure.