Appearance
Are you an LLM? You can read better optimized documentation at /deployment/docker.md for this page in Markdown format
Docker
This document provides a comprehensive guide for building, running, and managing the application using Docker. Containerization with Docker ensures a consistent and reproducible environment for development, testing, and deployment.
Overview
The project includes a Dockerfile configured to build and run the ASP.NET Core application in a containerized Linux environment. This approach encapsulates the application and its dependencies, simplifying the setup process and guaranteeing consistency across different machines. For alternative setup methods, refer to the Installation Guide.
The Docker setup utilizes a multi-stage build pattern to create an optimized, production-ready image.
Dockerfile Overview
The Dockerfile in the root of the repository defines a two-stage build process. This is a best practice for .NET Core applications as it separates the build environment from the runtime environment, resulting in a smaller and more secure final image.
Known Issue: The source code's Dockerfile contains a truncated base image reference (
FROM microsoft/aspnetcore:2.instead ofFROM microsoft/aspnetcore:2.1). This will cause the Docker build to fail. You must manually correct line 8 of the Dockerfile to readFROM microsoft/aspnetcore:2.1before building the image. The corrected version is shown below.
dockerfile
# Stage 1: The Build Environment
# Uses the full .NET Core SDK to build and publish the application.
FROM microsoft/dotnet:2.1-sdk AS builder
WORKDIR /src
# Copy all project files into the container.
COPY . .
# Restore NuGet dependencies.
RUN dotnet restore
# Build and publish the application for release.
# The output is placed in the /app/ directory.
RUN dotnet publish -c Release -o /app/
# Stage 2: The Runtime Environment
# Uses the lightweight ASP.NET Core runtime image.
FROM microsoft/aspnetcore:2.1
WORKDIR /app
# Note: The environment is hardcoded for Heroku compatibility.
# This can be overridden at runtime.
ENV ASPNETCORE_ENVIRONMENT=Heroku
# Copy the published application artifacts from the 'builder' stage.
COPY --from=builder /app .
# Define the command to run the application.
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
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
Stage 1: builder
- Base Image:
microsoft/dotnet:2.1-sdk. This image contains the full .NET Core 2.1 SDK, necessary for compiling the application and restoring dependencies. - Process: It copies the entire source code, restores NuGet packages with
dotnet restore, and then publishes a release build of the application into the/app/directory inside the container.
Stage 2: Final Image
- Base Image:
microsoft/aspnetcore:2.1. This is a much smaller image that only contains the .NET Core runtime and ASP.NET Core libraries required to run the application. It does not include the SDK. - Process: It sets the working directory to
/appand copies only the published output from thebuilderstage. This is the key to the multi-stage build's efficiency. - Entrypoint: It configures the container to execute
dotnet TheExampleApp.dllon startup, which runs the application.
Building the Docker Image
To build the Docker image from the Dockerfile, navigate to the root of the repository and run the docker build command. It's recommended to tag the image with a descriptive name and version.
bash
# Syntax: docker build -t <image-name>:<tag> <path-to-dockerfile-context>
docker build -t the-example-app:latest .1
2
2
-t the-example-app:latest: Tags the resulting image with the namethe-example-appand the taglatest..: Specifies that the build context (the set of files sent to the Docker daemon) is the current directory. The.dockerignorefile will exclude unnecessary files from this context.
Running a Container
Once the image is built, you can run it as a container using the docker run command. You must provide environment variables for the Contentful configuration and map a port to access the web application.
bash
docker run --rm -it \
-p 8080:80 \
-e "ASPNETCORE_ENVIRONMENT=Development" \
-e "ContentfulOptions:DeliveryApiKey=<YOUR_DELIVERY_API_KEY>" \
-e "ContentfulOptions:PreviewApiKey=<YOUR_PREVIEW_API_KEY>" \
-e "ContentfulOptions:SpaceId=<YOUR_SPACE_ID>" \
the-example-app:latest1
2
3
4
5
6
7
2
3
4
5
6
7
Command Breakdown:
--rm: Automatically removes the container when it exits.-it: Runs the container in interactive mode with a TTY, allowing you to see application logs and stop it withCtrl+C.-p 8080:80: Maps port8080on your host machine to port80inside the container (the default port for ASP.NET Core applications). You can then access the app athttp://localhost:8080.-e "...": Sets environment variables inside the container. These are crucial for configuring the application at runtime.
Environment Configuration
The application's configuration is managed via ASP.NET Core's configuration system, which reads from appsettings.json and then overrides values with environment variables. This is the standard pattern for containerized applications. For a detailed explanation, see the Environment Variables documentation.
Required Variables
You must provide the following environment variables to connect to the Contentful CMS. Refer to the Contentful Setup guide for details on how to obtain these keys.
| Variable | Description | Example |
|---|---|---|
ContentfulOptions:DeliveryApiKey | The API key for accessing published content. | abc...xyz |
ContentfulOptions:PreviewApiKey | The API key for accessing draft content. | 123...789 |
ContentfulOptions:SpaceId | The ID of your Contentful space. | a1b2c3d4 |
ContentfulOptions:UsePreviewApi | Set to true to use the Preview API. | false |
ASP.NET Core Environment
The Dockerfile sets ENV ASPNETCORE_ENVIRONMENT=Heroku. This is done for compatibility with the project's Heroku deployment configuration. When running locally or in other environments, you should override this:
- For Development:
-e "ASPNETCORE_ENVIRONMENT=Development" - For Production:
-e "ASPNETCORE_ENVIRONMENT=Production"
Multi-stage Build Benefits
The use of a multi-stage build provides several key advantages:
- Smaller Image Size: The final image is based on
aspnetcore:2.1(~255MB) instead ofdotnet:2.1-sdk(~1.7GB). This saves disk space and reduces image pull times during deployment. - Improved Security: The final image does not contain the .NET Core SDK, compilers, or source code. This significantly reduces the potential attack surface of the production container.
- Cleaner Artifacts: Only the necessary published files are included in the final image, ensuring a clean and predictable runtime environment.
Docker Compose
For a more streamlined local development experience, you can use Docker Compose. Create a docker-compose.yml file in the project root to define the service, build context, ports, and environment variables.
yaml
# docker-compose.yml
version: '3.7'
services:
webapp:
build:
context: .
dockerfile: Dockerfile
image: the-example-app:dev
ports:
- "8080:80"
environment:
- ASPNETCORE_ENVIRONMENT=Development
- ContentfulOptions:DeliveryApiKey=${CONTENTFUL_DELIVERY_API_KEY}
- ContentfulOptions:PreviewApiKey=${CONTENTFUL_PREVIEW_API_KEY}
- ContentfulOptions:SpaceId=${CONTENTFUL_SPACE_ID}
# To use environment variables from a file, create a .env file
# in the same directory as this docker-compose.yml file.
# Example .env file:
# CONTENTFUL_DELIVERY_API_KEY=...
# CONTENTFUL_PREVIEW_API_KEY=...
# CONTENTFUL_SPACE_ID=...1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
With this file, you can build and run the application with a single command:
bash
# Build and start the container in the background
docker-compose up --build -d
# View logs
docker-compose logs -f
# Stop and remove the container
docker-compose down1
2
3
4
5
6
7
8
2
3
4
5
6
7
8
.dockerignore
The .dockerignore file is critical for optimizing the build process. It prevents specified files and directories from being copied into the Docker image during the build.
# .dockerignore
bin\
obj\1
2
3
2
3
By excluding the bin and obj directories, we ensure that:
- The build context sent to the Docker daemon is smaller, speeding up the
docker buildcommand. - No local build artifacts interfere with the clean build process happening inside the container.
Note on Path Separators: The .dockerignore file currently uses Windows-style backslashes (\). While Docker generally handles both forward slashes and backslashes, the recommended cross-platform syntax is to use forward slashes (bin/ and obj/) or omit the trailing separator entirely (bin and obj). If you encounter issues with the ignore patterns not working correctly on Linux or macOS, update the file to use forward slashes.
Image Optimization
While the current Dockerfile is effective, it can be further optimized to better leverage Docker's layer caching. By copying the project files and restoring dependencies before copying the rest of the source code, Docker can reuse the dependency layer if only the application code changes.
Optimized Dockerfile Example:
dockerfile
FROM microsoft/dotnet:2.1-sdk AS builder
WORKDIR /src
# Copy only the .csproj file to restore dependencies first
COPY TheExampleApp/TheExampleApp.csproj ./TheExampleApp/
RUN dotnet restore ./TheExampleApp/TheExampleApp.csproj
# Copy the rest of the source code
COPY . .
WORKDIR /src/TheExampleApp
RUN dotnet publish -c Release -o /app/
# --- Final Stage (remains the same) ---
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
15
16
17
18
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
This change ensures that dotnet restore is only re-run when the TheExampleApp.csproj file changes, leading to faster rebuilds during development.
Security Considerations
- Base Image Updates: The base images (
microsoft/dotnet:2.1-sdkandmicrosoft/aspnetcore:2.1) are for an older, out-of-support version of .NET Core. For any new development, these should be updated to a supported LTS version (e.g., .NET 6.0 or 8.0). For maintenance, periodically pull the latest2.1images to receive security patches. - Vulnerability Scanning: Regularly scan the final image for known vulnerabilities using tools like Trivy, Snyk, or Docker Scout.bash
# Example using Trivy trivy image the-example-app:latest1
2 - Secrets Management: The application correctly avoids hardcoding secrets in the image by using environment variables. The default keys in
appsettings.jsonare for a public example space. In a real-world application, these should be removed and sourced exclusively from a secure environment configuration or secret management tool.