user@homelab:~/docs$ cat docs-site.md
Tento web
Hugo, dva jazyky, image s nginx bez root práv a zabezpečení podu
# obsah
Architektura
Markdown (docs-site/site/content/{en,cs}/*.md)
│ hugo --minify ← build stage: alpine + Hugo ověřený přes SHA256
▼
statické HTML/CSS/JS
│ COPY do nginx-unprivileged ← runtime stage: uid 101, port 8080
▼
ghcr.io/<owner>/homelab-docs:<tag> ← GitHub Actions
│ Flux → Deployment → Service → Ingress (Traefik)
▼
https://svejklab.dev ← Cloudflare TunnelSestavení a nasazení popisuje kapitola CI/CD a GitOps.
Struktura
docs-site/
├── site/
│ ├── hugo.toml # konfigurace, jazyky en (výchozí) + cs
│ ├── content/en/ # anglické stránky
│ ├── content/cs/ # české stránky (stejné názvy souborů)
│ ├── i18n/ # texty šablony (en.toml, cs.toml)
│ ├── data/status_*.yaml # tabulky na úvodní stránce
│ ├── static/images/ # diagramy (SVG)
│ ├── layouts/ # vlastní "terminálová" šablona
│ └── assets/ # CSS a JS
├── nginx/default.conf # bezpečnostní hlavičky, /healthz
└── Dockerfile # multi-stage buildŠablona je vlastní, bez externích témat a CDN. Web nic nenačítá z cizích domén.
Dva jazyky
Hugo má vestavěnou podporu více jazyků. Stránky se spárují podle stejného jména souboru (content/en/docs/07-ssh.md ↔ content/cs/docs/07-ssh.md) a přepínač v záhlaví vede vždy na tutéž stránku v druhém jazyce.
| Angličtina (výchozí) | Čeština | |
|---|---|---|
| adresa | https://svejklab.dev/ | https://svejklab.dev/cs/ |
| obsah | content/en/ | content/cs/ |
| texty šablony | i18n/en.toml | i18n/cs.toml |
| úvodní tabulky | data/status_en.yaml | data/status_cs.yaml |
defaultContentLanguage = "en"
defaultContentLanguageInSubdir = false # angličtina na /, čeština na /cs/Hlavička stránek obsahuje odkazy hreflang, takže vyhledávače vědí, že jde o jazykové verze téže stránky.
Image
Dockerfile má dvě fáze:
- build: Alpine stáhne Hugo z GitHubu, ověří jeho SHA256 a vyrobí z Markdownu statický web (
hugo --minify --gc), - runtime: hotové HTML se zkopíruje do
nginx-unprivileged, který běží bez root práv na portu 8080.
Výsledný image obsahuje jen nginx a statické soubory, žádný Hugo ani zdrojové texty.
nginx (nginx/default.conf):
/healthzpro readiness a liveness probe,- bezpečnostní hlavičky:
Content-Security-Policy(jen vlastní skripty a styly),X-Content-Type-Options,Referrer-Policy,Permissions-Policy, - CSS a JS mají v názvu otisk obsahu (fingerprint), takže se cachují natrvalo. HTML se vždy ověřuje (
no-cache).
Bezpečnost podu
| Nastavení | Proč |
|---|---|
namespace pod-security…/enforce: restricted | cluster odmítne pod, který nesplňuje přísný profil |
runAsNonRoot, uid 101 | nginx neběží jako root |
readOnlyRootFilesystem + emptyDir /tmp | kontejner nemůže měnit svůj systém souborů |
capabilities: drop ALL, allowPrivilegeEscalation: false | žádná zvláštní práva jádra |
seccompProfile: RuntimeDefault | omezená systémová volání |
automountServiceAccountToken: false | pod nemá token ke Kubernetes API |
imagePullSecrets: ghcr-pull | image se stahuje ze soukromého registru |
| requests/limits | bez swapu je limit paměti nutnost |
readiness/liveness na /healthz | Kubernetes pozná, že web žije |
rolling update s maxUnavailable: 0 | nová verze bez výpadku |
Úprava obsahu
Obsah se upravuje v docs-site/site/content/{en,cs}/ a nasazuje přes git push, viz CI/CD a GitOps. Lokální náhled:
cd ~/Projects/homelab/docs-site/site && hugo serverPřístup
- veřejně: https://svejklab.dev přes Cloudflare Tunnel, viz Veřejný web,
- v domácí síti:
http://<ip-serveru>/(Ingress má pravidlo bez hostu).