TubeSum ← Transcribe a video

Build a Complete .NET 10 Dev Environment with Docker Compose

0h 15m video Transcribed Jun 10, 2026 Watch on YouTube ↗
Intermediate 8 min read For: .NET developers familiar with basic Docker concepts who want to set up a local development environment with multiple services.
9.4K
Views
477
Likes
25
Comments
2
Dislikes
5.4%
🔥 High Engagement

AI Summary

This video demonstrates how to set up a .NET 10 development environment using Docker Compose, covering everything from creating a Dockerfile to orchestrating multiple services like PostgreSQL and Redis. The tutorial walks through scaffolding a Docker Compose file in Visual Studio, configuring services, environment variables, volumes, health checks, and networks, and finally testing the setup with a sample API.

[00:48]
Getting Started with Docker Compose in Visual Studio

Right-click the API project, select Add > Container Orchestrator Support, choose Linux as the OS, and select Alpine for a lightweight image.

[02:49]
Docker Compose File Structure

The scaffolded docker-compose.yml defines services, with the API service using a local image and build context pointing to the Dockerfile.

[03:22]
Port Mapping: Internal vs External

Internal container ports (e.g., 8080, 8081) are mapped to localhost ports (e.g., 5000, 5001) to access the API from the host.

[03:48]
Adding a PostgreSQL Service

Define a service named 'product-database' with image 'postgres:18-alpine', set environment variables (database, user, password) via a .env file, and map a volume for data persistence.

[06:49]
Health Checks and Dependencies

Add a health check to the PostgreSQL container and configure the API to depend on it with condition 'service_healthy' to prevent startup race conditions.

[07:34]
Connection Strings and Service Discovery

Use service names (e.g., 'product-database') as hostnames in connection strings; Docker Compose resolves them via its internal network.

[11:39]
Adding a Redis Cache Service

Define a 'product-cache' service with image 'redis:8-alpine', expose port 6379, set a password via command and environment variable, and add it to the network.

[13:24]
Integrating Redis in .NET API

Install 'Microsoft.Extensions.Caching.StackExchangeRedis' package, configure the cache with the connection string from Docker Compose, and use IDistributedCache in the GET endpoint.

Docker Compose simplifies local development by managing multiple services, networks, and dependencies. With the basics covered—port mapping, environment variables, health checks, and service discovery—you can easily extend your setup with additional services like Redis.

Clickbait Check

95% Legit

"The title accurately promises a complete .NET dev environment with Docker Compose, and the video delivers exactly that."

Mentioned in this Video

Tutorial Checklist

1 00:48 Right-click API project in Visual Studio, select Add > Container Orchestrator Support, choose Linux and Alpine image.
2 03:22 Map internal container ports (8080, 8081) to localhost ports (e.g., 5000, 5001) in docker-compose.yml.
3 03:48 Add PostgreSQL service with image 'postgres:18-alpine', set environment variables via .env file, and map a volume for data persistence.
4 06:49 Add health check to PostgreSQL container and configure API dependency with condition 'service_healthy'.
5 07:34 Set connection string in API environment variables using service name as host (e.g., 'product-database').
6 11:39 Add Redis service with image 'redis:8-alpine', expose port 6379, set password via command and environment variable.
7 13:24 Install 'Microsoft.Extensions.Caching.StackExchangeRedis' NuGet package and configure IDistributedCache in the API.

Study Flashcards (10)

What is the purpose of the Dockerfile in a .NET project?

easy Click to reveal answer

It defines how to build the .NET application into a Docker image, including base image, build steps, and exposed ports.

02:02

How do you map internal container ports to localhost ports in Docker Compose?

easy Click to reveal answer

Use the 'ports' section in the service definition: '8080:5000' maps internal port 8080 to localhost port 5000.

03:22

What is the default container registry for Docker images?

easy Click to reveal answer

Docker Hub.

03:48

How do you pass environment variables to a Docker Compose service without hardcoding?

medium Click to reveal answer

Use a .env file with key-value pairs and reference them in the YAML with ${VARIABLE_NAME}.

05:21

What is the purpose of a volume in Docker Compose?

medium Click to reveal answer

To persist data generated by a container, so data is not lost when the container restarts.

06:09

How can you ensure your API waits for the database to be ready before starting?

hard Click to reveal answer

Add a health check to the database service and set the API's 'depends_on' condition to 'service_healthy'.

06:49

What hostname should you use in a connection string to connect to another service in Docker Compose?

medium Click to reveal answer

