Containerize Your .NET 8 API Easily with Docker: A How-To Guide

Simple Steps to Containerize Your .NET 8 API with Docker

This article guides you through the initial steps of hosting a .NET 8 API on the internet by containerizing it with Docker. It covers the creation of a Dockerfile, building a Docker image, running a Docker container, and handling configuration variables. This is Part 1 of a series on .NET 8 deployment, with future parts focusing on deploying the API on AWS EC2, configuring dependencies, running the API, and setting up NGINX for HTTPS.

So you've built a .NET 8 API, and now you want to host it on the internet to be able to actually use it. This article will outline the first steps to take towards achieving that goal - containerization with Docker.

This is Part 1 of an ongoing series I'm writing, regarding .NET 8 deployment. The rest of the series can be accessed on my profile!

Prerequisites

To begin this article, there are some things you should have:

  • Some Docker knowledge

  • A working Docker installation

  • On Windows, you'll need WSL installed so you can use Docker commands. Otherwise, your terminal is fine!

  • A .NET 8 Web API.

Getting Started

Why are we using Docker?

If you don't know much about Docker, I'd turn to the guides as a resource. As a summary, Docker is great because it allows applications to run consistently across different environments by packaging them with all their dependencies in lightweight containers. I see a lot of people using VM's and manually adding dependencies for their app to run properly. It might work, but that is the quickest way to dependency hell. With Docker, we just containerize our app, and run it on the VM. Simple as that!


Packaging Our App with Docker

To begin our setup process, we need to get our app working with Docker. I'm assuming by now you have a working installation of Docker on your machine.

Creating a Docker Image

First, we need a Dockerfile, which is a text-based file (with no extension) that contains a script of instructions that Docker will use to create our container.

FROM mcr.microsoft.com/dotnet/aspnet:8.0 AS base
WORKDIR /app
EXPOSE 5184
ENV ASPNETCORE_URLS=http://+:5184

Let's walk through it. This section specifies the base image, and here we are specifically using the ASP.NET Core runtime image for .NET 8. Then, we specify that our final application will run in the /app directory and that our container will listen on the network port 5184 during runtime.

RUN adduser -u 5678 --disabled-password --gecos "" appuser && chown -R appuser /app
USER appuser

This section is a security best practice, and you can learn more about it here.

FROM mcr.microsoft.com/dotnet/sdk:8.0 AS build
WORKDIR /src
COPY ["<YourProjectPath>/<YourProjectName>.csproj", "<YourProjectPath>/"]
RUN dotnet restore "<YourProjectPath>/<YourProjectName>.csproj"
COPY . .
WORKDIR "/src/<YourProjectPath>"
RUN dotnet build "<YourProjectName>.csproj" -c Release -o /app/build

FROM build AS publish
RUN dotnet publish "<YourProjectName>.csproj" -c Release -o /app/publish /p:UseAppHost=false

FROM base AS final
WORKDIR /app
COPY --from=publish /app/publish .
ENTRYPOINT ["dotnet", "<YourProjectName>.dll"]

Finally we install all of the app dependencies. Then we build and publish the artifacts into the /app directory. Finally, we set the entry point to run the application. Make sure to replace the placeholders with your project names/paths!

Now let's test it out.

In the directory of your Dockerfile, run:

docker build -t your-image -f Dockerfile .

and then run:

docker images

You should see your image!

Creating a Docker Container

With our Docker image created, we need to test that our container actually works. We'll run:

docker run -d -p 5184:5184 --name container_name your_image

  • -d allows Docker to run in the background.

  • -p allows us to bind ports - use the exposed ports

  • Then we give container-name and supply our created image name.

Once you've done that, run:

docker logs <container name or ID>

This will let you know if the container started up successfully, or what errors caused it to fail.

Configuration Variables - Something to Consider

There is a good chance you are using a configuration provider in your API to provide config variables to your app:

i.e configuration.GetConnectionString("DefaultConnection")

This reads in values from a file, probably appsettings.json. The Docker container will need the values in appsettings.json, otherwise your app won't run. You can do this in a few ways:

  • Copy the appsettings.json into the Docker image via Dockerfile

  • Mount the appsettings.json as a volume when running the container

  • Use the -e flag when running the container to pass environment variables. Then use either colon (:) or double underscores (__) to represent JSON hierarchy. For instance, AppSettings: { Token: { "hi" } } becomes:

    • "AppSettings:Token=hi" or "AppSettings__Token=hi"

Personally, I would recommend you use environment variables, despite being a bit more complex and more verbose. For security, it's best to not store sensitive info in the image (in case it becomes compromised) and changing an environment variable on the CLI is more flexible and efficient. So, your run command would now look something like docker run -d -p 5184:5184 container_name your_image -e "AppSettings:Token=hi" ... "Tutorial:Example=bye" , for as many environment variables your app needs.


Conclusion

Congratulations! If everything is setup correctly, you should now have a .NET 8 API running in your Docker container. Make sure to check your Dockerfile into version control, so that it can be used by others or by yourself.

This is a great first step towards deploying your .NET 8 API. If you'd like to deploy your .NET API so it can actually be used over the internet, follow along with my series on .NET Deployment.


Next Steps

In part 2 of this series, we will:

  • Set up an AWS EC2 virtual machine instance

  • Configure all of our dependencies on the EC2 instance

  • Run the API on AWS EC2

  • Configure NGINX as a reverse proxy, and enable HTTPS