Binary Solo

How Chapter24 works under the hood

posted by Ayush Newatia on October 23rd, 2020
Chapter24 is a super simple to use blogging platform. I built it and am running myself without any collaborators. That meant I needed a tech stack and architecture that made it quick to build and easy to run as a solo endeavour.

Tech stack and high level architecture


At its core, Chapter24 is a Ruby on Rails monolith that runs on a single $5/month DigitalOcean box. In keeping with the Rails philosophy, I'm using server side HTML rendering with Turbolinks and Stimulus on the front end. I've got Sidekiq for running background jobs and my database is a DigitalOcean managed MySQL instance. To simplify deployments, I'm using Cloud66.

Where Chapter24 is unique is how the individual blogs are hosted. Every blog has its own Amazon S3 bucket where every page is stored as static HTML. A CloudFront distribution sits in front of every bucket to enhance performance and simplify DNS configuration. So in a way, the Rails app is a glorified static site generator.

High level architecture diagram


I chose this approach to decouple the hosting of every blog from the Rails app itself. Since I'm running the project on my own, I didn't want any potential downtime to affect the blogs themselves. So with this approach, if my server was to go down, the blogs would all still be available. Also, since the vast majority of traffic on blogs is "read" traffic; my server wouldn't have to deal with any of that meaning my Rails app needs very little resources.

I know there are plenty of "better" strategies to mitigate downtime; such as failover clusters. But for me, keeping the operations costs as low as possible was key as I had no idea if there was a market for the product.

Another advantage of using S3/CloudFront was that it made it very easy to manage SSL certificates for custom domains. When a user configures a custom domain, the Rails app will issue an SSL certificate from Amazon Certificate Manager and apply it to the blog's CloudFront distribution. No server configuration required!

Disadvantages of the static site approach


Hosting every blog statically on S3 came with its own set of pitfalls. Every time a user publishes a new post, the blog's index page needs to be regenerated and CloudFront needs to be invalidated. Worse than that, every time an existing post is updated; the index needs to be regenerated (in case the post preview on the index has changed) and again CloudFront needs to be invalidated.

All these updates are done in background jobs so the app still feels fast to the user. At large scale, this architecture would be completely untenable however at small scale, I think it works great!

If Chapter24 got to a scale where this architecture started causing problems, it wouldn't be very difficult to change to an approach where the blogs are hosted within the Rails app and cached using something like Redis or Memcached.

A potential future setup


How comments work


Comments was an interesting feature to build because it couldn't be hosted statically with the rest of the blog. The fact that it requires user interaction with the server meant that it had to be rendered dynamically.

When a blog post is loaded, the page makes an AJAX call to the Rails monolith to fetch the comments as an HTML partial and inserts it at the bottom of the page. If something goes wrong; or if the server is down, the call will silently fail and the comments section will be hidden.

A token generated with Rails' MessageVerifier API is included as a meta tag which is sent with the AJAX call; so the server knows which post's comments to return. The server also checks the request's origin and referer headers and enables CORS only for the relevant blog's domain to prevent fraudulent requests.

Comments are posted via an AJAX form submission. Using the same idea as the Rails UJS framework, I intercept HTML form submissions using JavaScript and make the request via AJAX.

Since my use case was super simple, I did all this manually using vanilla JavaScript rather than pull in a dependency. It resulted in a ~1kb gzipped JavaScript file so page loads are blazing fast. If you're interested in understanding the mechanics of this process, the source map is available so you can use your browser's developer tools to inspect application.js on this page and see how it handles comments.

Conclusion


I hope this post gave you a good overview of how Chapter24 works. Using Rails with server rendered HTML allowed me to build and iterate on this product very fast with minimal complexity. 

If you have any thoughts/questions/criticisms, let me know in the comments!

Subscribe to Binary Solo


We'll send you an email every time a new post is published. We'll never spam you or sell your email address to third parties. Check out our privacy policy for details.