demo article: shorter text for a mixed technical audience
This commit is contained in:
+98
-149
@@ -1,6 +1,6 @@
|
|||||||
---
|
---
|
||||||
title: "How this portal works: static pages, a visual editor, git as the database"
|
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. With an honest comparison against database-backed CMS platforms."
|
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
|
date: 2026-09-11
|
||||||
category: demo
|
category: demo
|
||||||
author: Cyfronet
|
author: Cyfronet
|
||||||
@@ -9,66 +9,125 @@ draft: false
|
|||||||
---
|
---
|
||||||
|
|
||||||
This portal is a set of **static HTML files** generated from Markdown kept in a
|
This portal is a set of **static HTML files** generated from Markdown kept in a
|
||||||
git repository. Editors get a **visual editor in the browser**, and still
|
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
|
**nothing on the public side runs an application or a database**: between the
|
||||||
reader and the content there is only a file server. The one service that does
|
reader and the content there is only a file server. This article was written
|
||||||
run, the git server, stands behind the editor and off the reader's path, and we
|
and published through exactly the path it describes.
|
||||||
come back to that honestly below. This article was written and published
|
|
||||||
through exactly the path it describes.
|
|
||||||
|
|
||||||
## The stack in three parts
|
## The stack in three parts
|
||||||
|
|
||||||

|

|
||||||
|
|
||||||
**Sveltia CMS is the editor.** It is one JavaScript file served as a static page
|
**Sveltia CMS is the editor.** A single JavaScript file, served as an ordinary
|
||||||
under `/admin/`. It signs the editor in to Gitea and writes Markdown files and
|
page under `/admin/`. It signs the editor in to the git server and writes
|
||||||
images straight into the repository through the git API. There is no CMS server
|
Markdown files and images straight into the repository, with no CMS server and
|
||||||
and no database behind it, and every save is a commit.
|
no database behind it. Every save is a commit.
|
||||||
|
|
||||||
**Git, served by Gitea, is the database.** An article is a Markdown file with a
|
**Git, served by Gitea, is the database.** An article is a Markdown file with a
|
||||||
defined set of fields, and uploaded images sit next to it. History, diffs and a
|
fixed set of fields, such as title, date and cover image, and uploaded images
|
||||||
one-command rollback come from git itself rather than from a paid tier of a
|
sit next to it. Full history, and restoring any earlier version in one step,
|
||||||
product.
|
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
|
**Astro is a generator, not a server.** Its build turns those Markdown files
|
||||||
into a directory of plain HTML, CSS and images, so deployment is copying that
|
into a directory of plain HTML, CSS and images, so deploying means copying that
|
||||||
directory onto a file server. Nothing from Astro runs in production. Pages ship
|
directory onto a file server, and nothing from Astro runs in production. If an
|
||||||
without JavaScript by default, while components, React among them, are rendered
|
article has a broken field the build stops, so a faulty article never reaches
|
||||||
to HTML during the build, so an interactive dashboard can later be added as an
|
readers. Interactive parts, a dashboard for example, can be added later without
|
||||||
island on the same site without changing the stack. The build also checks every
|
changing the technology.
|
||||||
article against the defined fields, which means a malformed article stops the
|
|
||||||
build instead of reaching the public site.
|
|
||||||
|
|
||||||
On the reader's side that leaves one moving part: nginx handing over files.
|
|
||||||
|
|
||||||
## From a save to a published page
|
## From a save to a published page
|
||||||
|
|
||||||

|

|
||||||
|
|
||||||
Publishing is automatic. A save in the editor shows up on the site moments
|
Publishing is automatic. A save in the editor shows up on the site moments
|
||||||
later, with no manual deployment step in between. Every change is a commit, so
|
later, with no manual deployment step in between. If a build fails, the
|
||||||
**a full content history and a one-command rollback come for free**.
|
previous version simply keeps serving.
|
||||||
|
|
||||||
## How it runs on the machine
|
## How it runs on the machine
|
||||||
|
|
||||||

|

