Dockerize ASP.Net Core Web API and React Web Application

Fábio de Paula Silva
9 min readJul 7, 2024

--

Simplest way to dockerize your back-end and your front-end in order to be execute the both ones in the same container.

I could say that in the past when every time someone talked about Docker Container, I was a bit afraid to study and learning, because of many reasons, but one of the biggest reason I would say that was the unknowledge about something new. Therefore, when you learn how powerful and helpful can be the use of Docker in nowadays you probably will change your mind discovering about the advantages and facilities and how it can make your live easier.

Instead of spending a lot of time configuring, deploying and customizing your application so that it can run in a specific environment, all you need is to create, install and run all the dependencies and configurations needed to launch your application very easily. Summarizing, you don’t have to worry about this difficult part besides developing yourself.

Highlighting some advantages of using Docker:

1. Consistency Across Environments

  • Isolation: Docker ensures that the application runs in the same environment, regardless of where it is deployed. This eliminates “it works on my machine” problems.
  • Environment Replication: Easily replicate development, testing, and production environments.

2. Simplified Deployment

  • Single Image: Package your application and all its dependencies into a single Docker image, making it easy to deploy consistently.
  • CI/CD Integration: Seamless integration with CI/CD pipelines for automated testing, building, and deployment.

3. Scalability

  • Microservices: Docker is well-suited for a microservices architecture, allowing you to scale different parts of your application independently.
  • Load Balancing: Easily deploy multiple instances of your API and use load balancing for better performance and reliability.

4. Resource Efficiency

  • Lightweight: Docker containers are more lightweight compared to virtual machines, as they share the host OS kernel and use fewer resources.
  • Portability: Containers can run on any system that supports Docker, making it easier to move applications across different environments.

5. Isolation and Security

  • Process Isolation: Containers provide process isolation, ensuring that applications do not interfere with each other.
  • Security: Containers can be configured with specific security policies, reducing the attack surface.

6. Dependency Management

  • Dependency Bundling: All necessary dependencies are bundled within the container, ensuring that the application always has everything it needs to run.
  • Version Control: Manage different versions of your application and its dependencies more effectively.

7. Development Productivity

  • Quick Setup: Developers can quickly set up their development environment using Docker, reducing the time needed for onboarding.
  • Consistent Development Environment: Ensures that all developers are working in the same environment, reducing environment-specific bugs.

8. Cost-Effective Testing

  • Automated Testing: Easily automate testing environments that mimic production, allowing for more reliable tests.
  • Ephemeral Environments: Quickly spin up and tear down testing environments as needed, reducing costs.

9. Rollback and Recovery

  • Versioned Images: Easily roll back to previous versions of the application by using versioned Docker images.
  • Disaster Recovery: Quickly recover from failures by redeploying stable container images.

10. Cross-Platform Development

  • Multi-Platform Support: Docker supports multiple platforms, allowing you to develop and test your ASP.NET Core applications on different OSes.

As now we know the advantages of using Docker. Let’s see how to use!

PS.: Let’s install the Docker Desktop from this link: Docker Desktop: The #1 Containerization Tool for Developers | Docker

First of all, when you are creating the Asp.Net Core Web API, lets check the option to Enable Container Support like the picture below. Notice that we are also keeping the Container OS with Linux selected.

Creating a new Asp.Net Core Web API with Docker support.

Using Linux as the operating system for a Docker container running an ASP.NET Core Web API offers several advantages, such as:

- Linux containers are generally more lightweight and efficient compared to Windows containers. They require fewer resources and can start up faster, which can lead to better performance and lower hosting costs.

So, right after the project has been created, a Dockerfile will be added automatically in the root of your project.

Dockerfile added.

Let’s open the file “Dockerfile” to identify what commands and/or configurations were added by default when the file was added into our project.

Then, we can notice that we have basically the configurations included into our container that belongs to our current project “Card.Api”. Sometimes is necessary to change the default configuration a little bit in order to set the correct path or even to add other projects, that’s indeed our case and we need to implement the changes to add the dependents projects into the same container.

# 3 projects need to be added into the docker.

So, besides the Card.API project we also have in this sample, 2 more projects (Card.Domain and Card.Infra) that we also want to add into the Docker. For to do that, let’s re-open the Dockerfile to add the setup needed as you can see in the lines highlighted. Observe that we are exposing the ports 80 and 443, where we are indicating that our api will be running over the port 80 for the protocol http and 443 for https.

So, as we have the dockerfile ready, now we need to add Container Orchestrator Support to manage what we want to be installed during the deployment of our project. Clicking over the API project and selecting the option to Add, like this image below:

