Welcome! In this post I will describe the work I've done on this custom blog site, written in React and Typescript.
The Blog Site
Welcome!
This is the culmination of a lot of tinkering, toying with different frameworks and styles, and really exploring the full stack of an application from self-hosting all the way to the site you see before you. I have added (almost) all of the features I wished to add to make this a site where I can largely post my thoughts on video games, their design, and what I like about them, but I plan on having plenty of technical escapades described here, especially about this site specifically. Let's get in to it.
The Hosting
This site is hosted on a single VPS from OVHCloud currently. It is a 2 intel vCPU with 4 Gigabytes of RAM which hosts quite a variety of things new and old, as you can see from this diagram of my current setup.
Let's break it down a bit.
Virtual Machine side
There is little outside of docker within this VM.
-
Fail2Ban - This is a service designed to trap and block repeated failures to log in via SSH. It is just an extra line of defense in to the server. I also largely disallow accounts from even logging in via SSH with a password, but the constant journal log spam (Journal is the main method of logging in the modern linux ecosystem) I was getting from bots just slamming my VPS trying to get in was getting annoying. You will find MOST decisions from here on out are related to the constant bots and their crawling of my services.
-
Datadog Vector - This is a service designed for observability pipelines which injects, transforms, and sends them on to "sinks", which can be places like Amazon S3 buckets, Elasticsearch, and etc. For my observability pipeline, I use the BetterStack service, but more on that later. This tool is lightweight and fantastic for transforming and dealing with a variety of different formats. It uses its own transformation language, which I have found quite simple to use, allowing me to track not only Docker metrics, but the VPS health as well.
Docker
The meat of my services are all under Docker. I highly prefer to utilize Docker for everything that I can, as, to go on a tangent, much of my initial learning in the I.T. World was spent wrangling virtual machine installations and setup. This was a painful experience, especially with any strange self-hosted things I would do: One of the worst offenders was when Ubuntu upgraded from 16.04 to 18.04 and it broke every single old gcc-reliant tool as they no longer packaged the right version! Horrific! I have heard of even older tales, such as being stuck way back in 12.04 to run ancient Perl scripts... Containerization is the clear and prudent solution to these problems, allowing me to encapsulate any "funny business" i may need to do in to the container itself which will run on ANY VM that I may desire. For now, let's get in to the structure of it.
-
Nginx Reverse Proxy - This is the one and only entrypoint in to my docker setup. I have all other routes blocked off and unreachable behind the internal docker networking for security purposes. Nginx will route all incoming non-HTTPS traffic to its more appropriate HTTPS endpoint and handles my TLS certificates. It is advisable to terminate your SSL at your load balancer, which I would do if this was a multi-server architecture, so instead I terminate it at the reverse proxy, as the applications beneath do not need to know about SSL at all I feel. I do not however use it for caching, as we will talk about in the NextJS section. All connections are logged with the supplementary...
-
GeoIP Service. - This is the ever popular GeoIP2 service. Docker once again lends itself very well to this, as within the Dockerfile I simply have it always pull to update the current GeoIP Tables. This service uses NGINX auth routes (sort of hacky way to do it to be honest) to route the request through it, which returns to NGINX, which logs to stdout for datadog vector. I largely do this to track the sources of all of the bots that constantly hit my services, and to see what sort of requests they are pushing.
-
NextJS App - This is the blog! Completely written in React and Typescript, with TailwindCSS as the css solution. This service is the core of the whole ordeal and is where 99% of my VPS traffic is routed. All requests run through NextJS Middleware (yes, I am aware of the bypass CVE and have rectified it) for authentication to certain endpoints within the application. By proxying it with the reverse proxy and disallowing access via port 3000 which it is hosted on, I assure that all traffic reaching my blog is first sent through the logging system. For caching, I allow NextJS to cache as much as it wishes. I have lots of free RAM (over 2 gigs still!) so allowing it to cache all pages so far has not got me in to trouble. I do manual revalidation whenever a post updates, but very little otherwise changes. For authentication, I went with a third party service after attempting to write my own JWT-based system. I felt that the work to make a truly secure system was beyond the scope of this project, and while I feel I should at some point work on my own home-grown auth, for now the third party system will work. This site also hosts my portfolio page (basically a quick CV) here if you were so curious.
The app itself has several features not already listed:
- Automatic Draft persistence
- Live updating preview system
- All text is markdown compatible
- pagination for blog posts
-
ZNC Bouncer - This is an old service that originally actually ran outside of docker. It uses nginx data streams instead of the typical routing system that nginx does for http requests to securely route TLS-encrypted traffic to and from and IRC channel for a bot and custom account I host.
-
PGAdmin - This is the only actual way to perform administrative actions to the postgres DB I host, which gives persistence to all of my other applications in the docker network. This blog post for example is hosted there. It is routed via HTTP to port 7000, still with those same SSL Certificates.
-
Postgres DB - This DB instance hosts a variety of information for all of my local applications. It however does not host my authentication usernames and passwords or any logging itself. That has been delegated to third party services.
Third Party Services
-
BetterStack - A serverless observability stack. I use this to track my other applications so why not this one as well. It has a very generous free plan for my level of things and has clean dashboards for metrics. A goal I want to work towards is to host my own sort of thing like this, with tracking not only for my services but for all file uploads to various object storage buckets and other hard disk space I have out there, but for now this service is fantastic for my needs and was my introduction to actually using Vector.
-
Supabase - This one I am not quite as happy with. Supabase is largely used as a serverless Postgresql solution, but I host my own database as I have plenty of room and bandwidth on the VPS to host it myself (at least, until the site gets too big...hopefully I have that problem someday). However, I have worked with Firebase extensively in the past and found that Supabase had a superior authentication system compared to Firebase. it was easier to work with, worked with fullstack applications by providing BOTH Client and Server implementations (unlike firebase, which is primarily a client-side ordeal) and just generally worked better with my workflow. However, with their free plan, if you do not utilize ANY resources for a week, they will shut your entire service off! Crazy! You have to go back in to the dashboard manually and re-enable it. Other solutions I tried, however, including Clerk (and even implementing my own Next Auth, as clerk is just Next Auth but hosted for you) were far too heavy for a simple blog site, more complex to implement, and were causing a variety of bugs and errors. For now, until I get my own Auth rolled, I think that Supabase is the best solution for me. It isnt intrusive, allows for both client and server auth (very important in a fullstack application), and is easy to manage from a dashboard side as well, allowing quite a strata of services to connect to it (I actually log in via discord! Crazy!)
That's mostly it! Insofar I have found my site can easily handle all of these various services easily and securely. There are many improvements I wish to make including
- Removing these third party dependencies, perhaps hosting a separate VPS for logging and metrics
- Rolling my own authentication solution using JWT. I have persistence locally, so It likely would not be beyond me, but I would want to do it right.
- Expanding beyond 1 VPS - I would like to turn the postgres database in to a cluster someday, with the ZNC bouncer and PGAdmin hosted on their own servers, and then use the load balancer features of nginx or learn and utilize kubernetes to run a primarily docker-based cluster for both the application and the database.
- Additional Features to the app. I have many plans for this site! The blog is only the beginning (and even it is not done, as I want to add a comments system utilizing recursive SQL queries), and I plan on implementing new things I learn here.
This project was a culmination of my I.T. experience combining with my newer development avenues to create something which is secure and stable. It includes observability, persistence, authentication, and so many other important concepts to web development. I learned a whole lot making it, and I hope you enjoyed the Frankenstein I've sort of constructed. If you would like to leave feedback, talk about services or my app, or any other reason you want to speak with me, please utilize the form at the bottom of the about page. I read all of them! You can also find the source code for my application here and my Nginx config and Dockerfiles here and here