
The Cheapest CMS Implementation in History?
This blog post describes what might be the cheapest web Content Management System implementation in history, which I'm trying to use for a personal project. I think the only cost is the domain (DNS), which is technically optional. This approach may be particularly appealing to (old-school?) software developers.
Caveats
- The site is completely static.
- The solution and site depend on github and Vercel.
- The user (or possibly users) edit(s) markdown files.
- All of the pages look the same but have different content.
Tools
- I use Linux, specifically the Fedora distribution.
- I use VSCodium and LibreOffice Writer to edit markdown files. I absolutely prefer local applications over browser-based editors.
- For preview, I use a local web server implemented in python.
- The solution uses a custom Rust program that converts markdown files to HTML.
- I use VSCodium to push the generated HTML files into a github project that automatically deploys them to a Vercel host.
Overview
-
Markdown Files: The source content. Technically, the markdown files don't need to go to github. The solution uses github to manage static HTML files generated from the markdown, so storing the markdown files there feels consistent with the rest of the solution. Additionally, using a github project for the markdown files:
- Provides a convenient backup of the files.
- Makes the files available to others.
- Provides visual tooling such as rendering markdown as HTML and comparing file versions.
- Could help with concurrent updates by multiple users.
- Makes it easy to work on the project from multiple computers.
-
Rust Program: Iterates the directory structure that contains the markdown files and creates a corresponding directory structure containing HTML files. Working on this informed me about some quirks in markdown.
- Copies /styles.css from the markdown project.
- Uses /template.html from the markdown project as a template for the HTML files.
- Creates index.html in every directory even if index.md doesn't exist in that directory.
- Generates navigation HTML based on the directory structure.
- Copies new and changed files such as images from the source to the target directory.
- https://github.com/deliverystack/jpw3gen
-
Python Web Server: Supports local preview of the website before deployment. Unfortunately, this basic web server requires that file extensions such as HTML appear in URLs, so I can't get rid of those as I would like. Plenty of easy solutions simply require use of a different web server, but this would also require a change to the Rust program to exclude extensions in URLs.
-
Generated Files: I push the generated files into a github project that automatically deploys them to a Vercel host.
-
Vercel: I configured Vercel to use my domain. Configuration and hosting are free, but the domain cost money. Vercel assigns a subdomain for the site for free, so a custom domain is technically optional.
Process
Once everything is set up, I follow this process to update the site:
- Use the Rust program to build the files.
- Preview things in a browser (optional).
- Use VSCodium to check everything in to github.
Next Steps
This is obviously a work in progress, but I'm so sick of WordPress that I might stop blogging and just use it anyway, which would encourage me to address some issues (no home page, no header or footer, file names in nav rather than titles, and so forth). Almost nobody comments on my blog anyway and I don't care about likes. I'll probably use LinkedIn to promote my posts and people can comment there. I might even link back from the posts to LinkedIn to encourage that.
WordPress also gives me traffic reports. I think I can just integrate google analytics instead, specifically by adding the required tag to /template.html. That's out of scope for this post, but it looks pretty straightforward:
This depends on the client, where WordPress may use the server for tracking.
Update 12.Dec.2025:
I added the google analytics tag.
I added the ability to embed some JSON in the markdown, but haven't fully tested.
page_title: Long HTML page title (and maybe a heading?).nav_title: Short link text for navigation.avoid_generation: Don't generate an HTML file or process the directory (mainly forindex.mdfiles).exclude_from_nav: Exclude this file from the site nav.keep_json_in_content: Include this JSON in the HTML.sort_key: For sorting the entry relative to its siblings (case-insensitive).
Comments
Feel free to comment here:
JSON Example
{
"page_title": "The Cheapest CMS Implementation in History?",
"nav_title": "Cheapest CMS",
"avoid_generation": false,
"exclude_from_nav": false,
"keep_json_in_content": true,
"sort_key": "ABC"
}