The service name defined in the docker-compose.yml (e.g., 'product-database').

08:06

What is the default network driver used by Docker Compose?

easy Click to reveal answer

Bridge.

09:29

How do you set a Redis password in Docker Compose?

hard Click to reveal answer

Use the 'command' option: 'redis-server --requirepass ${REDIS_PASSWORD}' and define the variable in the .env file.

12:18

Which NuGet package is used to integrate Redis caching in a .NET application?

medium Click to reveal answer

Microsoft.Extensions.Caching.StackExchangeRedis.

13:24

💡 Key Takeaways

💡

Health Check Prevents Startup Crashes

A simple health check solves the common problem of the API starting before the database is ready, preventing crashes.

06:49

Service Discovery Magic

Using service names instead of IP addresses for inter-container communication simplifies configuration and is a key Docker Compose feature.

08:06

Caching Works!

The demo shows the API hitting a breakpoint only on the first request, proving Redis caching is functioning correctly.

14:39

✂️ Creator Tools: Viral Hooks

AI-generated clip ideas for Shorts based on the transcript

No viral clips found for this video, or they are still being generated.

[00:00] In this video, I'm going to show you everything you need to know about using Docker Compose in your .NET applications. We're going to code this literally from scratch, starting from an empty Docker Compose file, adding our first service, we're going to talk about images,

[00:16] containers, networks, environment variables, and everything else you need to know. Let me first walk you through our sample application that has one external dependency, for now, and that is a Pose with database and it's just a .NET 10 API exposing a couple of endpoints through minimal

[00:32] APIs and here you can see a couple of crud endpoints for creating, reading, updating, and deleting products from our database. Now what we are missing is the database instance and I'm going to show you how we can easily manage this on our local machines using Docker Compose. So my

[00:48] recommended way of getting started with Docker Compose is through Visual Studio where you can right click your API project, then you're going to go to add and you're looking for container

[01:00] compose support. You may also want to explore a modern variant which is using Aspire as an orchestrator. However, using Docker Compose is a more loadable solution, so I think it's critically important to know this in order to know how to work with Docker images, containers,

[01:15] and local orchestration using Docker Compose. So from this tenure here, we first get to pick which operating system we want to use and I'll be using Linux as it's more lightweight and hosting options for this are a lot more affordable and we can choose our container build type. We can say

[01:31] we want to have a Docker file and here we're going to configure how we're going to build our API into a Docker image. You can think of this as a template that allows us to initialize a container which is a running instance of our image. The next thing is choosing a default image distribution and

[01:49] And right now I want to use Alpine. This will give us a lower memory footprint and our final image sizes will be smaller. So once I'm happy with this, I'm going to click OK. And this is now going to scaffold a couple of files into our solution.

[02:02] And let me create a solution folder that I'll call Source. We're going to move our API in there. And then let me show you what we scaffold it. First, inside of the product API, we now have a Docker file, which is what we use to define our Docker image.

[02:15] I always recommend learning about the basics of how Docker images are defined, but for our baseline understanding, you should know that we are starting from a base image, and we said we wanted to use a more lightweight Alpine image. We still have to use the .NET SDK to actually build our project,

[02:32] which is this part here, but finally we're going to publish it and then run it using our ASP.NET core image. Another thing we configure here is which ports are going to be exposed by this image, and those are going to be 8080 and 8081 for HTTP and HTTPS respectively. So that's our Dockerfile.

[02:49] It's what we use to define our image and then run our application inside of our container. And the second thing, which is what we came here for, is the Docker Compose file. And we're going to start from this minimal file that defines our services. So this is the top level section. And

[03:04] then our first and only service for now is the VLX API. We're using a local image and defining the build context which is just pointing to our docker file. So then let's see how we can customize this. For example, I can define which ports I want to expose to my local system. And this is one of

[03:22] the first things you have to understand. Ports that we exposed inside of the image definitions are internal to the container itself. And when we map these ports, we are essentially mapping the

[03:34] internal container port which are 8080 and 8081 to local host ports of in this case 5000 and 5001 which is where you'll be able to reach your API instance. Now let's define our first service and

[03:48] I'm going to call this the strada database. So this is going to be our postcode sequence and we have to start by defining which image we want to use We can find docker images for almost any service you want to run on Docker Hub which is the default option There are also other container registries that you going to run into but Docker Hub is going to be the most common one

[04:10] And to run a Postgres database as container, we specify an image called Postgres. Additionally, we can specify a version, let's say 18, which is the latest one, and we can also use an Alpine image, so I can say Alpine 18, and this is going to run a more lightweight

