Appearance
Are you an LLM? You can read better optimized documentation at /deployment/docker_deployment.md for this page in Markdown format
Docker deployment
This document provides a comprehensive guide for building, running, and deploying TheExampleApp using Docker. Containerization is the recommended approach for achieving consistent, reproducible deployments across different environments, from local development to production.
For a general overview of deployment strategies, see the Deployment Overview.
Containerized deployment
Containerizing the application with Docker offers several key advantages:
- Environment Consistency: A Docker image packages the application, its dependencies, and the exact runtime environment (.NET Core 2.1 runtime). This eliminates "it works on my machine" issues by ensuring the application runs identically everywhere.
- Simplified Dependency Management: The container includes the necessary ASP.NET Core runtime, so it does not need to be installed on the host machine.
- Isolation: Containers run in isolated environments, preventing conflicts with other applications or services on the same host.
- Scalability: Containerized applications are easy to scale horizontally using orchestration platforms like Kubernetes.
The core of our containerization strategy is defined in two files at the root of the project:
Dockerfile: A script that contains instructions for building the application's Docker image..dockerignore: A file that specifies which files and directories should be excluded from the build context to optimize the build process.
Dockerfile explained
The application uses a multi-stage Dockerfile to create an optimized, production-ready image. This approach uses a larger SDK image to build the application and then copies only the necessary published artifacts into a smaller, more secure runtime image.
Here is a detailed breakdown of the Dockerfile:
dockerfile
# Stage 1: The Builder
# Uses the full .NET Core SDK to build the application.
FROM microsoft/dotnet:2.1-sdk AS builder
WORKDIR /src
# Copy all source files into the container.
# The .dockerignore file prevents bin/ and obj/ folders from being copied.
COPY . .
# Restore NuGet package dependencies.
RUN dotnet restore
# Build and publish the application for release.
# The output is placed in the /app/ directory inside this stage.
RUN dotnet publish -c Release -o /app/
# Stage 2: The Runtime
# Uses the lightweight ASP.NET Core runtime image for the final container.
FROM microsoft/aspnetcore:2.
WORKDIR /app
# Set the environment. This can be overridden at runtime.
# See /deployment/environment_variables.md for more details.
ENV ASPNETCORE_ENVIRONMENT=Heroku
# Copy the published application from the builder stage.
COPY --from=builder /app .
# Define the entry point for the container.
ENTRYPOINT ["dotnet", "TheExampleApp.dll"]1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
Multi-Stage Build Process
Builder Stage (
AS builder):- It starts from the
microsoft/dotnet:2.1-sdkbase image, which contains the complete .NET Core SDK and tooling required to compile the application. - The
COPY . .command copies the project source code into the container. The.dockerignorefile is crucial here, as it prevents thebinandobjdirectories from being included in the build context, which speeds up the build and avoids potential caching issues. dotnet restoreanddotnet publishare executed to compile the application and package it into a distributable format in the/app/directory.
- It starts from the
Runtime Stage:
- This stage begins with a new, minimal base image:
microsoft/aspnetcore:2.. Note: The version tag appears to be incomplete in the Dockerfile. It should likely be2.1to match the SDK version used in the builder stage. This image only contains the ASP.NET Core runtime, making the final image significantly smaller and more secure than if we had used the SDK image. - The
COPY --from=builder /app .command is the key to the multi-stage pattern. It copies only the published output from the builder stage into the final runtime image, leaving behind all the source code and intermediate build artifacts. - The
ENTRYPOINTinstruction configures the container to executedotnet TheExampleApp.dllon startup, which runs the application using the Kestrel web server.
- This stage begins with a new, minimal base image:
Environment Configuration
The Dockerfile sets a default environment variable:
dockerfile
ENV ASPNETCORE_ENVIRONMENT=Heroku1
This configures the ASP.NET Core environment to Heroku. The application code in Startup.cs does not have specific logic for a "Heroku" environment, but this setting can be used for convention-based configuration loading (e.g., appsettings.Heroku.json if present).
The application recognizes three primary environment modes in Startup.cs:
- Development (
env.IsDevelopment()): Enables developer exception pages and browser link features. - Staging (
env.IsStaging()): Supports redirecting Contentful API calls to a custom host via theStagingHostenvironment variable. This is useful for testing against a staging instance of Contentful. - Production (default): Enables HTTPS redirection when deployed behind a reverse proxy that sets the
X-Forwarded-Protoheader, and uses generic error pages.
For deployment targets other than Heroku, it is recommended to override this variable to match your environment. For more information, see the Environment Variables documentation.
Building the image
To build the Docker image from the project root directory (where the Dockerfile is located), run the following command:
bash
# Build the image and tag it as 'the-example-app'
docker build -t the-example-app .1
2
2
Image Optimization and Layer Caching
The multi-stage build is the primary optimization, resulting in a small final image. Docker's layer caching mechanism further optimizes build times. Each instruction in the Dockerfile creates a new layer. If the files related to an instruction have not changed since the last build, Docker reuses the existing layer instead of rebuilding it.
Note: The current COPY . . command copies all files at once. A potential optimization is to copy the .csproj file first, run dotnet restore, and then copy the rest of the source code. This would allow Docker to cache the restored packages layer even when only .cs or .cshtml files change, speeding up subsequent builds.
Running containers
Once the image is built, you can run it as a container using the docker run command. The application requires configuration for the Contentful API, which must be provided as environment variables.
The application listens on port 80 inside the container by default. You need to map a port from your host machine to the container's port 80.
Example docker run command
This command runs the container, maps host port 3000 to container port 80, and provides the necessary Contentful credentials as environment variables.
bash
docker run -d -p 3000:80 \
-e ContentfulOptions__SpaceId="<YOUR_SPACE_ID>" \
-e ContentfulOptions__DeliveryApiKey="<YOUR_DELIVERY_API_KEY>" \
-e ContentfulOptions__PreviewApiKey="<YOUR_PREVIEW_API_KEY>" \
-e ASPNETCORE_ENVIRONMENT="Development" \
--name the-example-app-container \
the-example-app1
2
3
4
5
6
7
2
3
4
5
6
7
For staging environments, you can additionally set the StagingHost environment variable to redirect Contentful API calls:
bash
docker run -d -p 3000:80 \
-e ContentfulOptions__SpaceId="<YOUR_SPACE_ID>" \
-e ContentfulOptions__DeliveryApiKey="<YOUR_DELIVERY_API_KEY>" \
-e ContentfulOptions__PreviewApiKey="<YOUR_PREVIEW_API_KEY>" \
-e ASPNETCORE_ENVIRONMENT="Staging" \
-e StagingHost="<YOUR_STAGING_HOST>" \
--name the-example-app-container \
the-example-app1
2
3
4
5
6
7
8
2
3
4
5
6
7
8
| Parameter | Description |
|---|---|
-d | Runs the container in detached mode (in the background). |
-p 3000:80 | Maps port 3000 on the host to port 80 inside the container. You can then access the application at http://localhost:3000. |
-e ContentfulOptions__... | Sets environment variables. ASP.NET Core's configuration system uses the double underscore (__) to map these to the nested JSON structure in appsettings.json. See Environment Variables. |
-e ASPNETCORE_ENVIRONMENT | Overrides the default environment set in the Dockerfile. Common values: Development, Staging, or Production. |
-e StagingHost | (Optional) When ASPNETCORE_ENVIRONMENT is set to Staging, this redirects Contentful API calls to a custom staging host. |
--name the-example-app-container | Assigns a friendly name to the running container for easier management. |
the-example-app | The name of the image to run. |
For information on obtaining Contentful credentials, refer to the setup instructions in the Prerequisites guide.
Production deployment considerations
When deploying to production (any environment other than Development), the application automatically enforces HTTPS redirection if it detects that it is running behind a reverse proxy or load balancer. Specifically, the application checks for the X-Forwarded-Proto HTTP header and redirects HTTP requests to HTTPS with a 301 Moved Permanently status code (Startup.cs:86-93).
This behavior is important when deploying containers behind:
- Reverse proxies (e.g., nginx, Apache, Traefik)
- Cloud load balancers (e.g., AWS ALB/ELB, Azure Load Balancer, GCP Load Balancer)
- Container orchestration ingress controllers (e.g., Kubernetes Ingress, Istio)
Ensure that your reverse proxy or load balancer is configured to:
- Terminate SSL/TLS connections
- Set the
X-Forwarded-Protoheader to indicate the original protocol (httporhttps)
If you do not want automatic HTTPS redirection, set ASPNETCORE_ENVIRONMENT to Development.
Container orchestration
While docker run is suitable for local development and single-instance deployments, production environments typically use an orchestrator to manage container lifecycle, scaling, and networking.
Docker Compose
For local development, Docker Compose can simplify the process of running the application. A docker-compose.yml file can define the service, its build context, ports, and environment variables.
Example docker-compose.yml:
yaml
version: '3.8'
services:
webapp:
build:
context: .
dockerfile: Dockerfile
image: the-example-app
container_name: the-example-app-compose
ports:
- "3000:80"
environment:
- ASPNETCORE_ENVIRONMENT=Development
- ContentfulOptions__SpaceId=${CONTENTFUL_SPACE_ID}
- ContentfulOptions__DeliveryApiKey=${CONTENTFUL_DELIVERY_API_KEY}
- ContentfulOptions__PreviewApiKey=${CONTENTFUL_PREVIEW_API_KEY}
# You can create a .env file in the same directory to store the variables1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
With this file, you can start the application using docker-compose up.
Kubernetes
The container image is fully compatible with Kubernetes. Deploying to a Kubernetes cluster involves creating several resource manifests:
- Secret: To securely store the Contentful API keys and other sensitive configuration. This is the recommended practice over using plain environment variables in a deployment manifest.
- Deployment: To declare the desired state for the application. It defines the container image to use, the number of replicas (pods), and references the Secret for configuration. Kubernetes will automatically manage the pods, restarting them if they fail and handling rolling updates.
- Service: To expose the application to network traffic. A
LoadBalancerorIngressservice type would be used to make the application accessible from outside the cluster.
Deploying to Kubernetes provides robust scalability, self-healing, and advanced deployment patterns, making it an ideal choice for production workloads.