Skip to content

Commit c856aff

Browse files
cbrobergclaude
andcommitted
docs: admin data location — resolution order + volume setup per platform
New doc entry (EN + DA) explaining where cms-admin stores admin-server data (registry, users, access-tokens, device tokens, beam sessions, goto links) and how to configure it so container restarts don't wipe it. Covers: - The two kinds of data (admin-server vs per-site) and why the line matters - Resolution order: WEBHOUSE_DATA_DIR > /data/cms-admin auto-detect > XDG > $HOME/.webhouse/cms-admin > legacy _admin/ (pre-0.2.18) - Per-platform setup: local dev, Docker, Fly, Kubernetes, bare-metal tarball + systemd - Verification steps to confirm the right directory is in use - What breaks if you skip the volume mount in production - Migration path from pre-0.2.18 deployments Category: deployment, order 5. Slugs: admin-data-location (EN) / admin-data-location-da (DA), sharing translationGroup 40c73852…. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
1 parent 5dcc486 commit c856aff

2 files changed

Lines changed: 44 additions & 0 deletions

File tree

Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,22 @@
1+
{
2+
"id": "2c9c9815-37d9-45c2-8120-d2095606ca47",
3+
"slug": "admin-data-location-da",
4+
"status": "published",
5+
"locale": "da",
6+
"translationGroup": "40c73852-3fdf-4d8b-8984-69255de0533b",
7+
"data": {
8+
"title": "Placering af admin-data",
9+
"description": "Hvor cms-admin gemmer registry, brugere, access-tokens og anden server-level tilstand — og hvordan du konfigurerer det for dev, Docker, Fly og Kubernetes så det overlever genstarter.",
10+
"content": "## De to slags data\n\ncms-admin er en **server**. Sites som den administrerer er **klienter**. Den linje afgør hvor hvert stykke data lever:\n\n- **Admin-server data** — deles på tværs af alle sites: registry over orgs og sites, brugerkonti, access-tokens, push device-tokens, beam transfer-sessions, goto deep-link-shortcuts, agent-templates, org-level settings. Intet af dette hører hjemme inde i et enkelt sites mappe.\n- **Per-site data** — tilhører ét site og rejser med det: content JSON, site-config, team-medlemskab for dét site, analytics, lighthouse-rapporter, formular-submissions, brand voice, deploy-log, planlagte events. Lever inde i sitets egen mappe, så når sitet flyttes til en anden server følger dets state med.\n\nDenne doc handler om **admin-server**-halvdelen — hvor den gemmes, og hvordan du sikrer at den overlever genstarter på alle platforme.\n\n## Resolution-orden\n\n`getAdminDataDir()` tjekker disse i rækkefølge og bruger den første der findes:\n\n| # | Prioritet | Placering | Bruges af |\n|---|-----------|-----------|-----------|\n| 1 | Override | `$WEBHOUSE_DATA_DIR` | Production — Docker, Fly, Kubernetes (sat eksplicit i Dockerfile / fly.toml / pod spec) |\n| 2 | Auto-detect | `/data/cms-admin/` hvis writable | Containers hvor deploy-laget har mounted et persistent volume der (ingen env-variabel nødvendig) |\n| 3 | Linux-standard | `$XDG_DATA_HOME/webhouse-cms/` | Linux-dev med XDG eksplicit |\n| 4 | XDG default | `$HOME/.local/share/webhouse-cms/` | Linux-dev default |\n| 5 | Simpel fallback | `$HOME/.webhouse/cms-admin/` | macOS + Linux udviklermaskiner |\n| 6 | Legacy | `{CMS_CONFIG_PATH-parent}/_admin/` | Kun pre-0.2.18 deployments — auto-migreret ved første boot med 0.2.18+ |\n\nOverride'en vinder altid. I produktion bør du **altid** enten sætte `WEBHOUSE_DATA_DIR` eksplicit eller mount'e et persistent volume ved `/data/cms-admin` — ellers tørrer container-genstarter registry, brugere og access-tokens.\n\n## Opsætning platform for platform\n\n### Lokal udvikling (macOS, Linux)\n\nIntet at konfigurere — cms-admin opretter `$HOME/.webhouse/cms-admin/` ved første boot og skriver der. For at flytte det eksplicit:\n\n```bash\nexport WEBHOUSE_DATA_DIR=/opt/webhouse-cms\nmkdir -p $WEBHOUSE_DATA_DIR\npnpm dev\n```\n\n### Docker (plain)\n\nDe leverede Dockerfiles sætter `WEBHOUSE_DATA_DIR=/data/cms-admin` og forventer et volume mountet der:\n\n```bash\ndocker run \\\n -p 3010:3010 \\\n -v $(pwd)/cms-admin-data:/data/cms-admin \\\n -v $(pwd)/my-site:/site \\\n ghcr.io/webhousecode/cms-admin\n```\n\nMed `docker compose` deklarerer `docker-compose.yml` allerede et `cms_admin_data` named volume:\n\n```yaml\nvolumes:\n - cms_admin_data:/data/cms-admin\nvolumes:\n cms_admin_data:\n```\n\n`docker compose down` efterfulgt af `docker compose up` genbruger volumen; `docker compose down -v` sletter det.\n\n### Fly.io\n\n`deploy/fly.toml` kobler allerede `cms_data`-volumen til `/data` og sætter `WEBHOUSE_DATA_DIR=/data/cms-admin`. Redeploy er sikkert — volumen overlever en hvilken som helst enkelt maskine:\n\n```toml\n[env]\n WEBHOUSE_DATA_DIR = \"/data/cms-admin\"\n\n[mounts]\n source = \"cms_data\"\n destination = \"/data\"\n```\n\nFørste-gangs volume-opsætning (én gang pr. region):\n\n```bash\nfly volumes create cms_data --region arn --size 1\nfly deploy\n```\n\n### Kubernetes\n\nBrug et `PersistentVolumeClaim` og mount det ved `/data/cms-admin`. Container-image'et auto-detecter `/data/cms-admin` når det er writable, så du behøver strengt taget ikke `WEBHOUSE_DATA_DIR` hvis du mount'er der — men det er god skik at sætte det eksplicit:\n\n```yaml\nenv:\n - name: WEBHOUSE_DATA_DIR\n value: /data/cms-admin\nvolumeMounts:\n - name: admin-data\n mountPath: /data/cms-admin\nvolumes:\n - name: admin-data\n persistentVolumeClaim:\n claimName: cms-admin-data\n```\n\n### Bare-metal Linux (tarball-install)\n\nDen ugentlige tarball indeholder kun kode — ingen runtime-data. Installations-flow:\n\n```bash\ntar xf webhouse-cms-0.2.18.tar.gz -C /opt/\nuseradd --system --home /var/lib/webhouse-cms webhouse\nmkdir -p /var/lib/webhouse-cms\nchown webhouse:webhouse /var/lib/webhouse-cms\n\n# /etc/systemd/system/webhouse-cms.service\n[Service]\nUser=webhouse\nEnvironment=\"WEBHOUSE_DATA_DIR=/var/lib/webhouse-cms\"\nEnvironment=\"CMS_CONFIG_PATH=/srv/site/cms.config.ts\"\nWorkingDirectory=/opt/webhouse-cms/packages/cms-admin\nExecStart=/usr/bin/node server.js\n```\n\n## Verifikation af opsætningen\n\nEfter første boot skal `$WEBHOUSE_DATA_DIR` indeholde:\n\n```\nregistry.json # alle orgs + sites\n_data/\n users.json # brugerkonti\n access-tokens.json # API-tokens\n device_tokens.json # push-modtagere\n invitations.json # ventende invitationer\n org-settings/\n <orgId>.json\n agent-templates/\n <orgId>/\n beam-sessions/ # cross-site beam-overførsler\nbeam-sites/ # beam-import staging\ngoto-links.json # /admin/goto/<id> short links\n```\n\nFor at bekræfte at cms-admin læser fra det rette sted:\n\n```bash\ncurl -sk https://localhost:3010/api/cms/registry \\\n -H \"Cookie: <din-session>\" | jq '.registry.orgs | length'\n```\n\nAntallet skal matche hvad der står i `$WEBHOUSE_DATA_DIR/registry.json`.\n\n## Hvad sker der når mappen er flygtig\n\nHvis du kører cms-admin i Docker/Fly/Kubernetes **uden** at mount'e et volume ved `/data/cms-admin`:\n\n1. Første boot: cms-admin opretter registry.json osv. i containerens lokale filsystem.\n2. Du logger ind, opretter orgs/sites, minter tokens.\n3. Containeren genstarter (redeploy, OOM kill, host reboot).\n4. Registry + brugere + tokens er væk. Du er tilbage til en frisk installation.\n\n0.2.18+'s resolution-orden prøver hårdt på at fange det — auto-detecter `/data/cms-admin` når det findes, og falder tilbage til `$HOME` ellers — men en container uden hverken mount eller env-variabel vil stadig starte, og vil stadig miste data. Verificér altid at dit volume er mounted før du inviterer rigtige brugere.\n\n## Migration fra pre-0.2.18\n\nFør 0.2.18 levede admin-server-data i `{CMS_CONFIG_PATH-parent}/_admin/` og nogle filer i `{CMS_CONFIG_PATH-parent}/_data/`. Ved første boot med 0.2.18+, hvis den nye placering er tom og den gamle har en `registry.json`, læser cms-admin transparent fra den gamle sti — intet går i stykker.\n\nFor at migrere permanent:\n\n```bash\nmkdir -p $WEBHOUSE_DATA_DIR/_data\ncp -R /sti/til/bootstrap-site/_admin/. $WEBHOUSE_DATA_DIR/\ncp /sti/til/bootstrap-site/_data/users.json $WEBHOUSE_DATA_DIR/_data/\ncp /sti/til/bootstrap-site/_data/device_tokens.json $WEBHOUSE_DATA_DIR/_data/\ncp /sti/til/bootstrap-site/_data/access-tokens.json $WEBHOUSE_DATA_DIR/_data/\ncp -R /sti/til/bootstrap-site/_data/beam-sessions $WEBHOUSE_DATA_DIR/_data/\ncp -R /sti/til/bootstrap-site/.beam-sites $WEBHOUSE_DATA_DIR/beam-sites\n```\n\nEfter migration lad gerne de legacy-stier blive stående én release som rollback-sikkerhed, slet dem så når du har bekræftet at alt virker.\n\n## Se også\n\n- [Docker deployment](/docs/docker-deployment-da) — fuld Docker + Fly opsætnings-gennemgang.\n- [Deployment oversigt](/docs/deployment-da) — alle deploy-targets.",
11+
"category": "deployment",
12+
"order": 5,
13+
"_seo": {
14+
"metaTitle": "Placering af admin-data — webhouse.app Docs",
15+
"metaDescription": "Hvor cms-admin gemmer registry, brugere og access-tokens. Resolution-orden for Docker, Fly, Kubernetes og lokal dev — med volume + env-variabel opsætning.",
16+
"keywords": ["webhouse", "cms", "admin", "deployment", "docker", "fly", "kubernetes", "persistent volume", "data-mappe"]
17+
}
18+
},
19+
"_fieldMeta": {},
20+
"createdAt": "2026-04-20T00:00:00.000Z",
21+
"updatedAt": "2026-04-20T00:00:00.000Z"
22+
}
Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,22 @@
1+
{
2+
"id": "38130724-e598-4051-85c2-20048720f30d",
3+
"slug": "admin-data-location",
4+
"status": "published",
5+
"locale": "en",
6+
"translationGroup": "40c73852-3fdf-4d8b-8984-69255de0533b",
7+
"data": {
8+
"title": "Admin data location",
9+
"description": "Where cms-admin stores registry, users, access-tokens, and other server-level state — and how to configure it for dev, Docker, Fly, and Kubernetes so it survives restarts.",
10+
"content": "## The two kinds of data\n\ncms-admin is a **server**. Sites it manages are **clients**. That line determines where each piece of data lives:\n\n- **Admin-server data** — shared across every site: registry of orgs and sites, user accounts, access-tokens, push device tokens, beam transfer sessions, goto deep-link shortcuts, agent templates, org-level settings. None of this belongs inside any one site's folder.\n- **Per-site data** — belongs to one site and travels with it: content JSON, site-config, team membership for that site, analytics, lighthouse reports, form submissions, brand voice, deploy log, scheduled events. Lives inside the site's own folder so moving the site to another server takes its state along.\n\nThis doc is about the **admin-server** half — where it goes and how to make sure it persists across restarts on every platform.\n\n## Resolution order\n\n`getAdminDataDir()` checks these in order and uses the first one that resolves:\n\n| # | Priority | Location | Used by |\n|---|----------|----------|---------|\n| 1 | Override | `$WEBHOUSE_DATA_DIR` | Production — Docker, Fly, Kubernetes (set explicitly in Dockerfile / fly.toml / pod spec) |\n| 2 | Auto-detect | `/data/cms-admin/` if writable | Containers where the deploy layer has mounted a persistent volume there (no env var needed) |\n| 3 | Linux standard | `$XDG_DATA_HOME/webhouse-cms/` | Linux dev with XDG explicit |\n| 4 | XDG default | `$HOME/.local/share/webhouse-cms/` | Linux dev default |\n| 5 | Simple fallback | `$HOME/.webhouse/cms-admin/` | macOS + Linux developer machines |\n| 6 | Legacy | `{CMS_CONFIG_PATH-parent}/_admin/` | Pre-0.2.18 deployments only — auto-migrated on first boot with 0.2.18+ |\n\nThe override always wins. In production you should **always** either set `WEBHOUSE_DATA_DIR` explicitly or mount a persistent volume at `/data/cms-admin` — otherwise container restarts wipe the registry, users, and access-tokens.\n\n## Platform-by-platform setup\n\n### Local development (macOS, Linux)\n\nNothing to configure — cms-admin creates `$HOME/.webhouse/cms-admin/` on first boot and writes there. To move it explicitly:\n\n```bash\nexport WEBHOUSE_DATA_DIR=/opt/webhouse-cms\nmkdir -p $WEBHOUSE_DATA_DIR\npnpm dev\n```\n\n### Docker (plain)\n\nThe shipped Dockerfiles set `WEBHOUSE_DATA_DIR=/data/cms-admin` and expect a volume mount there:\n\n```bash\ndocker run \\\n -p 3010:3010 \\\n -v $(pwd)/cms-admin-data:/data/cms-admin \\\n -v $(pwd)/my-site:/site \\\n ghcr.io/webhousecode/cms-admin\n```\n\nWith `docker compose`, `docker-compose.yml` already declares a `cms_admin_data` named volume:\n\n```yaml\nvolumes:\n - cms_admin_data:/data/cms-admin\nvolumes:\n cms_admin_data:\n```\n\n`docker compose down` followed by `docker compose up` reuses the volume; `docker compose down -v` wipes it.\n\n### Fly.io\n\n`deploy/fly.toml` already attaches the `cms_data` volume at `/data` and sets `WEBHOUSE_DATA_DIR=/data/cms-admin`. Redeploy is safe — the volume outlives any one machine:\n\n```toml\n[env]\n WEBHOUSE_DATA_DIR = \"/data/cms-admin\"\n\n[mounts]\n source = \"cms_data\"\n destination = \"/data\"\n```\n\nFirst-time volume setup (once per region):\n\n```bash\nfly volumes create cms_data --region arn --size 1\nfly deploy\n```\n\n### Kubernetes\n\nUse a `PersistentVolumeClaim` and mount it at `/data/cms-admin`. The container image auto-detects the `/data/cms-admin` path when writable, so you don't strictly need `WEBHOUSE_DATA_DIR` if you mount there — but it's a good habit to set it explicitly:\n\n```yaml\nenv:\n - name: WEBHOUSE_DATA_DIR\n value: /data/cms-admin\nvolumeMounts:\n - name: admin-data\n mountPath: /data/cms-admin\nvolumes:\n - name: admin-data\n persistentVolumeClaim:\n claimName: cms-admin-data\n```\n\n### Bare-metal Linux (tarball install)\n\nThe weekly tarball contains code only — no runtime data. Install flow:\n\n```bash\ntar xf webhouse-cms-0.2.18.tar.gz -C /opt/\nuseradd --system --home /var/lib/webhouse-cms webhouse\nmkdir -p /var/lib/webhouse-cms\nchown webhouse:webhouse /var/lib/webhouse-cms\n\n# /etc/systemd/system/webhouse-cms.service\n[Service]\nUser=webhouse\nEnvironment=\"WEBHOUSE_DATA_DIR=/var/lib/webhouse-cms\"\nEnvironment=\"CMS_CONFIG_PATH=/srv/site/cms.config.ts\"\nWorkingDirectory=/opt/webhouse-cms/packages/cms-admin\nExecStart=/usr/bin/node server.js\n```\n\n## Verifying the configuration\n\nAfter first boot, `$WEBHOUSE_DATA_DIR` should contain:\n\n```\nregistry.json # all orgs + sites\n_data/\n users.json # user accounts\n access-tokens.json # API tokens\n device_tokens.json # push targets\n invitations.json # pending invites\n org-settings/\n <orgId>.json\n agent-templates/\n <orgId>/\n beam-sessions/ # cross-site beam transfers\nbeam-sites/ # beam-import staging\ngoto-links.json # /admin/goto/<id> short links\n```\n\nTo confirm cms-admin is reading from the right place:\n\n```bash\ncurl -sk https://localhost:3010/api/cms/registry \\\n -H \"Cookie: <your-session>\" | jq '.registry.orgs | length'\n```\n\nThe count should match what's in `$WEBHOUSE_DATA_DIR/registry.json`.\n\n## What happens when the directory is ephemeral\n\nIf you run cms-admin in Docker/Fly/Kubernetes **without** mounting a volume at `/data/cms-admin`:\n\n1. First boot: cms-admin creates registry.json etc. in the container's local filesystem.\n2. You log in, create orgs/sites, mint tokens.\n3. Container restarts (redeploy, OOM kill, host reboot).\n4. Registry + users + tokens are gone. You're back to a fresh install.\n\nThe 0.2.18+ resolution order tries hard to catch this — auto-detecting `/data/cms-admin` when present, and falling back to `$HOME` otherwise — but a container with neither mount nor env var will still start, and will still lose data. Always verify your volume is mounted before inviting real users.\n\n## Migrating from pre-0.2.18\n\nBefore 0.2.18, admin-server data lived in `{CMS_CONFIG_PATH-parent}/_admin/` and some files in `{CMS_CONFIG_PATH-parent}/_data/`. On first boot with 0.2.18+, if the new location is empty and the legacy one has a `registry.json`, cms-admin reads from the legacy path transparently — nothing breaks.\n\nTo migrate permanently:\n\n```bash\nmkdir -p $WEBHOUSE_DATA_DIR/_data\ncp -R /path/to/bootstrap-site/_admin/. $WEBHOUSE_DATA_DIR/\ncp /path/to/bootstrap-site/_data/users.json $WEBHOUSE_DATA_DIR/_data/\ncp /path/to/bootstrap-site/_data/device_tokens.json $WEBHOUSE_DATA_DIR/_data/\ncp /path/to/bootstrap-site/_data/access-tokens.json $WEBHOUSE_DATA_DIR/_data/\ncp -R /path/to/bootstrap-site/_data/beam-sessions $WEBHOUSE_DATA_DIR/_data/\ncp -R /path/to/bootstrap-site/.beam-sites $WEBHOUSE_DATA_DIR/beam-sites\n```\n\nAfter migration, leave the legacy paths in place for a release as a rollback safety net, then delete them once you've confirmed everything works.\n\n## See also\n\n- [Docker deployment](/docs/docker-deployment) — full Docker + Fly setup walkthrough.\n- [Deployment overview](/docs/deployment) — all deploy targets.",
11+
"category": "deployment",
12+
"order": 5,
13+
"_seo": {
14+
"metaTitle": "Admin Data Location — webhouse.app Docs",
15+
"metaDescription": "Where cms-admin stores registry, users, and access-tokens. Resolution order for Docker, Fly, Kubernetes, and local dev — with volume + env var setup.",
16+
"keywords": ["webhouse", "cms", "admin", "deployment", "docker", "fly", "kubernetes", "persistent volume", "data directory"]
17+
}
18+
},
19+
"_fieldMeta": {},
20+
"createdAt": "2026-04-20T00:00:00.000Z",
21+
"updatedAt": "2026-04-20T00:00:00.000Z"
22+
}

0 commit comments

Comments
 (0)