|
||||||
|
|
||||||
Two repositories, by design. The portal **code** is developed by the team, the
|
Two repositories, by design: the portal **code**, maintained by the developers,
|
||||||
editorial **content** is written by the CMS. Every build takes the newest commit
|
and the editorial **content**, written by the CMS. Each build takes the newest
|
||||||
from both, overlays content onto code and renders the site, so a push to either
|
version of both, so a change in either one republishes the site. Editors never
|
||||||
repository republishes. Editors never touch the code repository, and
|
touch the code, and development never mixes with editorial history.
|
||||||
development never mixes with the editorial history.
|
|
||||||
|
|
||||||
Everything runs rootless in podman on a single machine: a Gitea container, an
|
Everything runs in three containers on one machine: the git server, the web
|
||||||
nginx container, and a one-shot build container started by a systemd timer. A
|
server, and a build container that starts only after a change and exits when it
|
||||||
build that fails changes nothing, because the previous release keeps serving.
|
is done.
|
||||||
|
|
||||||
For comparison, the same machine running a classic CMS. The diagram keeps the
|
For comparison, the same machine running a classic CMS. The diagram keeps the
|
||||||
same skeleton on purpose, so the difference is visible at a glance: every
|
same shape on purpose: the application is up around the clock, editors and
|
||||||
element is up around the clock, the editor and the reader talk to the very same
|
readers use that same application, and its login page faces the internet.
|
||||||
application, and its login panel faces the internet.
|
|
||||||
|
|
||||||

|

|
||||||
|
|
||||||
|
## Why a static site
|
||||||
|
|
||||||
|

|
||||||
|
|
||||||
|
- **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
|
## Where things are
|
||||||
|
|
||||||
@@ -79,113 +138,3 @@ application, and its login panel faces the internet.
|
|||||||
| Content repository | `ctao/content` on the demo machine's Gitea, written by the CMS |
|
| Content repository | `ctao/content` on the demo machine's Gitea, written by the CMS |
|
||||||
| Code repository | `ctao/portal`, maintained by the team |
|
| Code repository | `ctao/portal`, maintained by the team |
|
||||||
| Deployment documentation | `deploy/README.md` in the code repository |
|
| Deployment documentation | `deploy/README.md` in the code repository |
|
||||||
|
|
||||||
## Why a static site
|
|
||||||
|
|
||||||

