RM Static CMS Kit is een lichte static-site-generator met een klein Flask CMS erbovenop.
Het doel is simpel: snelle websites bouwen voor kleine organisaties, zzp'ers, verenigingen en projecten zonder zwaar CMS zoals WordPress.
De publieke website is statisch. Het CMS draait apart en genereert bij wijzigingen opnieuw de statische HTML. Daardoor blijft de publieke website snel, simpel en onderhoudsarm.
Huidige basisversie:
v2.0.0
- Static-site-generator in Python
- Flask/Gunicorn CMS
- Statische publieke HTML-output
- Markdown-content voor pagina's, blog/publicaties, diensten en projecten
- JSON-content voor agenda/evenementen
- Contactpagina en contactformulier
- Projecten/portfolio-module
- Agenda/evenementenmodule
- RSS-feed
- Sitemap
- Configureerbare navigatie
- Configureerbare contenttypes
- Modulekeuze per site via installer-wizard
- Per-site kleuren en thema-opties via
config/site.json - Publiceren naar
/var/www/<domein>/public - Per-site CMS-wachtwoord via
/etc/rmstatic/<domein>.env - Per-site systemd-service voor het CMS
- Installer-scripts voor sites aanmaken en verwijderen
- Demo-content voor alle standaardmodules
Een RM Static CMS Kit-site bestaat uit twee delen.
Publieke website:
Bezoeker
↓
Nginx / reverse proxy
↓
/var/www/<domein>/public
↓
Statische HTML, CSS, JS en uploads
CMS:
Beheerder
↓
cms.<domein>
↓
Nginx Proxy Manager / reverse proxy
↓
Flask CMS via systemd
↓
/opt/sites/<domein>
↓
content, config, generator en theme
De publieke website is dus niet afhankelijk van Flask tijdens normaal bezoek. Flask is alleen nodig voor beheer en opnieuw genereren van de statische site.
Centrale kit:
/opt/rm-static-cms-kit
Sites:
/opt/sites/<domein>
Publieke webroot:
/var/www/<domein>/public
CMS-configuratie en wachtwoorden:
/etc/rmstatic/<domein>.env
Systemd-services:
/etc/systemd/system/<site>_cms.service
Voorbeeld:
demo.rmeijer.eu -> demo_rmeijer_eu_cms.service
Nginx-configs:
/etc/nginx/sites-available/<domein>
/etc/nginx/sites-enabled/<domein>
Exports:
/opt/rmstatic-exports
/opt/rm-static-cms-kit/
├── bin/
├── cms/
├── config/
├── content/
├── generator/
├── scripts/
├── theme/
├── requirements.txt
├── VERSION
├── README.md
└── .gitignore
Belangrijke onderdelen:
generator/build.py Static-site-generator
cms/app.py Flask CMS
theme/default/ Standaardthema
config/site.json Standaardconfiguratie
content/ Demo-content
bin/ Installer- en beheerscripts
scripts/deploy.sh Build + deploy per site
Hoofdcommando:
rmstatic-create-site klant.nl "Klantnaam"Dit start de wizard. De wizard vraagt welke modules moeten worden ingeschakeld:
Blog/publicaties inschakelen?
Dienstenmodule inschakelen?
Agenda/evenementen inschakelen?
Projecten/portfolio inschakelen?
Contactformulier inschakelen?
RSS-feed inschakelen?
Sitemap inschakelen?
Social images/uploads inschakelen?
Demo-content plaatsen/behouden?
Onder water gebruikt dit:
/usr/local/bin/rmstatic-create-site
/usr/local/bin/rmstatic-create-site-wizard
/usr/local/bin/rmstatic-create-site-core
De scripts staan ook in de repository onder:
bin/
rmstatic-remove-site klant.nlDe verwijdertool vraagt om expliciete bevestiging door het domein exact in te typen.
De tool verwijdert:
/opt/sites/<domein>
/var/www/<domein>
/etc/rmstatic/<domein>.env
/etc/systemd/system/<site>_cms.service
/etc/nginx/sites-available/<domein>
/etc/nginx/sites-enabled/<domein>
Help:
rmstatic-remove-site --helpGebruik bij voorkeur het CMS om de website opnieuw te genereren.
Handmatig deployen kan zo:
sudo -u moofz bash -lc 'cd /opt/sites/<domein> && ./scripts/deploy.sh'Voorbeeld:
sudo -u moofz bash -lc 'cd /opt/sites/demo.rmeijer.eu && ./scripts/deploy.sh'De scripts build.sh en deploy.sh bevatten een root-guard. Als ze toch als root worden gestart, proberen ze zichzelf opnieuw uit te voeren als eigenaar van de site-directory. Dit voorkomt root-owned bestanden in public/.
Ga naar een site-directory:
cd /opt/sites/<domein>Project controleren:
./scripts/check.shAlleen bouwen:
./scripts/build.shBouwen en deployen naar de publieke webroot:
./scripts/deploy.shRechten herstellen:
./scripts/fix-permissions.shLet op: fix-permissions.sh slaat .venv over. Dat is bewust, omdat Python virtualenv-bestanden uitvoerbare rechten nodig hebben.
Het CMS draait per site op een eigen poort.
Voorbeeld:
demo.rmeijer.eu -> 5059
De poort staat in:
/etc/rmstatic/<domein>.env
Controleer de poort:
grep RM_CMS_PORT /etc/rmstatic/<domein>.envVoor Nginx Proxy Manager:
Publieke website:
<domein> / www.<domein> -> http://<server-ip>:80
CMS:
cms.<domein> -> http://<server-ip>:<CMS_PORT>
Advies: zet het CMS achter een NPM Access List, VPN of LAN-restrictie.
Het CMS is opgesplitst in losse beheerpagina's.
Voorbeelden:
/admin
/admin/pages
/admin/posts
/admin/uploads
/admin/settings
/admin/system
Dashboard:
/admin
Geeft een overzicht van de belangrijkste onderdelen.
Pagina's:
/admin/pages
Beheer vaste pagina's zoals home, contact, over en diensten.
Publicaties/blog:
/admin/posts
Beheer artikelen, nieuwsberichten of externe publicaties.
Uploads:
/admin/uploads
Upload afbeeldingen en PDF-bestanden naar content/uploads. Na build/deploy worden deze publiek beschikbaar onder /uploads/.
Site-instellingen:
/admin/settings
Beheer basisvelden uit config/site.json, zoals sitenaam, site URL, omschrijving, contactmail, navigatie en thema-opties.
Systeembeheer:
/admin/system
Toont technische site-informatie en bevat de knop om de website opnieuw te genereren en publiceren.
content/
├── pages/
├── posts/
├── services/
├── events/
├── projects/
└── uploads/
Contenttypen:
| Module | Pad | Type |
|---|---|---|
| Pagina's | content/pages/*.md |
Markdown + frontmatter |
| Blog/publicaties | content/posts/*.md |
Markdown + frontmatter |
| Diensten | content/services/*.md |
Markdown + frontmatter |
| Agenda/evenementen | content/events/*.json |
JSON |
| Projecten/portfolio | content/projects/*.md |
Markdown + frontmatter |
| Uploads | content/uploads/ |
Afbeeldingen/PDF |
Uploads worden opgeslagen in:
content/uploads/
Na build/deploy komen ze publiek beschikbaar in:
/uploads/
Voorbeeld in Markdown:
Toegestane bestandstypen:
jpg
jpeg
png
gif
webp
pdf
De uploadmanager voorkomt simpele pad-traversal via bestandsnamen en gebruikt veilige bestandsnamen. Als een bestand al bestaat, krijgt de nieuwe upload automatisch een opvolgend nummer.
De belangrijkste configuratie staat in:
config/site.json
Belangrijke velden:
site_name
site_url
description
contact_email
deploy_dir
navigation
features
content_types
theme_options
contact_form
Modules kunnen aan of uit worden gezet via features en content_types.
Voorbeeld:
{
"features": {
"posts": true,
"services": true,
"events": true,
"projects": true,
"contact_form": true,
"rss": true,
"sitemap": true
},
"content_types": {
"posts": {
"enabled": true,
"label": "Blog",
"slug": "blog"
},
"events": {
"enabled": true,
"label": "Agenda",
"slug": "agenda"
},
"projects": {
"enabled": true,
"label": "Projecten",
"slug": "projecten"
}
}
}Pad:
content/pages/*.md
Voorbeelden:
content/pages/home.md
content/pages/contact.md
Output:
/
/contact/
Pad:
content/posts/*.md
Voorbeeld:
content/posts/eerste-publicatie.md
Output:
/blog/
/blog/eerste-publicatie/
Pad:
content/services/*.md
Voorbeeld:
content/services/nederlandse-les.md
Output:
/diensten/
/diensten/nederlandse-les/
Pad:
content/events/*.json
Voorbeeld:
{
"title": "Demo-evenement",
"slug": "demo-evenement",
"date": "2026-09-01",
"dates": ["2026-09-01"],
"summary": "Een voorbeeld van de agenda/evenementenmodule.",
"description": "Dit is een demo-evenement om de agenda-module te testen.",
"location": "Online",
"published": true
}Output:
/agenda/
/agenda/demo-evenement/
Pad:
content/projects/*.md
Voorbeeld:
---
title: Demo project
slug: demo-project
client: Voorbeeldklant
type: Website
year: 2026
status: Live
website_url: https://ontwikkel.rmeijer.eu
image: ""
summary: Een voorbeeldproject voor de portfolio/projectenmodule.
features:
- Statische website
- Eigen CMS
- Contactformulier
- Responsive ontwerp
tech:
- Python
- Flask
- Static Site Generator
- Nginx
published: true
featured: true
---
Dit is een voorbeeld van een projectdetailpagina.Output:
/projecten/
/projecten/demo-project/
Templates:
theme/default/templates/projects.html
theme/default/templates/project.html
De basis-kit bevat demo-content voor alle standaardmodules:
content/pages/home.md
content/pages/contact.md
content/posts/eerste-publicatie.md
content/services/nederlandse-les.md
content/events/demo-evenement.json
content/projects/demo-project.md
Met demo-content kun je na installatie direct alle routes testen.
Na aanmaken of deployen van een site:
curl -I -H 'Host: demo.rmeijer.eu' http://127.0.0.1/
curl -I -H 'Host: demo.rmeijer.eu' http://127.0.0.1/blog/
curl -I -H 'Host: demo.rmeijer.eu' http://127.0.0.1/diensten/
curl -I -H 'Host: demo.rmeijer.eu' http://127.0.0.1/agenda/
curl -I -H 'Host: demo.rmeijer.eu' http://127.0.0.1/projecten/
curl -I -H 'Host: demo.rmeijer.eu' http://127.0.0.1/contact/
curl -I -H 'Host: demo.rmeijer.eu' http://127.0.0.1/feed.xml
curl -I -H 'Host: demo.rmeijer.eu' http://127.0.0.1/sitemap.xmlAlle ingeschakelde modules moeten 200 OK geven.
CMS-backend testen:
sudo bash -c 'set -a; source /etc/rmstatic/demo.rmeijer.eu.env; curl -I -u "$RM_CMS_USER:$RM_CMS_PASSWORD" http://127.0.0.1:$RM_CMS_PORT/admin'Clone de repository:
cd /opt
git clone git@github.com:RMeijerEU/rm-static-cms-kit.gitZet rechten:
sudo chown -R moofz:www-data /opt/rm-static-cms-kit
sudo chmod -R g+rwX /opt/rm-static-cms-kitInstalleer de scripts:
sudo cp /opt/rm-static-cms-kit/bin/rmstatic-* /usr/local/bin/
sudo chmod +x /usr/local/bin/rmstatic-*Maak basispaden:
sudo mkdir -p /opt/sites /var/www /etc/rmstatic /opt/rmstatic-exports
sudo chown -R moofz:www-data /opt/sites /opt/rm-static-cms-kitMaak een eerste site:
rmstatic-create-site voorbeeld.nl "Voorbeeldsite"Pak het archief uit:
tar -xzf rm-static-cms-kit-v2.0.0-all-modules.tar.gzKopieer template en scripts:
sudo mkdir -p /opt/rm-static-cms-kit
sudo rsync -a opt/rm-static-cms-kit/ /opt/rm-static-cms-kit/
sudo cp opt/rm-static-cms-kit/bin/rmstatic-* /usr/local/bin/
sudo chmod +x /usr/local/bin/rmstatic-*Maak basispaden:
sudo mkdir -p /opt/sites /var/www /etc/rmstatic /opt/rmstatic-exports
sudo chown -R moofz:www-data /opt/sites /opt/rm-static-cms-kitMaak een eerste site:
rmstatic-create-site voorbeeld.nl "Voorbeeldsite"Maak een schone export van de kit:
cd /opt
tar -czf /opt/rmstatic-exports/rm-static-cms-kit-v2.0.0-all-modules-$(date +%Y%m%d-%H%M%S).tar.gz rm-static-cms-kitBestaande export:
/opt/rmstatic-exports/rm-static-cms-kit-v2.0.0-all-modules-20260624-102306.tar.gz
Checksum maken:
cd /opt/rmstatic-exports
sha256sum rm-static-cms-kit-v2.0.0-all-modules-*.tar.gz > SHA256SUMSDe export bevat:
- template;
- generator;
- CMS;
- thema;
- scripts;
- demo-content;
- installatiebestanden.
De export bevat niet:
/opt/sites/*;/etc/rmstatic/*.env;- klantdata buiten de kit;
- bestaande productie-sites.
De centrale kit staat onder Git:
/opt/rm-static-cms-kit
Gebruik Git altijd als moofz, niet als root.
Status:
cd /opt/rm-static-cms-kit
sudo -u moofz git statusCommit:
sudo -u moofz git add .
sudo -u moofz git commit -m "Beschrijving"
sudo -u moofz git pushTag maken:
sudo -u moofz git tag -a v2.0.0 -m "RM Static CMS Kit v2.0.0"
sudo -u moofz git push origin v2.0.0Huidige basis:
main
v2.0.0
Backup van oude main:
github-main-before-v2-clean-kit
Standaard eigenaarschap:
moofz:www-data
Rechten herstellen voor een site:
chown -R moofz:www-data /opt/sites/<domein>
chmod -R g+rwX /opt/sites/<domein>Voor de centrale kit:
chown -R moofz:www-data /opt/rm-static-cms-kit
chmod -R g+rwX /opt/rm-static-cms-kitVoorkom root-owned buildbestanden door build/deploy als moofz te draaien.
Er zijn drie lagen:
- Proxmox-backup van de LXC.
- GitHub-repository en tags.
- Losse kit-export op Proxmox-storage.
- Per-site content in
/opt/sites/<domein>.
Voor een volledige restore van een bestaande site is de Proxmox-backup het makkelijkst.
Voor een nieuwe installatie of migratie gebruik je de Git-repository of kit-export.
Oorzaak: Git wordt als root uitgevoerd in een repo die eigendom is van moofz.
Oplossing:
chown -R moofz:www-data /opt/rm-static-cms-kit
sudo -u moofz git config --global --add safe.directory /opt/rm-static-cms-kitDaarna Git gebruiken als moofz:
sudo -u moofz git statusControleer of de module aan staat:
python3 - <<'PY'
import json
from pathlib import Path
site = json.loads(Path("config/site.json").read_text())
print(site.get("features", {}))
print(site.get("content_types", {}))
PYControleer of er output is:
find public /var/www/<domein>/public -path "*/projecten*" -printDeploy opnieuw:
sudo -u moofz bash -lc 'cd /opt/sites/<domein> && ./scripts/deploy.sh'Controleer:
- NPM proxy host;
- juiste CMS-poort;
- Access List;
- firewall;
- systemd-service.
CMS-poort:
grep RM_CMS_PORT /etc/rmstatic/<domein>.envService:
systemctl status <service>.serviceVoorbeeld:
conflicting server name "hvvliet.nl" on 0.0.0.0:80, ignored
Dit betekent dat er dubbele Nginx server blocks bestaan voor hetzelfde domein. Het is een waarschuwing, maar moet later wel worden opgeschoond om verwarring te voorkomen.
Geteste demo-site:
demo.rmeijer.eu
Werkende routes:
/
/blog/
/diensten/
/agenda/
/projecten/
/contact/
/feed.xml
/sitemap.xml
Alle routes geven 200 OK.
Eerste productiecase:
hvvliet.nl
Publiek:
hvvliet.nl
www.hvvliet.nl
CMS:
cms.hvvliet.nl
- Schone nieuwe basis van de kit
- Installer-wizard als standaard
rmstatic-create-site - Modulekeuze per nieuwe site
- Projecten/portfolio-module toegevoegd
- Demo-content voor alle modules
- Agenda-demo toegevoegd
- RSS en sitemap getest
rmstatic-remove-sitetoegevoegd- Installer-scripts opgenomen in
bin/ - GitHub main opnieuw ingericht als schone basis
- Oude main veiliggesteld als
github-main-before-v2-clean-kit
Oudere ontwikkelreeks met eerdere modules, beheerfuncties en experimenten.
- CMS-editor voor event flyers
- Verbeterde event/flyer-rendering
- Publicatiesysteem verder uitgebreid
- Publicatie-editor verbeterd
- Delen-knoppen en metadata uitgebreid
- Basis site-instellingen via CMS
- Automatische backup van
config/site.jsonvóór opslaan
- Admin panel opgesplitst in losse secties
- Dashboard, pagina's, publicaties, instellingen en systeembeheer
- README/documentatie opgeschoond
- Kit exporteerbaar gemaakt
- Export bevat geen klantdata
- Export bevat geen CMS-wachtwoorden
- Site-informatie tonen via beheertool
- Per-site credentials via
/etc/rmstatic/<domein>.env - Geen hardcoded CMS-wachtwoorden meer
- Sitebeheer via commandline uitgebreid
- Site aanmaken via script
- Automatische vrije CMS-poorten
- Automatische systemd- en Nginx-configs
- CMS-beheer verbeterd
- Concept/publiceren
- Verwijderen via CMS
- Nieuwe pagina's en publicaties aanmaken via CMS
- Eerste werkende static-site-generator met CMS