Buckle up, friends. This is going to be a long post.
Here’s the short version: SteveCraft is running on an Ubuntu VPS through Ionos and built on an nginx, MySQL, and WordPress stack with certbot managing the LetsEncrypt certificate lifecycle. The stack was set up with Docker Compose following this guide. It links to self-hosted services running on a desktop in my home with routing and DNS managed by a Ubiquiti UDM Pro.
Still here? Okay, here we go.
Originally I wanted to host a few services for friends to access. A Plex server for watching movies with friends over Discord, Foundry VTT for playing Dungeons and Dragons, and my Grafana dashboards that I wanted to make public (well, some of them) to show off my home infra because charts and graphs are cool. I installed nginx on Behemoth, my trusty former desktop running a Core i7 6700k with 32gb of RAM and four terabytes of storage. As a dedicated server for hosting games and things, it’s more than capable and the more affordable option compared to renting the same amount of resources in the cloud.
I set up DNS in my Ionos web portal, then used nginx to act as a reverse proxy so my friends could go to <servicename>.stevecraft.org and get to the appropriate application. Stevecraft.org mapped to my home IP address and nginx listened on port 80, then proxied the connection to the appropriate service port as defined in my conf file. My UDM Pro had port forwarding configured for all the services. I tested it out, everything worked great, then I set up a very basic landing page with links to the available published services with their native ports. Knowing my external IP would change because of my ISP plan, I learned to use the Ionos DNS API and configured the UDM Pro to update the stevecraft.org DNS entry whenever the external IP changed. Native dynamic DNS is a great feature of the UDM Pro and it was going to save me a lot of maintenance down the road. I was on my way to self-hosted infra for fun and learning!
Now comes the first problem
As I was ready to tell my friends things were ready to go, I decided to do one last test. I disabled wifi on my phone and went to stevecraft.org:
Page cannot be displayed 🙁my phone when not on wifi
Like most internet service providers, to prevent exactly the kind of thing I was doing, ports 80, 443, and a number of others are blocked for “security reasons.” The only way to get them unblocked was to switch to a business plan at a cost of an additional $150 per month. Yikes, no thank you unnamed ISP. With some more testing, I found that the native application/service ports for the things I was hosting weren’t blocked, but I didn’t want to have to tell less technically inclined friends to specify the port at the end of the URL and ignore that pesky “your connection is insecure” warning.
Enter the VPS
Ionos had my back with their VPS plans. I could get a dedicated virtual private server running Ubuntu (oh yeah, I’m a recent Linux convert, more on that in later posts) and use that as my frontend. I ordered their cheapest and lowest powered VPS for a whopping $2 per month, installed nginx, and copied over my conf and index.html files. From ordering the VPS to everything running as I expected took under an hour. For extra fun and security, I set up key-based auth for SSH and changed the default port it ran under. It took me longer than it should have, but I’m still learning.
But Steve, why wouldn’t you just run nginx in AWS or Azure as an app service?you right now, probably
Well, because I have Plans®.
At this point, everything worked the way I wanted, but the insecure connection warning wasn’t something I was willing to deal with. I did some light Googling and found how to set up certbot, the automated method to request, assign, and renew SSL certificates. I had it up and running in about another hour, and felt pretty content with myself. For a few days.
Still not good enough
Recently I’ve been learning to use Docker Desktop. It’s pretty fun, you run a command or two and the program/service you want to use is up and running by itself in an isolated little bubble. New version comes out? Run another command, the program/service nukes itself and rebuilds on the new version with the same config as before in a fraction of the time as updating manually. Awesome! And the resource usage is lower too so I can run more stuff at once! At this point, my friends were telling me about Docker Compose. I would describe it as a template you use to set up multiple Docker containers in a single configuration, but there are probably better descriptions. I’ve only been using Docker for a few weeks, and only when I have spare time.
My first experiments with Docker Compose didn’t go well and I’m going to blame Windows for that. Docker Desktop is a Windows program that lets you manage a Linux program inside WSL, the Windows Subsystem for Linux. There were a lot of extra steps over natively running in Linux because of the Windows layer, and basic troubleshooting took more effort than I liked. It’s a program in a container in a Linux app in a Linux VM inside Windows, managed by a Windows program. Again, yikes. The extra layers made it tough to figure out why volumes weren’t persisting, and binding to an existing location was nigh impossible (in Windows, it works great when it’s native to Linux).
About the time that I started to understand how to use Docker, I decided I wanted to set up this site. My VPS was already running nginx so adding WordPress wouldn’t be a big deal, but I wanted to do it the correct, modern, and fun way. Docker Compose! Docker provides a document on running WordPress with Docker Compose, but like many things, it wasn’t good enough for me. The next link in my search took me here: https://www.digitalocean.com/community/tutorials/how-to-install-wordpress-with-docker-compose
This guide is EXCELLENT. It accounted for all the things I wanted to do. Docker Compose for nginx and WordPress and certbot with autorenewal! This is exactly what I was looking for!
However, as is tradition, the defaults weren’t good enough for me. I learned the hard way that if you have certain characters in your .env file, they have to be escaped or your MySQL passwords get set to who knows what and you get “unable to connect to the database” in WordPress, forcing you to dump and rebuild the MySQL service. But I learned a lot through my mistakes.
Another thing I learned is that if your cronjob executes on an interval that’s shorter than it takes to finish running, you’ll have Problems®. It took me all morning to figure that one out. Also, don’t rush to set up SSL in nginx with certbot until after you’ve got it running on HTTP/port 80, because that guide will work exactly as intended and forward all requests over to port 443 which hasn’t been configured yet, causing the certificate request to fail. If your DNS provider has a supported certbot DNS authorization plugin you can probably work around that issue, but Ionos is not on that list so I wasn’t able to experiment with it.
For now, that’s all I have to share. Stay tuned for adventures in trying to set up OpenSSH on Windows, my forthcoming efforts to learn kubernetes, and how those are related to why I’m dumping Windows on Behemoth to move over to Linux. It’s already happened on my primary machine, and I use that to run games.