[04:27] image of Postgres, although the correct name is 18 and then appending Alpine, but I think you get the jist. Then I can also provide a custom container name, if you want to call this locally with

[04:39] some other name, and we can also do the same for our API instance. Now this allows you to use uppercase, for example, if you want to. You can also just call this the API, but I'm going to revert to my service name because I find it simpler.

[04:53] Then, if you want to pass in some environment variables, we can specify the environment section in this yellow document. And then we can start setting things like the Postgres database, which is going to be initialized when we start the continuation.

[05:06] Then I can also say the default username and password, or let's say, Postgres and Postgres for the password. password the environment variable name is post this password and let's get a value but you also don't have to hard code these you can pass them in as

[05:21] environment variables and the simplest way is through an environment file so I'll say new item and I'm going to call this a .env and inside of here I can just find some key value pairs that are going to represent my environment variables so

[05:35] I can have a database variable a user and a password and how I reference these inside of my Docker Compose YAML file is through a dollar and then curly braces and then you specify the name of your environment variable. So now I can pass these as environment variables and I

[05:51] no longer have to hard code them. It's also a common practice to add this to your gitignore file so that it never enters your version control system. Then another thing you can do is in the case of a database or any other container that can store some data is map a volume. Now you can

[06:09] use a name volume which is going to be managed by Docker on your system or you can use a mapping to map this to a local file on your system. Let's say I mapped this to a folder called containers slash database and then I need to define my

[06:24] internal folder which is going to depend on the specific image but in case of Postgres 18 I should use varlib slash Postgres 12. This means that if my application restarts my data won't be lost because I'm storing it on my local

[06:37] file system. I can also expose the ports for Postgres. The default is 5.4.3.2. And then some other things you can do which are quite interesting is expose things like health checks. This is how

[06:49] I can expose a health check on my Postgres container. And what this allows me to do is to tell my API that it should wait for the Postgres container to become available. This is going to prevent a common set of problems where your API starts faster than your database. Maybe

[07:04] tries to perform some negations and then it crashes because the database isn't available, and adding a simple health check solves this problem. Now you need to tell your API service to depend on your product database. I'll say depend run and then specify the service name,

[07:20] which is this part here, and then we can specify these conditions. You have a couple of options to choose from, like service target, service healthy, or service completed successfully. I'll use service healthy and this requires you to have a health check defined on your dependent

[07:34] service. Another thing we can do is define our environment variables here and for example this can be your database connection string. Now you have to use connection string and then a double underscore to match the format inside of

[07:48] app settings JSON. So here we would say connection strings and then we would have database here with the respective value inside and if we want to match this inside of our Docker Compose file we going to say connection change double underscore and then database then the value for this is going to look

[08:06] something like this and notice that for the host value I'm using product database which is again the service name inside of our Docker Compose YAML file so I can depend on the service name and Docker knows how to resolve this to the correct address inside of the Docker network and this is another

[08:22] thing you need to wrap your head around. The containers running inside of Docker Compose are going to have their own internal network and they are allowed to reference each other, which is why we can use a service name here to kind of facilitate server discovery and not have

[08:36] to hard code the actual address. The port is going to be the internal port, not the one you mapped externally. So if you change the external port to 6432, your connection string doesn't change

[08:48] because it's connecting to the Postgres instance which is running within the same network in a container. The default database username and password those I can pass in as environment variables and finally we have a pretty decent setup inside of our Docker Compose file. Another

[09:03] thing you can configure for your containers is the restart policy. So let's say I always want the API to restart in case it runs into some problems. And then one more thing I mentioned is networks

[09:15] And this is also something that we can configure inside of our Dr.Compose file. So I can define a network section here. Let's just create a default network, which I'm going to call Bridge. For our network, we have to decide which driver we want to use,

[09:29] and the default one is Bridge. I recommend taking a look at the documentation to learn what are the other types of networks. For the most part, you won't really have to think about this. You can commit to this section and just fall back to the default option, which is Bridge, or you can define

[09:45] your network explicitly and then you can tell your services that they are part of a specific network by defining the network section under each service. So let's tell both of them that they are part of our default network and now I think we can run this and you'll notice that the startup

[10:02] project is now Docker Compose. This is another thing that is set up when we scaffolded the Docker Compose file and this gives us a very easy way to debug this through Visual Studio. So if I start this and we open up Docker Desktop. You can see we have two services up and running,

