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 portsThen 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.
For a .NET 8 API, you should see something like this:
This means the dockerized app is running properly!
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 DockerfileMount the
appsettings.json
as a volume when running the containerUse 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