As an IndieHoster, I offer managed personal server hosting for around 30 people, on (currently) 3 virtual servers, which I rent from Vultr. I don't develop any applications or control panels for them, but I do build up some collection of reusable parts each time I install software on servers for these people.
The reason I write software at all is that most personal server applications are not designed to run on as the main application on a server, they are designed to run on top of a webserver. A webserver like for instance Apache is in itself designed to be easily configurable for a number of situations, including shared hosting: the situation where you host a number of websites on one linux server. So in its simplest form, the collection of reusable parts I build up would be a webserver configuration file, which configures Apache so that it runs the right personal server applications for each user. I need to automate the process of configuring this webserver.
In practice, this webserver is a node application, which does the SNI offloading (the process that allows multiple https websites to live on one IP address), and that runs each user's personal server inside a Docker container. Through a contraction of SNI in front of Docker containers, I call it Snickers.
Snickers is designed to stay up, even when stuff changes. It loads its config on-the-fly, so if I add a new site to a running server, I don't have to interrupt its execution. But changes to the core code of Snickers itself do require interrupting it, of course.
Whenever I make such changes, even when I tested them first, I need to have a good way to stop the running instance of Snickers, start the updated one, and switch back quickly if necessary. The way I do this is with blue-green deployment. I actually called my two alternating slices "the blue team" and and "the red team", since with blue-green, I keep having the association that green is always the color of the live one.
Whenever I have an updated version of Snicker to deploy, I do a "git pull" in the folder from which it's not currently running, and kill the running instance. If anything unexpected happens, I kill the new instance, resulting in instant rollback. If everything is OK, I do a "git pull" in the other folder as well, and then the deploy is complete.
Simple yet effective! :)