Docker All The Things

Docker All The Things

Build an ASP.NET Core application with Docker and Visual Studio

Build an ASP.NET Core application with Docker and Visual Studio

In this article will guide you building, running, and debugging an ASP.NET Core application with Docker and Visual Studio ...

Development frameworks, platforms, and tools that do not offer a rich development experience will ultimately lack in adoption. Docker is an amazing technology but, what is the development experience like? Not too long ago, I wrote about creating and debugging an ASP.NET Core Docker container in two different ways.

  1. Visual Studio 2017 (Windows)
  2. Visual Studio Code (Linux)

With the launching of Visual Studio 2019 recently, I felt compelled to target one more IDE  .


The first step is making sure Visual Studio is set up correctly. This is a simple as installing Visual Studio 2019 with the .NET Core cross-platform development workload installed. More specifically, if you select Individual Components, you need to ensure that the Container Development Tools component is selected as shown below.

Lastly, you will need to have Docker Desktop for Windows installed if you haven’t already. Once this is done, we are ready to create an ASP.NET Core Docker container in Visual Studio.


You may also like: Tutorial Laravel 6 with Docker and Docker-Compose



As explained in this blog post, the new project dialog in Visual Studio has been given an overhaul. I have become quite accustomed to the previous version however, I must admit the improvements are very intuitive. For this tutorial, we will select the ASP.NET Core Web Application template and click Next.

Once this is done, we can give our new project a name, location, and solution name. As you can see, this process is much more like a wizard as opposed to the monolithic dialog that was used in previous versions.

Now we can provide some more specifics for our new application. For the purposes of this example we will select the API project template. That said, a key piece can be found in the advanced section. Here we want to select Enable Docker Support and make sure Linux is selected in the following drop-down.

Similar to when working with Visual Studio 2017, a Dockerfile is generated with four named build stages (base, build, publish, and final). Multistage builds are helpful to optimize layers and keep our Dockerfile easy to maintain.

FROM AS base

FROM AS build
COPY ["JrTech.Docker.Vs2019/JrTech.Docker.Vs2019.csproj", "JrTech.Docker.Vs2019/"]
RUN dotnet restore "JrTech.Docker.Vs2019/JrTech.Docker.Vs2019.csproj"
COPY . .
WORKDIR "/src/JrTech.Docker.Vs2019"
RUN dotnet build "JrTech.Docker.Vs2019.csproj" -c Release -o /app

FROM build AS publish
RUN dotnet publish "JrTech.Docker.Vs2019.csproj" -c Release -o /app

FROM base AS final
COPY --from=publish /app .
ENTRYPOINT ["dotnet", "JrTech.Docker.Vs2019.dll"]

Next lets take a look at how our application gets built and deployed as a container.


Prior to building or debugging our application, we will already notice some activity in the Container Tools output in the output window.

========== Checking for Container Prerequisites ==========
Verifying that Docker Desktop is installed...
Docker Desktop is installed.
========== Verifying that Docker Desktop is running... ==========
Verifying that Docker Desktop is running...
Docker Desktop is running.
========== Verifying Docker OS ==========
Verifying that Docker Desktop's operating system mode matches the project's target operating system...
Docker Desktop's operating system mode matches the project's target operating system.
========== Pulling Required Images ==========
Checking for missing Docker images...
Docker images are ready.
========== Warming up container(s) for JrTech.Docker.Vs2019 ==========
Starting up container(s)...
docker build -f "C:\Users\jason\source\repos\JrTech.Docker.Vs2019\JrTech.Docker.Vs2019\Dockerfile" -t jrtechdockervs2019:dev --target base --label "" --label "" "C:\Users\jason\source\repos\JrTech.Docker.Vs2019"
Sending build context to Docker daemon 18.94kB
Step 1/6 : FROM AS base
---> 9a8e320a271f
Step 2/6 : WORKDIR /app
---> Using cache
---> 3bca35715a51
Step 3/6 : EXPOSE 80
---> Using cache
---> 854d77a40024
Step 4/6 : EXPOSE 443
---> Using cache
---> 962750b42169
Step 5/6 : LABEL
---> Using cache
---> 09e977d58879
Step 6/6 : LABEL
---> Using cache
---> 5053ced48dc0
Successfully built 5053ced48dc0
Successfully tagged jrtechdockervs2019:dev
SECURITY WARNING: You are building a Docker image from Windows against a non-Windows Docker host. All files and directories added to build context will have '-rwxr-xr-x' permissions. It is recommended to double check and reset permissions for sensitive files and directories.
docker run -dt -v "C:\Users\jason\vsdbg\vs2017u5:/remote_debugger:rw" -v "C:\Users\jason\source\repos\JrTech.Docker.Vs2019\JrTech.Docker.Vs2019:/app" -v "C:\Users\jason\AppData\Roaming\Microsoft\UserSecrets:/root/.microsoft/usersecrets:ro" -v "C:\Users\jason\AppData\Roaming\ASP.NET\Https:/root/.aspnet/https:ro" -v "C:\Users\jason.nuget\packages:/root/.nuget/fallbackpackages2" -v "C:\Program Files\dotnet\sdk\NuGetFallbackFolder:/root/.nuget/fallbackpackages" -e "DOTNET_USE_POLLING_FILE_WATCHER=1" -e "ASPNETCORE_ENVIRONMENT=Development" -e "NUGET_PACKAGES=/root/.nuget/fallbackpackages2" -e "NUGET_FALLBACK_PACKAGES=/root/.nuget/fallbackpackages;/root/.nuget/fallbackpackages2" -p 49558:80 -p 44304:443 --entrypoint tail jrtechdockervs2019:dev -f /dev/null
Container started successfully.
========== Finished ==========

