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

141 lines
7.2 KiB
Markdown

---
title: "How this portal works: static pages, a visual editor, git as the database"
description: "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."
date: 2026-09-11
category: demo
author: Cyfronet
cover: /uploads/diagrams/cover-git-cms.svg
draft: 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](/uploads/diagrams/stack-overview.svg)
**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](/uploads/diagrams/publish-flow.svg)
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](/uploads/diagrams/architecture-machine.svg)
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](/uploads/diagrams/architecture-cms-database.svg)
## Why a static site
![Attack surface compared: a classic CMS exposes an application, a login panel and a database, this solution exposes files only](/uploads/diagrams/attack-surface.svg)
- **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 |