|
|
||||||
|
|
||||||
- **Security.** What faces the internet is a file server and a directory of
|
|
||||||
files. There is no admin panel to attack, no database to exfiltrate and no
|
|
||||||
application runtime carrying its own vulnerabilities.
|
|
||||||
- **Maintenance.** There is no application that has to be monitored and upgraded
|
|
||||||
under patch pressure. What remains is a git service, which the team runs
|
|
||||||
anyway, and a short build script.
|
|
||||||
- **Speed.** Static HTML is as fast as a page gets, which search engines reward
|
|
||||||
as well.
|
|
||||||
- **History.** Versioning is a property of git, not a feature tier of a product.
|
|
||||||
|
|
||||||
## Two classes of solution
|
|
||||||
|
|
||||||
The dividing line is often misread. The question is not whether content sits in
|
|
||||||
a database or in files. It is **whether a running application stands between the
|
|
||||||
reader and the content**.
|
|
||||||
|
|
||||||
| | 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; Grav in files, Tina in git |
|
|
||||||
| In production you run | a file server | an application, usually with a database, non stop |
|
|
||||||
| Maintenance | a build script plus a git service | patches, upgrades, database backups |
|
|
||||||
| Change history | git, built in | product-dependent, sometimes paid |
|
|
||||||
|
|
||||||
Two clarifications, so the table is not read too simply. **Grav** keeps content
|
|
||||||
in files with no database at all, yet a PHP application still serves every page,
|
|
||||||
which puts it in the right-hand column for attack surface and maintenance.
|
|
||||||
**TinaCMS** keeps content as Markdown in git, but its editor needs a backend
|
|
||||||
with a database in order to work, so it inherits the costs of both worlds.
|
|
||||||
|
|
||||||
Limits of the individual products, verified during our research:
|
|
||||||
|
|
||||||
- **Strapi**: content version history does not exist in the free self-hosted
|
|
||||||
edition (Growth and Enterprise plans only). It needs Node and PostgreSQL
|
|
||||||
running at all times, plus regular upgrades.
|
|
||||||
- **WordPress**: the largest ecosystem, and also the longest vulnerability
|
|
||||||
history, mostly through plugins; a public login panel and PHP to patch
|
|
||||||
continuously.
|
|
||||||
- **Grav** (PHP, flat file, the "light" alternative): serious remote code
|
|
||||||
execution issues in recent years, and still a PHP server facing the internet.
|
|
||||||
- **TinaCMS**: it edits Markdown, but it requires a backend. The Tina cloud is
|
|
||||||
free for up to two users, and self-hosting means your own Node server, a
|
|
||||||
database and authentication, which is an application to maintain again.
|
|
||||||
- **Open-core risk**: features can move into paid plans over time, as above.
|
|
||||||
When the content and its history live in git, there is no feature a vendor
|
|
||||||
can move behind a paywall.
|
|
||||||
|
|
||||||
## Trade-offs over six years
|
|
||||||
|
|
||||||
The portal is meant to be maintained for at least six years, so the balance has
|
|
||||||
to be drawn over two horizons. What follows judges the two approaches as classes
|
|
||||||
of solution, not individual products.
|
|
||||||
|
|
||||||
**Short term: launch and the first months**
|
|
||||||
|
|
||||||
| | Static + git | CMS with a database |
|
|
||||||
|---|---|---|
|
|
||||||
| Strengths | few moving parts from day one; a visual editor ready to use; history and rollback immediately, in git | a richer panel out of the box: roles and permissions, editorial workflow, relations between content types, more ready-made integrations |
|
|
||||||
| Weaknesses | simple permissions, inherited from git, without editorial roles; browsing history happens in the git interface rather than in the editor | more components to stand up, connect and secure from day one: application, database, backups |
|
|
||||||
|
|
||||||
**Over six years**
|
|
||||||
|
|
||||||
| | Static + git | CMS with a database |
|
|
||||||
|---|---|---|
|
|
||||||
| Strengths | no application on the reader's path, so no patch pressure there; what stays is a git service, ideally the one the team already runs; content in an open format survives any change of tooling | if application features are eventually needed (accounts, personalisation, strongly structured content), the platform already has them |
|
|
||||||
| Weaknesses | at very large scale (thousands of pages) build times grow and need attention; advanced editorial features depend on the pace of open-source tools | several major version migrations in six years, with breaking changes; the database needs backups and restore drills; a permanent attack surface; features can move into paid tiers |
|
|
||||||
|
|
||||||
## "Isn't Gitea just another CMS?"
|
|
||||||
|
|
||||||
A fair question. Gitea also has a login, a database and security updates, and it
|
|
||||||
has to be reachable by editors. Does the difference come down to maintaining
|
|
||||||
Gitea instead of a CMS?
|
|
||||||
|
|
||||||
Partly yes, and we do not claim that maintenance is zero. The difference rests
|
|
||||||
on three things:
|
|
||||||
|
|
||||||
- **Gitea is off the reader's path.** It can be down, mid-upgrade, or even
|
|
||||||
compromised while the portal keeps serving safe files. The worst case of a
|
|
||||||
break-in is content vandalism: visible, fully recorded, and undone with a
|
|
||||||
single revert. In a CMS that serves the site, the same incident means a
|
|
||||||
compromised public site.
|
|
||||||
- **The team already runs Gitea.** The content repository can live on the
|
|
||||||
instance the team maintains anyway, which brings the marginal cost close to
|
|
||||||
zero and lets one AAI integration handle sign-in.
|
|
||||||
- **Class of software.** Gitea is a single Go program with SQLite: an upgrade is
|
|
||||||
swapping an image and restarting, with no plugin ecosystem and no server-side
|
|
||||||
npm dependency tree. CMS platforms are application frameworks with regular
|
|
||||||
major-version migrations.
|
|
||||||
|
|
||||||
## No lock-in
|
|
||||||
|
|
||||||
Every element can be replaced on its own, because the content is plain Markdown:
|
|
||||||
|
|
||||||
- **Sveltia to Decap**: the same configuration format, so switching is one
|
|
||||||
script tag.
|
|
||||||
- **Astro to Hugo or Eleventy**: the Markdown stays untouched, only templates
|
|
||||||
change.
|
|
||||||
- **Gitea to any git hosting**: GitHub, GitLab, or an internal service.
|
|
||||||
- Even dropping the whole approach is a trivial export, because the content has
|
|
||||||
been sitting in an open format in our own repository from the start.
|
|
||||||
|
|
||||||
The conclusion we draw from this demo: start with the simplest solution that
|
|
||||||
meets the requirements, which means a visual editor for the editors and a public
|
|
||||||
site that needs no care. Add complexity only when a need appears that this
|
|
||||||
approach cannot serve.
|
|
||||||
|
|||||||
Reference in New Issue
Block a user