This is a new optimization added to Visual Studio 2019. In order to allow our application to build, deploy, and run quickly Visual Studio preemptively creates a container. We can see the container by running docker ps from the command line.

λ docker ps
e93c776e500a jrtechdockervs2019:dev "tail -f /dev/null" 20 seconds ago Up 19 seconds>80/tcp,>443/tcp flamboyant_shirley

With the http and https ports exposed, the container is primed and ready to go. If we open a browser and browse to the http port, 49558 in my case, we see that we do not get a response yet. This makes sense because while our container is started, nothing is actually deployed to it yet.

To build and deploy our application to the running container, we must debug using the Docker configuration profile. This should be selected by default. Once we are up and running, we can see that our application is available through http/https ports that were exposed in our Docker container.

We can also see that the same container that was started when we created our application is still running. When we run our application a new container isn’t created, rather the output from our project is copied into the running container. We can see this by observing the running containers which shows or original container is still running.

λ docker ps
e93c776e500a jrtechdockervs2019:dev "tail -f /dev/null" 7 minutes ago Up 7 minutes>80/tcp,>443/tcp flamboyant_shirley

Visual Studio remotely attaches to the process running inside the container. This gives us the ability to set breakpoints and debug our application while it is running.

The development experience in Visual Studio 2019 is very similar to Visual Studio 2017 as it pertains to building Docker containers. That said, there are some nice enhancements under the hood that make the development process even more seemless. It is great to Microsoft’s continued investment in this great new technology!

Happy Coding!

Further Reading

How To Set Up Laravel, Nginx, and MySQL with Docker Compose

Containerizing a Node.js Application for Development With Docker Compose

Docker Basics: Docker Compose

Originally published by JROB5756  at


Thanks for reading :heart: If you liked this post, share it with all of your programming buddies! Follow me on Facebook | Twitter

Build a .Net Framework App with Visual Studio on Windows using Docker

Build a .Net Framework App with Visual Studio on Windows using Docker

In this post, you’ll build a .Net Framework application with Visual Studio on Windows 10. You’ll then containerize your application so it can be reliably deployed and run on any instance of Docker for Windows.

In this post, you’ll build a .Net Framework application with Visual Studio on Windows 10. You’ll then containerize your application so it can be reliably deployed and run on any instance of Docker for Windows.

Since containers share the host operating system, you can only run containers compatible with your host operating system kernel. For Linux based applications, practically all versions of Linux share the same kernel, so as long as you’ve installed Docker, you’re good to go. However, for .Net Framework applications, you need a Windows-based container that can only run on Windows-based hosts with the Windows Containers feature enabled.

Note: It’s possible to run Linux-based containers on Windows with a combination of virtualization and containerization to run Docker against a Linux kernel. Docker on MacOS works in a similar manner.

For this post, we’ll build a CRUD application that uses ASP.NET MVC and Entity Framework, so we have something interesting to containerize. You’ll need:

Build a .Net Framework Application

To help you get started quickly, I’ve added some sample code to a Git repo. Clone by entering this in your command line:

git clone

Once you’ve downloaded the source code, open CrudMVCCodeFirst.sln in Visual Studio, and we can get this show on the road.

Configure Identity Management for Your ASP.NET Application