[10:17] our API instance exposed on the correct boards as well as our database instance. My API is configured to start the Swagger UI page when it loads and you can see this is available on localhost 5001

[10:30] and let's just test this out by trying to create a product. You can see we get a response back. let's fetch a product with the ID01. This also works. I should also be able to update my product,

[10:42] which says the price to 1, 2 and 3. And if I fetch this through the all product endpoint, you can see this is spatch nation. We get the updated product instance. And finally, we can also delete this. So just going through all the front endpoints, you can see this succeed.

[10:58] So, and just to make sure that this is deleted, let's try to fetch this. And as expected, we get 404 not filed. You can confirm all of this is working as expected by taking a look at your logs inside of your Docker container and you can see our select statement here, you can see our

[11:13] delete statement here, you can also find the update and somewhere over here should be our insert. So logging is working just fine. We can also go into the logs for our database and see if there's

[11:25] anything interesting there. This covers a lot of the baseline setup as well as some more advanced scenarios like help checks and properly defining dependencies and also even passing in connection strings depending on your other services. Now say we want to

[11:39] extend this by adding another service. So for example let's say we want to use Redis as a cache, I can now define a product cache service. For the image let's

[11:51] use something like redis8alpine Let say I want to always restart this in case of failures For the ports let expose the default port which is 6P79 I want to map the same value internally

[12:05] I also want this to be a part of my network. So we'll say network, and then we're going to say default. And then another thing you can do, which is a more advanced option, is pass in a command for your container instance

[12:18] when it's starting up. So I can pass in a command in Reddit server, require pass, and this is going to require any client application connecting to my Reddit server to pass in a valid password. And as you may suggest, we're going to pass in this value through an environment variable.

[12:33] So let's define this inside of our .eav file. So it can look something like this. We're going to pass this value to our container instance and it's going to require a password for any server trying to connect it. Now, much the same as the database, we can also define our dependency on the cache service.

[12:50] So let's say the API depends on the cache, but we can make the condition more loose and we can just say that we need the service to be started for our API to also begin running. Of course, I'm also going to pass in the connection string to an environment variable, so we'll say connection string and then cache.

[13:08] And then I can pass in the connection string value, which is going to point to the product's cache service, the internal port, which is 6379. and we're also going to pass in the password using the environment variable. So we defined our service but we have to actually use it inside of our API.

[13:24] So let me just give you a quick demo of that. I'm going to add a new package into my products API. Let me look for Microsoft Extensions Caching Stack Exchange Redis. I will install the latest version and then with this library installed,

[13:40] we can say builder services add stack exchange redis cache and this expects a connection string. So we're going to say options, and then in the options we're going to set the configuration value,

[13:53] and we'll get this by saying Builder configuration getConnectionString, and we're looking for a connection string called cache, which we are setting inside of our Docker Compose YAML file. And finally, let's use this inside of our GET endpoint for fetching a single product.

[14:09] So we're going to go from this implementation to this one. It uses an IDistributed cache which is going to connect to our Redis instance, and we've got a getAsync method which is going to implement the cache as I pattern.

[14:22] So let's start this, and when we open Docker Desktop, you can see that under our Compose stack we also have a Redis instance up and running, and to test out if this is working as expected, I'll create a product that has the ID of 2, and I'll try to fetch this product, and on the backend,

[14:39] I place a breakpoint inside of endpoint and also the factory function. So if I send this request, we're going to immediately hit the breakpoint. If I press continue, we'll execute the factory function and we'll get the response in the Swagger UI. And if I execute this again and press continue

[14:55] here, we will not hit the breakpoint because this value is cached inside of Redis and you can see we get the response served back to our Swagger UI client. From here, you can continue building on your Dr. Homo setup and I show you a lot of the basics like how to map ports, what are internal and

[15:12] external ports, how to set connection screen values, which host values you should be using, for example referencing the internal service names and relying on the composed service discovery to resolve this to the correct address, dependencies and health checks,

[15:25] networks, mapping volumes, passing in environment variables. So you should be ready to get started and of course if you want to grab the source code for this it's going to be available for free from the pinned comment right under this video. And if you want to see how you can take this further

[15:39] and even get rid of a Docker file to build your .NET application into an image, I highly recommend taking a look at this video next which shows you how you can use the .NET SDK to build a Docker image. If you enjoyed this video, consider gently tapping on the like button.

[15:55] Thanks a lot for watching and until next time, stay awesome!

⚡ Saved you 0h 15m reading this? Transcribe any YouTube video for free — no signup needed.