🏗️ Understanding Kirby’s Data Architecture for Sustainable Structural Enhancements
📌 Introduction
Kirby CMS is a file-based content management system designed for flexibility, speed, and developer control. While its “no-database” approach offers simplicity, it also demands that structural changes—such as adding new fields, modifying blueprints, or altering content models—be handled with a methodical and sustainable process.
This article will walk you through the foundations of Kirby’s data architecture, the best practices for implementing structure changes, and the strategies to avoid breaking existing content while enhancing your site’s capabilities.
1️⃣ The Core Principles of Kirby’s Data Model
1.1. File-Based Storage 📂
- No SQL database — Kirby stores content in text files (
.txtby default, sometimes.md) alongside your page folders. - Each folder represents a Page in Kirby’s content hierarchy.
Example structure:
content/
home/
home.txt
projects/
project-a/
project-a.txt
project-b/
project-b.txt
1.2. Blueprint-Driven Fields 📝
- Blueprints define what fields a page, file, or user has.
- Changes in blueprints directly influence the Panel UI but do not retroactively remove data—important for backwards compatibility.
Example (blueprints/pages/project.yml):
title: Project
fields:
description:
type: textarea
date:
type: date
1.3. Uniform YAML + Content Separation 🗂️
- Structure files (
.yml) define logic, while content files (.txt) store actual values. - This separation makes version control easier and keeps the structure portable.
2️⃣ The Hierarchy of Kirby Entities
2.1. Pages 📄
- Core building blocks of content.
- Can be nested to create hierarchies.
- Defined in
/content/and linked to a blueprint in/site/blueprints/pages/.
2.2. Files 🖼️
- Attachments to pages.
- Can have their own metadata (
.txtfile with the same name as the file).
2.3. Fields 🏷️
- Basic data units in blueprints: text, number, date, structure, etc.
2.4. Users 👤
- Have their own blueprints and data structure in
/site/accounts/.
2.5. Site 🌍
- Global container for settings and shared fields (
site.txt+blueprints/site.yml).
3️⃣ Best Practices for Enhancing Structure
3.1. Plan First, Change Later 🧠
- Always map existing structure before making changes.
- Use Kirby’s Panel and content folder inspection to identify dependencies.
3.2. Backward Compatibility 🔄
- Never remove a field abruptly—mark it as deprecated in the blueprint, keep it hidden, then phase it out.
Example:
deprecated_field:
label: Deprecated
type: text
help: "This field is no longer used."
width: 1/1
when:
role: admin
3.3. Test Changes in a Staging Environment 🧪
- Use a staging copy of
/content/and/site/before deploying to production. - This avoids irreversible content overwrites.
3.4. Version Control Your Blueprints 📜
- Store
/site/blueprints/in Git to track changes. - Commit both blueprint changes and content migrations together.
4️⃣ Safe Process for Implementing a Structural Change
Step 1: Audit Current Structure 🔍
- List existing blueprints and their fields.
- Identify content types in
/content/.
Step 2: Define the Enhancement Goal 🎯
Example: Add “Icon” to Tags for projects.
Step 3: Update the Blueprint 🛠️
Add the new field:
icon:
type: files
multiple: false
layout: cards
image:
cover: true
Step 4: Update Panel Usage Guidelines 📑
- Document where and how editors should use the new field.
Step 5: Run Content Validation ✅
- Open existing pages in the Panel to ensure they load without errors.
- Add default values where needed.
Step 6: Deploy & Monitor 📡
- Push changes to production.
- Monitor logs for YAML parsing errors or missing field references.
5️⃣ Common Pitfalls to Avoid
- ❌ Overwriting content files without backups.
- ❌ Renaming field keys without a migration plan.
- ❌ Forgetting to adapt snippets/templates to new fields.
- ❌ Not documenting changes, making future maintenance harder.
6️⃣ Migration Strategies for Larger Changes
- YAML-to-YAML Conversion Scripts: For mass field renaming.
- Content Parsing Tools: Read
.txtfiles and update keys. - Incremental Deployment: Roll out changes to a subset of pages first.
🏁 Conclusion
Kirby’s flexibility is one of its biggest advantages—but with great flexibility comes great responsibility. By understanding the underlying data architecture, following best practices, and implementing changes with a clear migration plan, you ensure that your enhancements are not only functional but also future-proof.
Well-planned structure changes empower your site to grow without introducing unnecessary complexity or risking data loss.
🔗 Possible Slugs
kirby-data-architecture-structural-enhancementskirby-blueprints-best-practicesmanaging-kirby-structure-changeskirby-architecture-guidesafe-kirby-structure-updates
If you want, I can also prepare a Kirby-specific "structure change checklist" in Markdown that you can reuse internally before making modifications. That would make this even more practical for you.
Here’s a Kirby-specific Structure Change Checklist you can copy into your repo wiki or Notion. It’s practical, step‑by‑step, and designed for safe, reversible changes. ✅
🧱 Kirby Structure Change Checklist
Use this when adding/removing fields, changing blueprints, or reorganizing content folders.
0) Header (fill before you start) 🗂️
- Change Title:
- Owner:
- Date:
- Goal / Outcome (1–2 lines):
- Risk Level: Low / Medium / High
- Affected Areas: Pages / Files / Snippets / Controllers / Blocks / Panel UI / API
- Issue / Ticket Link:
1) Discovery & Audit 🔍
- Inventory blueprints involved (
/site/blueprints/pages|files|site|users). - Inventory templates/snippets (
/site/templates,/site/snippets) that read/write target fields. - Map content tree impacted (
/content/**/...txt). - Identify Panel flows (who edits what, where).
- Check for programmatic access (Controllers, API endpoints, hooks, plugins).
- Confirm translations or multilang behavior (per-language content files).
- Note any Structure, Blocks, or Layout fields referencing the change.
Tip: keep a quick “field usage log” (grep or ripgrep):
rg -n "my_old_field|my_new_field" site/ content/ --hidden
2) Design the Change 🧭
-
Choose field type and options (validation, default, required, width, etc.).
-
Decide backward compatibility:
- Keep old field as hidden/deprecated temporarily.
- Provide default values for existing content.
- Plan migration script if renaming or changing types.
-
Decide Panel UX (positions, groupings, help texts).
-
Update information architecture (if adding a new page type or folder structure).
Blueprint draft (example):
# site/blueprints/pages/project.yml
title: Project
tabs:
content:
label: Content
fields:
title:
type: text
required: true
tags:
type: tags
placeholder: Add tags
icon: # NEW
type: files
multiple: false
layout: cards
image:
ratio: 1/1
cover: true
description:
type: textarea
size: medium
meta:
label: Meta
fields:
deprecated_icon: # OLD (hidden)
type: text
when:
role: admin
help: "Deprecated. Will be removed after migration."
3) Safety Nets 🛟
- Git branch created (
feature/kirby-structure-change-xxx). - Full backup of
/content(zip or copy). - Local staging with production content copy.
- Define Rollback Plan (how to revert blueprints, snippets, and content).
- Define Success Criteria (what must work to call it done).
4) Implement Incrementally 🧑💻
- Add new fields alongside old ones (don’t delete yet).
- Add Panel help/placeholders to guide editors.
- Update snippets/templates to be tolerant:
<?= $page->icon()->isNotEmpty() ? $page->icon()->toFiles()->first()?->url() : '' ?>
- Guard access in code (avoid fatal calls when empty/absent).
- Add feature flag or config toggle if needed.
5) Migration Plan & Tools 🧰
Pick your approach:
Option A: Manual via Panel (small sites)
- Open affected pages; populate new field(s).
- Verify previews and frontend output.
Option B: Scripted (recommended for medium/large)
- Write a one-off CLI script (Kirby bootstrap) to copy/transform values.
- Dry-run on a subset of content.
- Log successes/failures.
Minimal PHP bootstrap (example):
<?php
// scripts/migrate-icon.php
require __DIR__ . '/../kirby/bootstrap.php';
$kirby = new Kirby(['roots' => ['index' => dirname(__DIR__)]]);
$pages = $kirby->site()->index()->filterBy('template', 'project');
foreach ($pages as $page) {
// Example: move deprecated_icon (text) -> icon (file relation not set here; adapt as needed)
if ($page->content()->has('deprecated_icon') && $page->deprecated_icon()->isNotEmpty()) {
// Write new field (simple text copy example)
$page->update([
'icon' => $page->deprecated_icon()->value()
]);
echo "Migrated: " . $page->id() . PHP_EOL;
}
}
Field rename in raw content (advanced; be careful):
# Back up first!
cp -a content content_backup_$(date +%F)
# Example: rename key in txt files (key: value) — adjust regex to your format
rg -l "^deprecated_icon:" content | xargs -I{} sed -i '' 's/^deprecated_icon:/icon:/g' {}
⚠️ Validate encoding and line endings. Test on a small sample first.
6) QA Checklist ✅
- Panel: pages load; no missing-field errors.
- Create/Edit flows: new fields behave as expected (required, validation).
- Frontend: snippets/templates render without notices.
- Multilang: language switching preserves values.
- Search / Collections: queries still match expected pages.
- API / Webhooks (if any): responses unchanged or documented changes accepted.
- Images/Files: file blueprints & metadata unaffected.
- Performance: no regressions on heavy pages.
7) Documentation & Training 📝
- Update README.md or /docs/structure-changes.md.
- Note deprecated fields and removal date.
- Provide editor cheatsheet (what’s new, where to click).
- Update content model diagram (optional but helpful).
8) Deploy & Monitor 🚀
- Merge PR and deploy blueprints + code.
- Do not delete old fields yet (grace period).
- Monitor error logs (PHP, Panel) for 24–72 hours.
- Fix any unexpected edge cases.
9) Decommission Deprecated Fields 🧹
(After grace period + confirmation)
- Remove deprecated fields from blueprints.
- Remove fallback code paths from snippets/templates/controllers.
- Final content cleanup (optional): strip obsolete keys from
.txt. - Close ticket and tag release in Git.
10) Reference Templates & Snippets 📎
Blueprint field with defaults & validation
priority:
type: number
min: 1
max: 10
default: 5
width: 1/3
help: "1 = highest priority"
Conditional (admin-only) deprecated field
old_field:
type: text
label: Deprecated
when:
role: admin
help: "Do not use. Will be removed on YYYY-MM-DD."
Snippet: robust reading with fallback
<?php
$value = $page->newField()->or($page->oldField())->value();
if (!empty($value)) {
echo esc($value);
}
Commit choreography (suggested)
git checkout -b feature/kirby-structure-<slug>
# 1) Add new fields + tolerant templates
git commit -am "feat: add new fields and tolerant rendering"
# 2) Migration scripts + docs
git commit -am "chore: add migration script and docs"
# 3) Post-migration cleanup (later)
git commit -am "refactor: remove deprecated fields and fallbacks"
Rollback Plan (fill this too) 🔁
- How to revert blueprints:
- How to revert templates/snippets:
- How to restore content: point to backup path & steps
- How to revert deployment: tag/commit reference
Acceptance Criteria 🎯
- All affected page types editable in Panel with no errors.
- New data visible where expected on frontend.
- Old fields not referenced in runtime paths (or only in admin views).
- No 4xx/5xx spikes post‑deploy.
- Stakeholders sign‑off.
Optional: “Mini” Checklists by Change Type
Add a new field
- Blueprint updated
- Panel help text added
- Template fallback in place
- Docs updated
Rename a field
- New + old coexist
- Migration script tested
- Templates tolerate both
- Logs clean after migration
- Old removed after grace period
Change field type
- Data conversion path defined
- Lossy conversions documented
- Editor guidance provided
Paste‑Ready Task Template (Short Form)
Title: <Change name>
Goal: <What improves?>
Scope: <Blueprints, templates, content paths>
Risk: Low/Med/High
Plan:
- Add fields: <list>
- Keep deprecated: <list>
- Migrate: <script/manual>
Safety:
- Backup: done
- Branch: <name>
- Rollback: <steps>
QA:
- Panel OK
- Frontend OK
- Multilang OK
- Logs clean
Docs:
- README updated
- Editors informed
Decommission date: <YYYY-MM-DD>
If you’d like, I can also convert this into a Notion template or a /docs/kirby-structure-change.md file tailored to your repo.