If you try running the project in Visual Studio, by pressing F5 or selecting Debug > Start Debugging, you’ll find that it fails with an error like:

Message=Your Okta URL must start with https. Current value: {yourOktaDomain}. You can copy your domain from the Okta Developer Console. Follow these instructions to find it:

Thankfully, this is very easy to fix. Okta simplifies identity management, while making to more secure and scalable than is worth building yourself. Okta’s APIs create, edit, and securely store user accounts and data, and connect them with one or more applications, without the need for custom code. To add Okta to your app to offload authentication, simply configure Okta Identity Management and add the appropriate configuration values to your web.config file:

<add key="okta:ClientId" value="{clientId}" />
<add key="okta:ClientSecret" value="{clientSecret}" />
<add key="okta:OktaDomain" value="{yourOktaDomain}" />

If you don’t already have an Okta account, you can sign up for a free-forever developer account. Once logged into your Okta account, you’ll see your Okta domain at the top right of your developer dashboard.

To create a new application, select Applications from the main menu and click the Add Application button at the top of the applications list. Then choose Web Application and click Next.

To configure the application, set the values to look like below, and then click Done.

At the bottom of the General Settings tab, you’ll see a section with Client ID and Client Secret. Copy those into the corresponding configuration values from above.

Once you’ve added the correct configuration values, you’ll be able to launch the application using Ctrl-F5 or Debug > Start Without Debugging. Point your browser at https://localhost:44300 and add some Launches (after authenticating, of course!) to make sure everything is working correctly.

Docker and the .Net Framework

Now you’ve got a working sample application, you can containerize it with Docker for Windows. The process is pretty much the same for Windows containers as for Linux containers.

Add a Dockerfile to Your .NET Application

Visual Studio provides some useful tooling to containerize applications. To easily set up your application to use Docker and Docker Compose,right-click on the project node (CrudMVCCodeFirst) and then select Add > Container Orchestrator Support. In the dialog that appears, select Docker Compose. Then, as if by magic, Visual Studio adds a Dockerfile and docker-compose file to your application and things start building in the background.

Once the build process completes, start the application and keep an eye on the URL. You’ll see that it’s pointing to http://172.x.x.x What’s going on?

Note: It can take a long time to build a Docker image the first time around. The build process must first download the appropriate base image, which are generally large for Windows Containers. If you’re not blessed with a Gigabit internet connection, have some coffee, and don’t worry.We’ll wait!

Docker Networking for .NET Applications

Usually, applications running in Docker containers don’t know they’re running in a container. They talk to other containerized applications over the network as though they were running on physical hardware. However, Docker’s networking capabilities are extensible and can be set up in many different ways to support particular scenarios. Visual Studio automatically deploys your Docker container on a virtual network with an address in the 172.x.x.x range. You can then connect to that virtual network address from your browser and see your containerized application as though it was running on a physical machine.

Create a Self-Signed Certificate for .NET in Docker

To securely authenticate, you need to add an SSL certificate to your application. When you run the application locally without Docker, then Visual Studio and Kestrel (the development web server) take care of the certificate for you automatically. However, since you’re running the app in a container, you’ll need to take a few additional steps to make this work properly.

Modify your Docker file to create a self-signed certificate. Change your Dockerfile to:

ARG source
WORKDIR /inetpub/wwwroot
COPY ${source:-obj/Docker/publish} .

SHELL ["powershell", "-Command", "$ErrorActionPreference = 'Stop'; $ProgressPreference = 'SilentlyContinue';"]

RUN $newCert=New-SelfSignedCertificate -DnsName 'localhost' -CertStoreLocation cert:\LocalMachine\My; \
  New-WebBinding -Name 'Default Web Site' -Protocol 'https'; \
  $binding=Get-WebBinding -Name 'Default Web Site' -Protocol 'https'; \

These changes use PowerShell as part of the Docker build process. The build now:

  • creates a new self-signed certificate
  • changes the default binding to use https rather than http
  • adds the new certificate to the https binding.

Important Note: The base image differs from the default one provided by Visual Studio. A known issue with windowsservercore base images earlier than ltsc2019 results in an incorrect system clock. This issue causes significant problems with authentication and certificate generation since both rely on the current date and time to determine the validity of access tokens and certificates.

When you save these changes, Visual Studio automatically builds a new Docker image. Again, this might take some time on the first build since Docker needs to download a new base image. While you wait, there’s another change you need to make to hook up SSL.

Modify Docker Compose to Use Localhost

