For people with custom build pipelines

Swap the authoring + compile layer.
Keep everything else.

You built a static-site generator that works. Your CDN works. Your Lambdas work. You don't want a platform migration — you want a productized version of build-engine.js. Here's how to swap StaticOwl in for the authoring + compile step without touching anything downstream.

Who this is for.

You have a working custom static-site generator. Maybe it's Hugo + a thousand lines of glue code, maybe it's 11ty + Handlebars + a bespoke content schema, maybe it's a Lambda pipeline that reads a JSON file and emits HTML to an S3 bucket. Whatever it is, you wrote it because the off-the-shelf options didn't quite fit, and you've kept it running because nothing better has shown up.

You also have everything else: a CDN with your invalidation logic, a search backend (your own Lambda or Algolia or a custom vector store), maybe gamification or an auth API or a leaderboard — services that aren't going anywhere. You don't want a platform migration. You want a productized version of build-engine.js that:

  • writes content into a database with versioning + audit you don't have today,
  • gives your team an authoring UI you didn't have to build,
  • gives you AI-assisted rewriting / image generation / bulk operations,
  • compiles to your existing output schema,
  • exports the result to your bucket,
  • and stops there.

That's the use case this page is for. The rest of this page walks through how to validate the swap on a single page or single course before you commit to the whole site.

The pattern.

Three things change. Everything else stays.

Change #1 — Source of truth. Today: a JSON file or a directory tree of Markdown files. Tomorrow: a versioned graph of content records, with an authoring UI, AI assistants, and an audit trail. You don't lose the JSON — we can emit it on every build. We just stop letting the JSON be the source of truth.

Change #2 — Build engine. Today: build-engine.js or equivalent — a script you maintain that turns content into HTML. Tomorrow: a Liquid template per content type, each rendering exactly the same shape your old script produced.

Change #3 — Deploy step. Today: aws s3 sync + a CloudFront invalidation. Tomorrow: the same thing, triggered by StaticOwl's build endpoint. You can keep using your existing deploy script if you prefer; StaticOwl's free Static Export tier just writes to your bucket.

That's it. Your auth, your search, your analytics, your gamification, your CDN, your custom Lambdas — all completely unaware that the build step changed.

The single-page pilot, step by step.

You should not commit your whole site to a swap like this without proving it on one page first. Here's the minimum-viable pilot. It takes an afternoon.

Step 1. Get a dev key (60 seconds).

Hit app.staticowl.com/signup-key.html. Email-verified, no card. You get an API key + access to the admin UI.

Step 2. Define a content type matching your existing JSON shape.

Open the admin and create a content type whose fields match your current JSON. If your build engine reads { order, topic, lessons[], quiz, search_keywords, html_content }, declare exactly that — a content type called course with six fields. No magic. You write the schema; we provide the storage + UI for it.

This is the part that surprises people: the content type system is intentionally not opinionated. We do not try to make you remodel your content. You declare your shape; we honor it.

Step 3. Paste your existing layout file into a Liquid template.

If your current layout is _layout.html or _article_details.html or whatever, paste it verbatim into a new template through the admin UI or via POST /api/templates. Then replace the spots where your build engine substitutes values with Liquid expressions:

<!-- Before -->
<h1>__TITLE__</h1>
<div class="content">__BODY__</div>

<!-- After -->
<h1>{{ title }}</h1>
<div class="content">{{ body }}</div>

The template is just HTML with curly-brace expressions. Bind it to your content type with boundType: "course" and that template is used for every course record. Multiple templates per type are supported if you need them (article vs. quiz vs. course landing, etc.).

Step 4. Import one page or one course.

Either through the admin UI (paste fields manually) or via POST /api/content/<type> with the JSON your existing pipeline already produces. The import endpoint accepts the same shape your build engine reads, so the migration script is roughly for record in lessons.json: POST it.

Step 5. Build + sync to your bucket.

Configure the site's publishConfig to point at your existing S3 bucket (the one your current pipeline already writes to). Set preserveExternal: true so we never delete files we didn't produce — your existing pages stay put. Trigger a build:

curl -X POST https://app.staticowl.com/api/build \
  -H "Authorization: Bearer $KEY" \
  -H "X-Site-Id: site:your-site" \
  -d '{"env": "dev"}'

The compile runs in StaticOwl, the artifact ships directly to your bucket, CloudFront serves it the way it always did. Total bytes changed in your infrastructure: zero.

Step 6. Open the rendered page. Verify search still works. Verify your analytics still fire. Verify the gamification API still hits the right URL.

If all that works (it should — none of those services even know StaticOwl exists), you've validated the swap on one page. Now you can decide: pilot a section, pilot a course, batch-import the rest.

Honest answers on scale.

If you're considering this with a large site (10k+ pages, 100k+ records), here are the honest numbers we have today.

  • Engine layer is benchmarked at 536K nodes / 810K edges in insurance_huge (an actual production graph). 1-hop counts in tens of milliseconds; 3-hop queries in single seconds. 22k lessons + 362 courses is small for the engine layer.
  • CMS layer has been pressure-tested in the low thousands of records (the dogfood site for staticowl.com is 65 records — small). We have not run an end-to-end CMS pilot at 20k records in a single site. We expect it to work; we have not proven it on that exact volume.
  • Import path uses the fast_import MCP tool or POST /api/import. It batches records into the engine's bulk-load procedure. Import-of-22k-records is something we'd benchmark with you before you committed.
  • Build pipeline does incremental builds: edit one page, ~5–10 files change. Full rebuild of all pages happens on cold start or after a template change. Full rebuild time scales roughly linearly with page count; we have not measured 22k-page full rebuild specifically.

What this means in practice: we'd happily benchmark your specific size before you commit. Email founders@staticowl.com with a count and a sample record; we'll run a synthetic load at that volume and send you the numbers. Hedging on the page lets us under-promise; running the actual benchmark lets us over-deliver. That's the right shape.

What you keep.

To be explicit, because the agent reviewing this pattern got the wrong impression the first time. None of these are touched by the migration:

  • Your CDN (CloudFront, Cloudflare, Fastly, anything) — keeps serving from your bucket.
  • Your search backend (Lambda + vector store, Algolia, ElasticSearch, anything) — keeps responding to your client-side queries.
  • Your gamification / leaderboard / XP / certificate APIs — same endpoints, same database.
  • Your analytics — same EventBridge / Pinpoint / Mixpanel / Plausible setup. We don't insert tracking and don't request the data.
  • Your auth — your JWT issuer, your session store, your login UI all stay where they are.
  • Your custom Lambdas at the edge or origin — invisible to us.

If you decide later you want to move some of these onto StaticOwl primitives — for instance, the engine ships BM25 + vector + HNSW so the search backend is something we could replace for you — that's a separate decision you make on your own timeline. The migration doesn't force it.

Where to go from here.

  1. Try the pilot above. One page, one content type, one template, one bucket sync. Takes an afternoon. Get a dev key.
  2. Email us your record shape and we'll do the schema mapping with you. founders@staticowl.com — usually reply same day.
  3. Read the architecture docs. /docs/architecture/ covers content types, templates, build pipeline, and the deploy/export model. Useful before you commit a migration but not required to start the pilot.
  4. Run the benchmark. If you have a specific scale concern, send us a record sample + a target count. We'll run the synthetic load and send you the numbers. Free; no commitment.

This page is itself a content record on StaticOwl. The template that renders it is the same kind of template you'd write for your site. The page is the demo.