Free lesson · 7:40

What a Self-Hosted Production System Looks Like

One 2-core, 4 GB server runs the control plane, the .NET application, Postgres, and Redis for around $6 per month. This lesson tours the finished system and explains what each part does.

0.2 · What you'll build

Press play to watch this lesson.

Source code and course materials are included with enrollment.

Get the complete course

Lesson transcript

Taught by Milan Jovanović, from Module 0 of Pragmatic Self-Hosting: Foundations. Lightly edited from the captions.

The finished production system

In this lesson, I'm going to show you the application that we are going to deploy on our own infrastructure. And I'm going to explain the system that we are going to build to support all of this. And our system begins with a virtual private server that you can rent from a server provider like Hetzner.

We're going to run our entire system, including the control plane, the application, the database, and the cache on a 2-core and 4GB machine that costs around $6 per month to rent. Here's our deployed production application. It's a simple task management system built with .NET and vertical slice architecture and all of the best practices that you might expect.

It has a valid domain name and a valid HTTPS certificate provided by Let's Encrypt. We can sign into the system, see the actual data that's there. For example, here's a task to introduce you to the Pragmatic Self-Hosting course, which I'm going to move over to done as I'm confident that we're going to get through this lesson.

We're going to operate this application through a private control plane using Dokploy, which is a free and open source platform as a service that lets you manage your applications and services. And here you can see the running TaskFlow application with its 3 runtime containers, the API, the Postgres database, and the Redis cache.

Even though this says not secure, our control plane is actually inside of a virtual private network managed by Tailscale. So nobody else other than you is allowed to access it. And this is enforced by inbound firewall rules that completely close off public access to the server.

Automated deployments, backups, and monitoring

We're also going to implement automated deployments so that after the initial setup, you can just worry about building your application features. And every time you push a commit to the main branch, it gets automatically deployed to production. Of course, I'm going to show you how you can customize this if you want to have a staging environment before production.

More on this in the upcoming lessons. We're going to use Cloudflare R2 as our object storage for automated server and database backups so that we can always restore from a crash. And we are going to integrate an external monitoring tool that's going to monitor the health of our application and send us an immediate alert notification when it detects a potential outage.

The build order

So that was a quick walkthrough of our production system. And here's a high-level system and architecture diagram that you can always refer to, to gain a better understanding of how all of the components fit together. We're going to explain each part of the system before we build it. And our build process is roughly going to be starting from a VPS that we are going to rent from Hetzner, or you can use any other server provider.

It doesn't matter as they all offer the same core capabilities. We're going to ensure that only we can access the server through a virtual private network. And we're going to expose our application to the public through a reverse proxy that's also going to automatically take care of HTTPS for us. We'll make sure that our deployment process is fully automated so that we don't have to spend time on deployments.

And we'll make sure that we have offsite backups for our control plane and our database so that we can always recover from a crash.

Private access with a VPN

Now I want to take a closer look at each of these components in case you are not as familiar with them, starting from our virtual private network. This is how we are going to allow private access to our server so that we can manage it over SSH and so that we are the only ones that can access the Dokploy control plane where we can configure, deploy, or back up our applications.

We are going to completely disable public SSH or access to the Dokploy panel so that our system remains private and secure.

Dokploy as the control plane

You may have already heard me say Dokploy a couple of times, but what it is is a platform as a service. You're going to see that it gives you a very similar feeling like working with a cloud provider, for example, Azure or AWS, as it serves one simple user interface that allows us to configure our applications and manage our releases.

And this is how we are going to deploy and set up our .NET application and its supporting services.

The application stack

Then here's the application that we are going to deploy. It includes an API written in .NET, using the vertical slice architecture and all of the best practices that you come to expect. We're also going to package up a React client application into our .NET application and serve it using the static file middleware just to make our system that much simpler, and so that we don't need a separate deployment for the client application.

We're going to use Postgres as our database and Redis as an optional cache to improve query performance.

Traefik and HTTPS

Dokploy, our platform as a service, is also going to deploy a public entry point using Traefik, a popular reverse proxy. It's going to route incoming HTTP and HTTPS requests to the correct service that we have deployed on our own infrastructure. It's also going to take care of HTTPS certificates. And this means that our application can remain completely private inside of our own infrastructure, as only our reverse proxy is able to reach it and send a direct request to it.

CI/CD with GitHub Actions

We are also going to build an automated CI/CD pipeline. This is short for continuous integration and continuous delivery or continuous deployment. In this course, we are going to interpret CD as continuous deployment, as we are going to automatically deploy our changes to production using GitHub Actions, where we are going to build and package up our application as a Docker image that we are going to upload and host on the GitHub Container Registry.

Both of these services are free to use if you have a GitHub account.

Off-site backups

We also want to ensure that in case of a service outage, for example, our VPS crashing, we don't lose any critical data. And this is why we are going to introduce an offsite backup. This can be any S3-compatible object storage. And in this course, I will be using Cloudflare R2 as it's a very popular solution.

It's S3-compatible and it has a benefit that the network egress is free of charge. You're going to see how we will automate the backup and restore process in one of our upcoming modules.

External monitoring

We also need a way to monitor the health of our system. And for this, we have to use an external server monitor because if it's on the same VPS as our application and the VPS crashes, we won't get notified. So for this, we're going to integrate UptimeRobot. But I'm going to show you a free alternative that you can also self-host on a cheap VPS.

And that service can notify you as soon as there are some potential issues with your production system.

The path through the course

So here's the rough path that we are going to take to build our final system. We're going to start off by preparing and security hardening our VPS, preparing our SSH keys, and creating our virtual private network. Then we're going to deploy our control plane and our application and make sure that it serves over HTTPS.

And then we're going to integrate some of the operational requirements like a continuous integration and deployment pipeline, automated backups and rollbacks, and being able to verify our releases with confidence.

Where AI agents fit

Of course, I'm aware that AI and agents are a big part of software engineering and they are going to continue to be a core part of how we do our work as software engineers. For that reason, this course will give you all of the knowledge and skills that you might need to operate a self-hosted system manually.

I want you to have a deep understanding of how everything works and connects together. But I'm also going to give you an easy way to make all of these processes repeatable with some useful agent skills that are going to work with any AI agent that you might be using. And this is going to be a little bonus lesson that you will find at the end of this course.

And this brings us to the end of this lesson where I showed you what we are going to build throughout this course. And in the next lesson, we're going to do a quick review of the tools and services that you will need to complete this course. I'm also going to share the source code for the application that we will be deploying.