Right after has been added this feature, you will see the docker-compose project and docker-compose.yml file added in the Visual Studio Solution Explorer:

Well, everything seems to be in place, then let’s take a look at the docker-compose.yml file to analyze what we want to up during the deployment of our project. But, before that let’s learn about which kind of configurations we can have in our docker compose:

Services: These are the distinct containers that form your application. Each service is based on a specific Docker image and can have unique configurations, environment variables, ports, and other settings. As the objective of this article is to teach you how to up back-end and front-end at the same container, we’ll have 2 services defined.

Networks: Custom networks can be defined to connect services, allowing controlled communication between containers.

Volumes: Volumes can be created to offer persistent storage for containers. They enable data to be stored outside of the container and shared across multiple services.

Environment Variables: These can be set for services to pass configuration information to the containers, which is particularly useful. So, instead of to use AppSettings.json to store your sensitive data, let’s use docker-compose to store the environments variables that we are using in our project.
Tips: reading the environment variable in .NET:
Ex.: “Environment.GetEnvironmentVariable(“Jwt_Issuer”)”

Dependencies: The relationships and dependencies between services can be defined. For example, a web service might rely on a database service. Specifically in this project we are using SQLite in memory as database, but in case you are using a normal SQL Server database we could set the database as a service dependency.

Build Context: For building custom images for your services, you can specify the build context and Dockerfile path. When you open the docker-compose.yml file.

Back-end: card.api

Okay, so in our docker-compose.yml file we have the structure created. Let’s start from “Services” definition: Following the hierarchy you can see the back-end and the front-end project defined as Services, “card.api” and “card.front”. Observe that under the card.api service we also have the image name that we want to use when the docker is running, so the prefix ${DOCKER-REGISTRY-} + the service name cardapi should be like this. After that we also have the ports where we defined 5000:80 for the back-end, we are will run our API and right after we are defining the Environments variables to store the variables used over the API, such as: login, password, jwt_key and etc… The entry point where we should define the command to start the api. The last step is the build/context where we should define the Dockerfile path to be executed.

Front-end: card.front

Then… We have a similar configuration to the front-end side, but with less settings to be done. But, as we are planning to have 2 services by running over the same Docker Container, firstly we need to go to the front-end project in order to Add a Dockerfile as well, so our docker-compose.yml will be able to read and understand our intention.

Dockerfile

Ps.: You can create this file manually, just adding a new file without any extension, exactly like the picture above.

Let’s open the file to include the settings needed to execute our front-end project correctly.

Step by step: we are using the first line to download an image of the Node as we are executing a React project. So let’s use node:16-alpine or another one if you prefer. The WORKDIR by default we’re gonna use /app, the COPY command is essential to copy the packge.json and yarn.lock file to the image we’re creating for the front-end. RUN “yarn install” will install all the dependencies of the project, COPY to copy the code to the image, EXPOSE to use the port 3000 the one we defined to run our Front and the last but not the least, CMD is the command to start the front-end project.

Now as we also have the Dockerfile configured in our Front-end project, let’s gonna back to the docker-compose.yml file and easily we can identifying and understand what we are missing there. At this point we need to set the service name, that is “card.front”, build context that contains: front-end path (../../FRONT) and Dockerfile, port (3000) and environment, that in our case we’re gonna using development.

Guys, if you did everything like it was explained in this article, make sure that you also have the Docker Desktop running in the background and let’s start our docker project. Your project should be like this picture below, with the option to start the project by using Docker Compose:

Starting the project:

If everything has been configured as needed, when running the app you’ll see your visual studio like this next picture. Observe that should appear the containers running in the left side and the logs registered during of raising environments:

Now let’s check our Docker Desktop in order to see if the containers and the images have been built and raised as expected.

Containers — Docker Desktop: The Docker compose and the main container are running correctly as expected. Also the port we’ve exposed in the “docker-compose.yml” and the protocol http are configured — 5000:80.

Images — Docker Desktop: Opening the Images section we can see the service images we’ve configured in the docker-compose.yml (“cardapi” and “cardfront”).

Finally let’s check if the projects are working properly, so let’s open the Swagger by running these links: http://localhost:5000/swagger/index.html and http://localhost:3000 .

Back-end API:

Front-end — React project:

That’s all guys! Thank you in advance for the reading and I hope that it can be useful somehow. See you in the next article!

--

--

Fábio de Paula Silva

Software Engineer at @Riverty - Inspiring someone to be better doing what you love to do most.