Files
content/news/how-this-portal-works.md
T

7.2 KiB

title, description, date, category, author, cover, draft
title description date category author cover draft
How this portal works: static pages, a visual editor, git as the database The architecture behind this demo: a visual editor in the browser, content as Markdown in git, and a public site that is nothing but static files. 2026-09-11 demo Cyfronet /uploads/diagrams/cover-git-cms.svg false

This portal is a set of static HTML files generated from Markdown kept in a git repository. Editors work in a visual editor in their browser, and still nothing on the public side runs an application or a database: between the reader and the content there is only a file server. This article was written and published through exactly the path it describes.

The stack in three parts

The three parts of the stack: Sveltia as the editor, git as the database, Astro as the generator

Sveltia CMS is the editor. A single JavaScript file, served as an ordinary page under /admin/. It signs the editor in to the git server and writes Markdown files and images straight into the repository, with no CMS server and no database behind it. Every save is a commit.

Git, served by Gitea, is the database. An article is a Markdown file with a fixed set of fields, such as title, date and cover image, and uploaded images sit next to it. Full history, and restoring any earlier version in one step, come from git itself rather than from a paid tier of a product.

Astro is a generator, not a server. Its build turns those Markdown files into a directory of plain HTML, CSS and images, so deploying means copying that directory onto a file server, and nothing from Astro runs in production. If an article has a broken field the build stops, so a faulty article never reaches readers. Interactive parts, a dashboard for example, can be added later without changing the technology.

From a save to a published page

Five steps of publishing: save, commit, change detected, build, published

Publishing is automatic. A save in the editor shows up on the site moments later, with no manual deployment step in between. If a build fails, the previous version simply keeps serving.

How it runs on the machine

The demo machine: Gitea with two repositories, a one-shot Astro build, the releases directory and nginx

Two repositories, by design: the portal code, maintained by the developers, and the editorial content, written by the CMS. Each build takes the newest version of both, so a change in either one republishes the site. Editors never touch the code, and development never mixes with editorial history.

Everything runs in three containers on one machine: the git server, the web server, and a build container that starts only after a change and exits when it is done.

For comparison, the same machine running a classic CMS. The diagram keeps the same shape on purpose: the application is up around the clock, editors and readers use that same application, and its login page faces the internet.

A database-backed CMS: one always-on application with a public login, plus a database and a media volume

Why a static site

Attack surface compared: a classic CMS exposes an application, a login panel and a database, this solution exposes files only

  • Security. What faces the internet is a file server and a set of files: no admin panel to attack, no database to steal, no application code needing urgent security fixes.
  • Maintenance. Nothing on the reader's side has to be watched and upgraded. What is left is the git server, which the team runs anyway, and a short build script.
  • Speed. A ready-made HTML file is the fastest thing a server can hand over, which search engines reward as well.
  • History. Every version of every article is kept, and going back is a single step.

How it compares with a database-backed CMS

The dividing line is not whether content sits in a database or in files. It is whether a running application stands between the reader and the content. The portal should last at least six years, so the last row matters as much as the first ones.

Static + git CMS (this demo) CMS that serves the site itself
Examples Sveltia, Decap (+ Astro / Hugo / Eleventy) Strapi, WordPress, Drupal, Grav, TinaCMS
Content lives in Markdown files in git usually a database
In production you run a file server an application, usually with a database
Maintenance a build script plus a git service patches, upgrades, database backups
Change history git, built in product-dependent, sometimes paid
Day one few moving parts, a visual editor ready to use; simple permissions, no editorial roles a richer panel at once: roles, workflow, structured content; more parts to set up and secure
Over six years nothing to patch on the reader's side; content in an open format outlives the tools major upgrades, database backups, a permanent attack surface

Two products blur the line: Grav keeps content in files but still serves every page with a PHP application, and TinaCMS keeps content in git but needs a backend with a database to edit it.

In the free self-hosted editions we checked, Strapi keeps content version history for paid plans only, while WordPress and Grav carry long records of vulnerabilities. Features can move into paid plans over time; content and history in git leave nothing a vendor can put behind a paywall.

Isn't Gitea just another CMS?

A fair question, because Gitea also has a login, a database and security updates, so maintenance here is not zero either. Three things differ:

  • It is off the reader's path. Gitea can be down, mid-upgrade or even broken into while the portal keeps serving its files. The worst case is vandalised content: visible, recorded, undone with one command. In a CMS that serves the site, the same incident means a compromised public site.
  • The team already runs it. The content repository can live on the instance the team maintains anyway, so the extra cost is close to zero.
  • It is smaller software. Gitea is one program with a small database, and an upgrade is a restart. CMS platforms are frameworks with plugins and regular major migrations.

No lock-in

The content is plain Markdown, so each part can be swapped on its own: Sveltia for Decap (one line of configuration), Astro for another generator, Gitea for any other git hosting. Leaving the approach entirely is a plain export.

That is the conclusion from this demo: start with the simplest solution that meets the requirements, a visual editor for the editors and a public site that needs no care, and add complexity only when a need appears that this approach cannot serve.

Where things are

What Where
Portal this site
Content editor /admin/, the "Content editor" link in the footer
Content repository ctao/content on the demo machine's Gitea, written by the CMS
Code repository ctao/portal, maintained by the team
Deployment documentation deploy/README.md in the code repository