MuStatic: a static website architecture for serverless settings

An easy tech-stack that fits many small websites

Intro

Credit to AliMark71 for developing this architecture and reviewing this article.

This document outlines an architecture useful for projects like hackathon proofs-of-concept (PoC), personal or work portfolios, and public data viewer websites.

Before continuing, it helps to be familiar with static engine generators (like Jekyll, Liquid, or Blade) and GitHub Workflows.

History

This architecture began in the informatics-sa/Website repository (Visit website):

  • 01 May 2024: Started with standard HTML/Jekyll using manual _layouts.
  • 15 Aug 2024: Created a build.py script to speed up the building process.
  • 18 Aug 2024: Added a workflow to automatically run build.py on every commit.
  • 07 Sep 2024: Cleaned the repository root and moved served files to a root/ directory.
  • 13 Mar 2025: AliMark71 added a library to build.py to reduce repeated code, such as reading JSON and writing frontmatter.
  • 14 Mar 2025: Improved internationalization (i18n) by adding a translations.json file.
  • 04 Oct 2025: Moved the _layout/ folder into a Git submodule.
  • 18 May 2026: AliMark71 integrated a secret database feature.

Overview

This architecture replaces a traditional monolithic backend with a pre-compiled, serverless pipeline. It is built on three pillars:

  • Data: Uses JSON (or SQLite/CSV) as the single source of truth instead of a runtime database.
  • Builder: A script (often Python, Swift, PHP, Go, or JavaScript) that handles heavy calculations before the page loads.
  • Engine: A tool like Jekyll (or Liquid/Blade) to compile Markdown and frontmatter into final HTML. This is optional, but useful for speed and aesthetics.

This guide focuses on a stack using JSON, Python, and Jekyll, all hosted on GitHub Pages.

Structure

The recommended folder structure separates core data, builders, and raw HTML/Markdown files to keep things organized:

  • .github/workflows/pages-deploy.yml: Handles the deployment automation.
  • builder/: Contains all builder scripts, including lib/ and build.py.
  • data/: Stores your raw data files, like *.json.
  • root/: Holds the final output to be served, including _layouts/, compiled *.html, and *.md files.

(Note: Some older examples place data/ and _layouts/ inside root/ and leave build.py in the main directory, but the structure above is cleaner).

Deployment

Updating the site requires no manual server administration. The automated lifecycle works like this:

  1. Edit data: A maintainer pushes updated JSON or CSV files directly to the GitHub repository.
  2. Trigger workflow: GitHub Actions detects the push and runs the .github/workflows/*.yml file on an isolated runner.
  3. Run builder script: The runner executes build.py. This script reads the data files, checks them for errors, synthesizes the content, and writes the final pages and frontmatter into the ./root directory.
  4. Host via engine: The workflow runs bundle exec jekyll serve -s ./root, mapping the pre-calculated data into HTML layouts and pushing the final assets to GitHub Pages.

This setup is optimized for GitHub Pages, but it works just as well with Cloudflare Pages or similar services.

Best Practices

Public Data API

Since your raw data is in JSON format, expose it via endpoints like website.com/data/file.json. This gives users and AI tools direct access to the data without needing to scrape the site.

Data Validation

The builder script should verify your data format before compiling. If the data is invalid, the workflow should fail and log the error for easy debugging.

Balance Workflow and JavaScript

Do not rely on JavaScript to fetch all your data, as it slows down the experience for the end user. Precompute as much as possible during the workflow build. Reserve JavaScript for dynamic features like redirects, public API calls, timers, and animations.

As a rule of thumb: aim for a maximum of 3 seconds for JavaScript loading and 3–5 minutes for workflow building. (A base workflow with an empty build.py takes about 20–30 seconds).

Addons

Secrets Management

Use GitHub Secrets to securely pass keys if your builder script needs to fetch data from hidden external APIs during the build process.

Localization (i18n)

Support multiple languages (like Arabic and English) by centralizing your text in a translations.json file. The builder script can duplicate routing paths, and localized strings are mapped via Jekyll’s _data/ folder.

Encrypted Pages

For sensitive content, the builder can generate an AES-encrypted page (e.g., page.enc.html). Using a simple view.html interface, users can enter a password and decrypt the content directly in their browser—no backend server required.

Submodules

You can use Git Submodules (.gitmodules) to share core builders, layouts, and styles across multiple repositories. Proceed with caution: while this keeps your code DRY (Don’t Repeat Yourself), debugging can be tricky, and bugs often aren’t found until a migration commit is tested.

Website Updater

You can build a simple frontend page that asks for a GitHub API token. If the token is valid, it provides an easy interface to update the site’s content directly from the browser.

Pros & Cons

Pros

  • Serverless: Host projects with a budget of exactly $0.
  • Fast Editing: Updating a JSON file from your phone can deploy site-wide changes in minutes.
  • Accessible Data: Easily manage and expose website data, even if it is sensitive.
  • Traffic Analysis: Link seamlessly with Google Analytics to track statistics without needing a database.
  • Version Control: Every change is an immutable Git commit, providing a perfect audit trail and instant rollbacks if mistakes happen.
  • High Security: No runtime database means no risk of SQL injection. Removing the backend server drastically reduces exposure to XSS and DDoS attacks.
  • Professional Polish: You can easily link a custom domain and set up email routing.

Cons

  • Read-Only Database: Visitors cannot write data back to the server.
  • Manual Setup: This architecture currently lacks extensive documentation, so initial setup requires manual work.
  • Build-Time Limits: As your dataset grows over the years, the builder script will take longer to compute. You are bound by the processing limits of GitHub Actions.
  • No Real-Time Features: Live chat, WebSockets, or instant leaderboards are impossible without relying on third-party external APIs.

Real-World Examples

You can use this Github template which is a basic website for poem lines in Arabic using (JSON + Python + Jekyll).