How to deploy a .NET app using Docker
Docker is a popular open-source technology for running applications in a containerized environment. This is useful because it allows you to easily scaffold and deploy applications to any platform that supports Docker. In this tutorial, we will learn how to build a .NET application that can be run in a Docker container. We will do this by building a Docker image and run it as a container.
What is .NET?
Before we begin, let's get a quick overview of what .NET is. .NET is a software development platform by Microsoft that is used to build and run applications. It is written in C#, a cross-platform, open-source, general-purpose, object-oriented programming language.
Installing .NET
You can download .NET by following the instructions on Microsoft's website. Their download page contains instructions for Windows, Linux, and macOS. Download the .NET SDK for your platform.
Once you have installed .NET, you can check your version like so:
BASHdotnet --version
You should output similar to this:
BASH6.0.0
Installing Docker
You will also need to install Docker.
- Visit the official Docker website to get the installer.
- After it downloads, run the installer until the end.
- Restart your computer to ensure the changes can take effect.
The Docker installer.
Directory Structure
By the end of this tutorial, our application will be in the following directory structure:
BASHapp
├── App.cs
├── App.csproj
├── docker-compose.yml
└── Dockerfile
Since the purpose of this tutorial is to Dockerize a .NET application, we will be keeping the project simple.
.NET app
This tutorial assumes that you already have a .NET app that you want to deploy on a Docker container, however we will build a very minimal one from scratch.
Create a file called App.cs
in the app
directory.
BASHtouch App.cs
Populate the file with the following code:
CSHARPusing System;
using System.Text;
using System.Net.Sockets;
using System.Net;
namespace App
{
class App
{
private static string IP = "0.0.0.0";
private static int PORT = 8080;
static void Main(string[] args)
{
TcpListener server = new TcpListener(IPAddress.Parse(IP), PORT);
server.Start();
Console.WriteLine("Server started on http://{0}:{1}", IP, PORT);
while (true)
{
TcpClient client = server.AcceptTcpClient();
string [html](https://sabe.io/classes/html) = "Hello from C#";
string[] headers = {
"HTTP/1.1 200 OK",
"Content-Type: text/html",
"Content-Length: " + html.Length,
"",
html
};
string response = string.Join("\r\n", headers);
byte[] data = Encoding.ASCII.GetBytes(response.ToCharArray(), 0, response.Length);
client.GetStream().Write(data);
}
}
}
}
This file is the entry point for your application. We are creating a simple TCP server that will respond to any request with a simple HTML page. We will use the TcpListener
class to listen for incoming connections and respond back to the client's request. We are manually defining the headers to ensure that the client knows that we are sending HTML. The server is running on 0.0.0.0
and port 8080
.
Now create a new filed called App.csproj
in the app
directory.
BASHtouch App.csproj
Add the following to the file:
HTML<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<OutputType>Exe</OutputType>
<TargetFramework>net6.0</TargetFramework>
<LangVersion>10</LangVersion>
</PropertyGroup>
</Project>
This file is the project file for your application. We are using the net6.0
target framework and the Exe
output type. This allows us to run the application on any .NET Core runtime.
To run your app, simply run this command:
BASHdotnet build && dotnet run
This will build the app and run it. You should see the following output:
BASHServer started on http://0.0.0.0:8080
Now simply visit http://localhost:8080
in your browser and see the server's response to you. You should see Hello from C#
.
Our .NET web server running on port 8080.
Docker Overview
Before we continue, let's run through a quick overview of how Docker works. As said before, Docker is a containerization technology that makes it easy to create, deploy, and run applications inside containers.
A Docker container is a lightweight, portable, and isolated unit of software. It is a single process that runs on a single computer. It is designed to run a specific application, such as a web server, or a database.
A Docker image is a blueprint for a Docker container. It is a collection of files and commands that can be used to create a Docker container, and it is stored inside of a file called Dockerfile
. The instructions in this file are used to create an image.
Docker overview
Docker Compose
Now that we have an app to deploy and the basic terms for Docker defined, let's deploy it on a Docker container. There are several ways to do this, but the easiest way is to use the Docker Compose tool. It comes with Docker, so you should already have access to it.
Create a file named docker-compose.yml
in the root directory of your folder.
YMLversion: "3.9"
services:
app:
container_name: app
image: app
build:
context: .
dockerfile: Dockerfile
target: runtime
restart: always
ports:
- "8080:8080"
Let's breakdown what's going on here. This file tells Docker to create a service called app
that runs the image app
with container name app
and maps the port 8080
on the host machine to port 8080
to the Docker container. The actual instructions for building the image are in the Dockerfile
file.
Dockerfile
Create a file named Dockerfile
in the root directory of your folder.
DOCKERFILEFROM mcr.microsoft.com/dotnet/sdk:6.0-alpine AS build
WORKDIR /app
COPY ./*.csproj /app/
RUN dotnet restore
FROM build as publish
WORKDIR /app
COPY . .
RUN dotnet publish ./App.csproj -c Release -o ./app/bin/Release/net
FROM mcr.microsoft.com/dotnet/aspnet:6.0-alpine AS runtime
WORKDIR /app
COPY --from=publish /app/app/bin/Release/net ./app/bin/Release/net
ENTRYPOINT ["dotnet", "./app/bin/Release/net/App.dll"]
We are using a multi-stage Docker build for our app. This isn't necessary but it makes a more efficient Docker image in the end. The first stage is a build stage that is used to build the application. The second stage is a publish stage that is used to copy the application to the Docker container. The third stage is a runtime stage that is used to run the application.
Let's break it down line by line:
DOCKERFILEFROM mcr.microsoft.com/dotnet/sdk:6.0-alpine AS build
This tells Docker to get the latest version of the sdk:6.0-alpine
Docker image by mcr.microsoft.com/dotnet
and name this image build
.
DOCKERFILEWORKDIR /app
This tells Docker to switch to and run the app in the /app
directory.
DOCKERFILECOPY ./*.csproj /app/
This tells Docker to copy our App.csproj
file to the root of the container's working directory.
DOCKERFILERUN dotnet restore
This runs the dotnet restore
command which will download the NuGet packages for our project.
DOCKERFILEFROM build as publish
This starts a new stage called publish
. This stage is used to publish the application.
DOCKERFILEWORKDIR /app
This tells Docker to switch to and run the app in the /app
directory.
DOCKERFILECOPY . .
This tells Docker to copy the entire contents of the /app
directory to the root of the container's working directory.
DOCKERFILERUN dotnet publish ./App.csproj -c Release -o ./app/bin/Release/net
This runs the dotnet publish
command which will build the application in release
mode. We are using the -o
flag to tell the command to output the application to the ./app/bin/Release/net
directory.
DOCKERFILEFROM mcr.microsoft.com/dotnet/aspnet:6.0-alpine AS runtime
This tells Docker to get the latest version of the aspnet:6.0-alpine
Docker image by mcr.microsoft.com/dotnet
and name this image runtime
.
DOCKERFILEWORKDIR /app
This tells Docker to switch to and run the app in the /app
directory.
DOCKERFILECOPY --from=publish /app/app/bin/Release/net ./app/bin/Release/net
This tells Docker to copy the application from the publish
stage to the runtime
stage so that it can be run.
DOCKERFILEENTRYPOINT ["dotnet", "./app/bin/Release/net/App.dll"]
Finally, this tells Docker to run the application using our compiled App.dll
file.
Running the app
Finally we are ready to deploy the app on a Docker container. To do so, use Docker Compose.
BASHdocker compose up --build
On the first build, you don't need the build
flag, but you will in subsequent runs. If successful, you should see something like this:
The Docker Compose output.
The app is now built and accessible at http://localhost:7000/
just like before, however now the app is running in Docker.
Our .NET web server running on port 8080.
You can confirm the container is running in Docker, along with all of the other containers that are running by using the docker ps
command.
BASHdocker ps
BASHCONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES
ba804e8bff5f app "docker-entrypoint.s…" 5 seconds ago Up 5 seconds 0.0.0.0:8080->8080/tcp app
You can stop the running container by then running docker compose down
.
Deploying to Production
Now that your .NET app has been containerized successfully, it is ready to be deployed to whatever environment you want. This tutorial showed how to deploy it on your own machine, however, this Docker container can be deployed on a staging environment, remain local, or be deployed to a production environment.
To deploy it to production, you will need some kind of server or hosting platform to host the app. There are several different options for hosting, each with their pros and cons. In addition, if your app is a web app, it will likely need a domain as well. Either way, containerizing your app using Docker is becoming the industry standard and it will certainly help you get your app accessible to your users.
Conclusion
Docker is a powerful tool for a developer to learn and use. As you saw, with just a few lines in your Dockerfile, you can set up a full-blown .NET app. This makes deploying the app anywhere you want a breeze. I hope this tutorial has helped you get a better understanding of Docker and Docker Compose. Happy deploying!
Docker's hot for a reason though!
Resources
- How to Install Node on Windows, macOS and Linux
- Getting Started with Solid
- Managing PHP Dependencies with Composer
- How to Set Up Cron Jobs in Linux
- Best Visual Studio Code Extensions for 2022
- How to deploy a PHP app using Docker
- How to deploy a Deno app using Docker
- How to deploy an Express app using Docker
- How to deploy a Node app using Docker
- Using Puppeteer and Jest for End-to-End Testing
- Build a Real-Time Chat App with Node, Express, and Socket.io
- Getting Started with Moment.js