Your build process creates a self-signed certificate that uses localhost as the domain name, although your app is currently configured to use http://172.x.x.x. To access your application on localhost, modify your docker-compose file to map port 443 in the container to port 5001 on your host machine. That way, you’ll be able to access the application as https://localhost:5001, and it’ll work properly with your self-signed certificate.

Modify your docker-compose file to:

version: '3.4'

    image: ${DOCKER_REGISTRY-}crudmvccodefirst
      context: .\CrudMVCCodeFirst
      dockerfile: Dockerfile
    - "5001:443"

Update Identity Management Configuration

You’ll also need to update your Okta Identity application with the new URL. Log into your Okta dashboard and select Applications and choose the Okta MVC Crud App. In the General tab, click Edit to change the various URLs.


Once you’ve done this, update your web.config file to use the new URLs:

  <add key="okta:RedirectUri" value="https://localhost:5001/authorization-code/callback" />
  <add key="okta:PostLogoutRedirectUri" value="https://localhost:5001/Account/PostLogout" />

Take Docker and .NET For a Spin

With any luck, by the time you get here your Docker base image has downloaded and you’re ready to go. To ensure Visual Studio has rebuilt everything after your changes, use Ctrl+F5 or Debug > Start Without Debugging to build and deploy to Docker.

Point your browser at https://localhost:5001, and see your CRUD sample application. You can also login securely via Okta, with all traffic encrypted via SSL/TLS. So far, so good.

Try creating a new Launch.

Houston, We Have a Problem

After a bit of time you’ll see:

An error occurred while processing your request.

This error is thrown by a missing SQL server. Applications run on your local machine use a SQL server installed in the Visual Studio setup process. However, when you run your application in a Docker container, no SQL server has been set up yet.

You may think the solution is to tweak the Dockerfile to install SQL Server. While that would work, it’s not the best solution. When it comes to containerization, best practice is to have each container perform a single function only. To that end, you can combine multiple containers using Docker Compose. All you need to do is pull down a container with SQL Server pre-installed and hook it up to your application.

Add a SQL Server Container

Modify your docker-compose file to include:

version: '3.4'

  image: ${DOCKER_REGISTRY-}crudmvccodefirst
    context: .\CrudMVCCodeFirst
    dockerfile: Dockerfile
    - "5001:443"
    - sqlserver
    image: microsoft/mssql-server-windows-express
        SA_PASSWORD: "JackSwigertRocks123"
        ACCEPT_EULA: "Y"

Now, instead of just one service that runs your application, you’ll have two. One for the application and one for the database server.

Since you’re using a different database, you also need to update the connection string in your web.config. Change it to:

  <!--<add name="LaunchContext" connectionString="Data Source=(LocalDb)\MSSQLLocalDB;Initial Catalog=RocketLaunch1;Integrated Security=SSPI;" providerName="System.Data.SqlClient" />-->
  <add name="LaunchContext" connectionString="Server=sqlserver;Database=RocketLaunch;User=sa;Password=JackSwigertRocks123;" providerName="System.Data.SqlClient" />

With these changes in place, once your Docker containers are rebuilt, you can browse to https://localhost:5001 and experience the joys of complete CRUD functionality for your Launches. It’s not quite up there with landing on the moon, but hopefully it was a voyage of discovery nonetheless!

Now you know how to use containerization for Windows-based applications. You also saw how containerization makes it easy to compose applications from several different components. Composition goes a long way toward improving reliability since each component does only one thing. There have been many different approaches to composition over the years, but you’ve seen how composition and containerization is a powerful combination.

What’s New for Visual Studio 2019 Integrations with Azure Boards

<strong>In this video Abel catches up with Dan Hellem for a quick walk through of the new experience of Visual Studio 2019.</strong>

In this video Abel catches up with Dan Hellem for a quick walk through of the new experience of Visual Studio 2019.

In previous versions of Visual Studio, the work item experience was centered around queries, which need to be created and managed to find the right work items. In Visual Studio 2019, we have removed queries and added a new view for work items centered at the developer. This allows the developer to quickly find the work they need and associate them to their pending changes.

Learn More

C# Developers: Double Your Coding Speed with Visual Studio

A 18 Hour SQL/SQL Server 2014/Visual Studio 2017 Course

VS Code extensions you may not have heard of before

Getting Started with Python in Visual Studio Code

Build Productive Python Web Apps with VS Code and Azure

VS Code: C++ Development with Visual Studio Code

What VS Code Extensions you should consider using?

Creating a Python Class Generator for VS Code