Best practices for containerizing .net applications
Tags: applicationsecurity, opensource
Published: 2022-09-05 12:18:56

---
title: Best practices for containerizing .net applications
published: true
date: 2022-08-31 15:31:15 UTC
tags: ApplicationSecurity,OpenSource
canonical_url: https://snyk.io/blog/best-practices-for-containerizing-net-applications/
---

Containerization with Docker has become a major trend in web application development that many .NET developers have adopted. There are many compelling advantages for developers and DevOps engineers to containerize .NET applications, even when working with the older .NET Framework 4.x versions. However, if we don’t know how to use containers properly, we’ll experience little benefit from them.

In this article, we’ll cover some best practices for containerizing .NET applications — including those on the 4.x version framework. We’ll also discuss using small images and image scanning to reduce security risks and remove unnecessary components from our containers.

### Requirements

Before getting started, you’ll need to:

- Download and install [Visual Studio 2022](https://visualstudio.microsoft.com/vs/).
- Have a local Docker client to run containers locally. You can use [Docker Desktop](https://www.docker.com/get-docker), which requires Windows 10 or later. Be sure to choose the Windows installation.
- Download and install the [Snyk Command Line Interface (CLI)](https://docs.snyk.io/snyk-cli/install-the-snyk-cli).
- Sign up for a [free Snyk account](https://app.snyk.io/login?cta=sign-up&loc=nav&page=support_docs_page).

## Consider containerizing your .NET Apps (even .NET Framework 4.x)

After .NET Core 1.0, web apps became cross-platform, portable, and open source, enabling developers to port them to Docker and experience the most out of containerization.

But, while newer .NET versions running in Linux garner the most attention, [Windows containers](https://hub.docker.com/_/microsoft-windows-base-os-images) support the older Windows-only .NET versions that countless enterprise applications rely on. And not every organization maintaining .NET Framework web apps is ready to port them to newer versions. This is why containerizing our .NET Framework 4.x applications makes sense.

The following steps demonstrate how to create and run a containerized [ASP.NET](http://asp.net/) Web Forms application in Visual Studio 2022. 

First, run **Docker Desktop** on your development machine:

![](https://snyk.io/wp-content/uploads/blog-net-docker-desktop-search.jpg)

From the Visual Studio **Get started** window, select **Create a new project**.

![](https://snyk.io/wp-content/uploads/blog-net-create-new-project.jpg)

Type “Web Forms” in the **Search for templates** textbox and select the **ASP.NET Web Application (.NET Framework)**.

![](https://snyk.io/wp-content/uploads/blog-net-web-forms.jpg)

Enter a name for your new application (for example, `WebFormsApp`), specify the location on the disk, select **.NET Framework 4.8** , and click **Create**.

![](https://snyk.io/wp-content/uploads/blog-net-configure-new-project-name.jpg)

Then, select the **Web Forms** type.

![](https://snyk.io/wp-content/uploads/blog-net-create-new-asp.net_.jpg)

Make sure that the **Web Forms** and the **Docker support** options are checked, then click **Create**.

![](https://snyk.io/wp-content/uploads/blog-net-add-folder-ref.jpg)

Wait for Visual Studio to create the project. This is the project structure as shown in Visual Studio:

![](https://snyk.io/wp-content/uploads/blog-net-solution-webforms-1of1.jpg)

Now, open Docker Desktop. You’ll notice that the `WebFormsApp` container is running on port 61469:

![](https://snyk.io/wp-content/uploads/blog-net-docker-webformsapp.jpg)

Open the **Images** tab to see the `WebFormsApp` image, which the **WebFormsApp** container runs from.

![](https://snyk.io/wp-content/uploads/blog-net-docker-images-on-disk.jpg)

Finally, press **Ctrl** + **F5** to build your Docker image and run it locally. When the container image is built and running in a Docker container, Visual Studio launches the web app in your default browser.

![](https://snyk.io/wp-content/uploads/blog-net-asp.net_.jpg)

Now, return to Visual Studio and click the `Dockerfile` file:

![](https://snyk.io/wp-content/uploads/blog-net-solution-dockerfile.jpg)

This will open the `Dockerfile`, which configures your project to run in a container.

![](https://snyk.io/wp-content/uploads/blog-net-dockerfile-from.jpg)

The `FROM` instruction initializes a new build stage and sets the Base Image for subsequent instructions. In this case, the image is `aspnet:4.8-windowsservercore-ltsc2019`, which Docker pulls from Microsoft’s [image repository](http://mcr.microsoft.com).

Of course, it’s more likely that your Web Forms project wasn’t initially created with Docker support. To simulate this particular situation, create a new ASP.NET Web Forms project, but this time without Docker support:

![](https://snyk.io/wp-content/uploads/blog-net-solution-webforms-2of2.jpg)

Now, let’s add a Dockerfile to the new project. Right-click on the project name, choose the **Add** menu, and then the **Docker Support** submenu:

![](https://snyk.io/wp-content/uploads/blog-net-add-docker-support.jpg)

This will make Visual Studio create the Dockerfile project automatically.

![](https://snyk.io/wp-content/uploads/blog-net-solution-explorer-dockerfile.jpg)

Note that Visual Studio has already selected the image and source code path from which the container will be generated.

![](https://snyk.io/wp-content/uploads/blog-net-dockerfile-workdir.jpg)

From here, your project already has the support and benefits inherent to containerization with Docker — including portability, agility, faster delivery, improved security, and easier management.

## Keep container images as small as possible

Both Linux and Windows containers offer slim base images that contain only the apps and services necessary for your application to work. A small image with minimal dependencies minimizes the number of vulnerabilities introduced through the dependencies, reducing the attack surface.

Let’s discuss some base images that .NET developers might consider for small containers. First, if you’re still using ASP.NET Framework 4.x, which is not supported in Linux, your only option is Windows containers. Microsoft’s recommended slim base images are [Windows Server Core](https://hub.docker.com/_/microsoft-windows-servercore) and [Nano Server](https://hub.docker.com/_/microsoft-windows-nanoserver). 

Windows Server Core is a minimal, command-line-driven OS for performing Windows Server roles and running applications that don’t require the standard Windows graphical user interface. Nano Server is an operating system optimized for private clouds and data centers. Nano Server has no local logon capability, is smaller, and restarts much faster than Server Core.

For .NET Core and .NET 5+, [SDK images](https://hub.docker.com/_/microsoft-dotnet-sdk/) should only be used for the build, and [runtime images](https://hub.docker.com/_/microsoft-dotnet-runtime/) should be used for production deployments as part of a multistage build. Some sufficient slim base images include the [Debian bullseye-slim-amd64](https://hub.docker.com/r/amd64/debian/) and [Alpine Linux](https://hub.docker.com/_/alpine) images.

We can make base images even smaller by using images for the [.NET runtime dependencies](https://hub.docker.com/_/microsoft-dotnet-runtime-deps/) and building our .NET application in [self-contained](https://docs.microsoft.com/en-ca/dotnet/core/deploying/#publish-self-contained) mode — so we only pull the dependencies our app needs and nothing more.

Now, let’s practice building a .NET 6 web application with slim images. Open Visual Studio 2022, click **Create New Project** , and select the **ASP.NET Core Web App** type.

![](https://snyk.io/wp-content/uploads/blog-net-solution-webapp.jpg)

Give the project a name, such as `WebApp`.

![](https://snyk.io/wp-content/uploads/blog-net-configure-new-project-location.jpg)

Click **Next** and select these options:

- .NET 6.0 Framework
- Authentication type: None
- Configure for HTTPS: Check
- Enable Docker: Check
- Docker OS: Linux

![](https://snyk.io/wp-content/uploads/blog-net-additional-information.jpg)

Click the **Create** button and observe the new project on Visual Studio.

![](https://snyk.io/wp-content/uploads/blog-net-template-search.jpg)

Now press **F5** to run the web app hosted on your local Docker container.

![](https://snyk.io/wp-content/uploads/blog-net-home-page-webapp.jpg)

Now run the `docker images` command to get a list of Docker images on your system:

```
> docker images
```

This command will result in something like the following:

```
REPOSITORY TAG IMAGE ID CREATED SIZE
webapp dev e3b51b1eef08 4 hours ago 208MB
```

Note that the app is running on a Linux container, and the size of the webapp image above is 208 MB.

Next, let’s reduce the size of our web app container image. 

Stop the app on Visual Studio, and open the `Dockerfile` file. You’ll see the following block instructing Docker to pull the `dotnet/aspnet:6.0` and `dotnet/sdk:6.0` images from the Microsoft Container Registry (mcr), and use them as base images for your app container:

```
FROM mcr.microsoft.com/dotnet/aspnet:6.0 AS base
WORKDIR /app
EXPOSE 80
EXPOSE 443

FROM mcr.microsoft.com/dotnet/sdk:6.0 AS build
WORKDIR /src
COPY ["WebApp/WebApp.csproj", "WebApp/"]
RUN dotnet restore "WebApp/WebApp.csproj"
COPY . .
WORKDIR "/src/WebApp"
RUN dotnet build "WebApp.csproj" -c Release -o /app/build
```

Then press **F5** to rerun the application. Once the web app is up and running, execute the `docker images` command to get a list of Docker images on your system:

```
> docker images
```

Your image list will appear like the following:

```
REPOSITORY TAG IMAGE ID CREATED SIZE
webapp dev 778bcd52f366 4 hours ago 100MB
<none> <none> e3b51b1eef08 4 hours ago 208MB
```

Note that the webapp image has been rebuilt, and its size is now 100 MB (108 MB smaller than the original image).

## Scan images for vulnerabilities

Frequently updating the codebase is a best practice in itself. The most obvious advantages include having access to new features, bug fixes, a better user interface, and improved performance. But the security updates are equally important. 

There may be unpatched vulnerabilities in the base image’s operating system or libraries, and any scripts, code, and apps added to the base image may contain vulnerabilities that we don’t want to accidentally ship to production.

Scanning containers for vulnerabilities is essential to assess potential security risks and take the appropriate action. In this case, let’s pick an old .NET image: [mcr.microsoft.com/dotnet/core/aspnet:2.2](http://mcr.microsoft.com/dotnet/core/aspnet:2.2).

```
dotnet new webapp -n "AspNet2.2" -f "netcoreapp2.2"
```

Let’s open the project on Visual Studio and add a Dockerfile to the new project. Right-click on the project name, choose the **Add** menu, and then click the **Docker Support** submenu.

Now open the `Dockerfile` file and replace its contents with the following code:

```
#See https://aka.ms/containerfastmode to understand how Visual Studio uses this Dockerfile to build your images for faster debugging.

FROM mcr.microsoft.com/dotnet/core/aspnet:2.2 AS base
WORKDIR /app
EXPOSE 80
EXPOSE 443

FROM mcr.microsoft.com/dotnet/core/sdk:2.2.105 AS build
WORKDIR /src
COPY ["AspNet2.2.csproj", "."]
RUN dotnet restore "./AspNet2.2.csproj"
COPY . .
WORKDIR "/src/."
RUN dotnet build "AspNet2.2.csproj" -c Release -o /app/build

FROM build AS publish
RUN dotnet publish "AspNet2.2.csproj" -c Release -o /app/publish

FROM base AS final
WORKDIR /app
COPY --from=publish /app/publish .
ENTRYPOINT ["dotnet", "AspNet2.2.dll"]
```

Next, we’ll see how to apply image scanning to discover how vulnerable our container is. First, run the `docker images` CLI command to list your current local images:

```
docker images

REPOSITORY TAG IMAGE ID CREATED SIZE
aspnet22 latest 223b485f4516 About a minute ago 265MB
```

Then run the `snyk auth` command to authenticate for the Snyk CLI:

```
> snyk auth
```

Once the account has been authenticated, we’re ready to use the Snyk CLI. Let’s run the `snyk container test <repository>:<tag>` command to test the image we’ve built locally, and made available in our local Docker daemon:

```
> snyk container test aspnet22:latest
```

![](https://snyk.io/wp-content/uploads/blog-net-high-severity-vuln-1240x654.jpg)

The `snyk container test` command generates a list of the components installed in the image, sends that list to the Snyk service, and returns a list of the vulnerabilities in our image.

In the end, the image scanning found 193 vulnerabilities:

![](https://snyk.io/wp-content/uploads/blog-net-docker-image-193-issues-1240x547.jpg)

To monitor the image, run the `snyk container monitor <repository>:<tag>` command:

```
> snyk container monitor aspnet22:latest
```

![](https://snyk.io/wp-content/uploads/blog-net-aspnet22.jpg)

The `snyk container monitor` command generates a list of the components installed in the image, sends that list to the Snyk service, and returns a link to the Snyk service at `http://app.snyk.io/org/{YOUR-ACCOUNT}/`, where you can see the results:

![](https://snyk.io/wp-content/uploads/blog-net-snyk-recommendations.jpg)

## Top 3 containerization tips

We’ve seen many compelling advantages for developers and DevOps engineers to containerize .NET applications. However, containers are not a magical solution for all problems. We still need to implement some best practices that help demystify containers and give directions on how to get the most out of them.

Despite the persistent misconception that containers are just for .NET Core apps, we’ve seen how containers can benefit .NET Framework 4.x applications as well.

When it comes to the container image size, the smaller, the better. Fortunately, we’ve learned that some base images are small enough to contain only what our app needs to run, and reduce the attack surface to the container.

Lastly, we’ve seen how to mitigate security risks by scanning container images and keeping them updated. The Snyk container test and Snyk container monitor commands are invaluable tools that help us scan and continuously monitor our .NET containers for vulnerabilities. When used correctly, containerization can provide that extra layer of security and flexibility to our apps, regardless of their age.

## Secure your container images for free

Create a Snyk account today to increase application security with comprehensive scanning and continuous container monitoring.

[Sign up for free](https://app.snyk.io/login?cta=sign-up&loc=body&page=best-practices-for-containerizing-net-applications)
<!-- /.block -->