[00:01] has been self-hosting my .NET applications. The solution I've landed on is publishing them as Docker containers, which I can run on a VPS of my choice. So, in this video, I wanted to show you how you can easily publish [00:15] your .NET apps as Docker containers, and how you can get them uploaded to a container registry. So, here's a sample .NET 10 application, and what you'll see is that it's very bare-bones. All I've got is a single API endpoint returning [00:29] the current time on the server in the UTC time zone. I'm also exposing an OpenAPI document and also a Swagger UI, but these are just add-ons. If we quickly run this and I send a get request to our API, which is available [00:45] on localhost 7256, and our endpoint is time, we're going to So, if you wanted to package this application, you can easily do that by creating a Docker image. How I used to [00:58] do this for many years is using a Dockerfile. Now, how you can add this is, for example, writing one yourself, or you can right-click your project and say add and then container support. From this menu, you can choose a couple of [01:12] options. Now, the key here is the container build type. You definitely want to run this on Linux as those VPSs are far more affordable, and .NET is cross-platform, so why not? Now, as I was saying about the build type, the [01:24] default approach is kind of scaffolding a Dockerfile. And if you go ahead and do that, this is what you're going to get. A Dockerfile that defines the base image, exposes the HTTP and HTTPS ports, and then there's some code to actually [01:37] build your project before publishing it here and finally running it using the .NET command. Now, this is a pretty set-it-and-forget-it approach. If you had multiple projects in the solution, you usually have one root project like [01:50] an API in this example, and all the project references would be listed here. Now, I'm going to completely delete this, and I want to demo the second approach using the .NET SDK. So, starting from around .NET 8, I believe, [02:03] we have the ability to build our .NET applications directly into container images using the .NET SDK. And what that looks like is you go ahead and say .NET publish. You can choose the operating system. Let's say I want to publish this [02:18] to Linux, and then I want to say that I want to publish this as a container by saying {dash}t and then publish container. So, if I go ahead and run this, it's going to build my solution. And this also works with the SLNX [02:31] solution format. And the end result is a Docker image that has the same name as your .NET project. So, if I now list out any Docker images that I have on my system, you can see the latest one at the top called Time API. So, now my [02:45] application is available as a Docker image, and we can run it by saying Docker run. We can specify which ports to expose. Let's say I want to map port to expose. Let's say I want to map port 3000 on the local machine to port 8080, [02:58] which is the default Docker port within the container for HTTP, that is. There's also 8081 for HTTPS. And then for the image, I want to say Time API. So, if I go ahead and run this, you can see that my application is now available. What I [03:13] actually should have done here is run this in detached mode. So, let me go is up and running, and I should be able to send a request to HTTP localhost 3000, and the endpoint is time, and you can see that we get back a response from [03:29] our API. If you got something like Docker Desktop, which I'm using here, the image that it's running, and the port that it's exposed on. I'm going to go ahead and stop it and also get rid of the container. So, you can see that [03:42] without having to do anything to my project, I'm able to publish it as a Docker container. The next thing I probably need is somewhere to host my Docker images. What you're looking for is a container registry. There are many [03:56] options out there, such as Docker Hub, Azure, and AWS also have container registries. And what I'm going to demo here is the GitHub container registry because it's free to get started. And I do believe, if I'm not mistaken, that [04:10] you have unlimited storage for public repositories, and you get about 500 megabytes of storage for your packages for private repositories. Now, you can view any of your packages in the packages tabs on your GitHub profile, [04:23] and you can see an example I have here. And the time service is what I actually I'll show you how you can connect this to a repository. Now, before we can get started, we're going to need an access token. So, go ahead and head over to [04:37] settings, scroll down to developer settings, and you want to look for personal access tokens. What we need is the classic version. So, I already have create one. So, you're going to say generate new token and then choose [04:50] classic. And I just want to comment on these codes that you should select here, the most important one being write packages, read packages, and then the repository scopes. These should be sufficient for our example. Once you [05:02] have that, you can use the access token to log in to the GitHub container registry. And how you do that is by saying Docker login, specify the saying Docker login, specify the registry URL, which is ghcr.io, [05:15] then you specify your username, and you can provide your password as an argument better approach would be using the standard input output. Now, once you log in to the GitHub container registry, you can go ahead and tag the image that you [05:30] slightly different approach where I'll publishing our .NET application. So, we're still publishing as a container Then I have a couple of parameters. I [05:43] which is going to translate to the image name, to be time service. And I'm using this to match the name of my repository on GitHub. I'm also specifying the private repository URL. This was one way how I figured out to set the source for [05:59] my Docker image, which points to my repository containing the time service code. And I also needed to set publish repository URL to true to have this if you go ahead and run this, we should now see a time service Docker image. So, [06:14] I can say Docker inspect and specify time service. And here we see the details for our Docker image. Now, as you can see in the labels here is my image source pointing to my repository, which is needed to connect our package [06:28] in the GitHub container registry to our repository. The next thing we want to do repository. The next thing we want to do is to tag our local Docker image. So, we're going to specify it as time service. And then I want to specify the [06:40] container registry, which is going to be ghcr.io. And then you want to use your GitHub username, your repository URL, and then you can specify the tag that you want. So, in my case, this is going to be [06:54] So, in my case, this is going to be ghcr.io/mnovak/timeservice, course, I messed up because this should be Docker tag, not .NET tag. So, this succeeds. And now, if I go ahead and list out any images that we have [07:10] tag that I just wanted to use, and it's pointing to the same image ID as our time service image. So, the next step, once we have tagged this, is to push it going to say Docker push. Then you just specify the full image tag. So, I'm [07:25] going to type it out again. So, time service latest, and then this is going to take this image and push it to the respective repository. And then you'll be able to see it inside of GitHub. So, if I open up my time service image, you [07:39] can see that it was pushed less than a minute ago. It's also connected to my repository, which you can see here. And now, if I wanted to run this on a VPS, I can say Docker pull, grab my image with this tag that I just used, and run it [07:52] inside of a container. Just to give you a glimpse of what this might look like, I'm going to say Docker image rm, and let's get rid of the time service image. It's no longer present locally. And let's also remove the tag that we pushed [08:06] to our container registry. Also, I can now say Docker run, and let's expose this on port 3000 locally. And I'm going to specify the image name inside of my container registry. Because we aren't able to find it locally, you can see [08:21] that this is now pulled from my container registry repository, and I'm able to run this on my system as any other Docker container. Now, if I want to just quickly run this in detached mode, we can also test it out by sending [08:35] mode, we can also test it out by sending a request to localhost 3000/time, can see that our application is working as expected. Now, I know a lot of this is very basic for some of you, but I'm trying to lead into some interesting [08:48] videos that I've got coming up that deal with self-hosting your .NET apps on a VPS, so stay tuned for that. And if you're looking to improve as a .NET developer, I've made this roadmap that covers what are all of the fundamental [09:00] skills that make up a great .NET developer. If you enjoyed this video, Subscribe to this channel for more videos like this one. Thanks a lot for watching, and until next time, stay awesome.