← All docs
Reference

Deploy targets

The build pipeline emits artifacts; deploy targets decide where those artifacts go. One env var (STATICOWL_DEPLOY_TARGET) flips between four modes. Operators can run any combination side-by-side.

Deploy targets

The build pipeline emits artifacts; deploy targets decide where those artifacts go. One env var (STATICOWL_DEPLOY_TARGET) flips between four modes. Operators can run any combination side-by-side.

At a glance

Target Output Hosting model Use case
static-paths (default) Plain HTML/CSS/JS at predictable URLs in S3 Self-host: S3+CF, Netlify, Vercel, GitHub Pages, your own nginx "Host anywhere" — the front-door promise
manifest-pointer Content-addressed artifacts + sharded manifest + L@E resolver Managed atomic-release CDN Atomic blue/green deploys + audit-grade replay
github HTML + JSON sidecars + _meta/ to a Git repo you own GitHub Pages / Cloudflare Pages / Vercel auto-publish on push "Your content + metadata in your repo"
both static-paths + manifest-pointer simultaneously Both Transition / dual-shipping

How to choose

Need Mode
Marketing site, indie project, personal blog static-paths
"Host on S3/CloudFront myself, never touch anything" static-paths
Multi-locale, large catalogue, frequent updates static-paths (it works)
"I want my content + metadata in a Git repo I own" github
Auto-deploy via GitHub Pages / Cloudflare Pages / Vercel github
Provable lock-in escape hatch for compliance / procurement github
Atomic deploy + instant rollback + cryptographic audit manifest-pointer
Regulated industry (finance, pharma, gov) manifest-pointer
Both the static export and the managed CDN both

static-paths (default)

What it writes

{outputDir}/
  index.html                     ← root page
  blog/post-1/index.html
  api/articles.json              ← rendered API endpoints (if defined)
  rss.xml
  sitemap.xml
  assets/main.css

{outputDir} is per-site, per-env. The CMS server's deployToS3() then syncs the directory to:

Env vars

Var Required Default Purpose
STATICOWL_DEPLOY_TARGET yes static-paths Set to static-paths (or both)
STATICOWL_DEPLOYMENT_MODEL no new legacy reads envPublishedAt_<env> scalars; new reads from active Release; dual writes both during transition
STATICOWL_REGION no us-east-1 AWS region for the bucket

Strengths

Trade-offs


manifest-pointer (managed atomic-release)

What it writes

s3://staticowl-artifacts/
  by-hash/<sha256>.<ext>           ← every artifact, content-addressed

s3://staticowl-manifests/
  <siteId>/<deploymentId>/
    manifest-root.json             ← totals, errors, hashAlgo
    shards/00.json … ff.json       ← 256 shards, xxhash32(path) → entry
  <siteId>/current/
    <envId>.json                   ← THE POINTER — single S3 write = deploy

s3://staticowl-buildlogs/
  <siteId>/<deploymentId>/
    log.ndjson                     ← full build log
    summary.json                   ← top-level summary

A Lambda@Edge resolver in front of CloudFront reads the pointer per-request, looks up the right shard, and serves the artifact. See architecture.md → manifest-pointer for the full flow.

Env vars

Var Required Default Purpose
STATICOWL_DEPLOY_TARGET yes Set to manifest-pointer (or both)
STATICOWL_ARTIFACTS_BUCKET yes staticowl-artifacts Where content-addressed artifacts live
STATICOWL_MANIFESTS_BUCKET yes staticowl-manifests Where the manifest + pointer live
STATICOWL_BUILDLOGS_BUCKET yes staticowl-buildlogs Where build logs go
STATICOWL_REGION no us-east-1 AWS region
STATICOWL_DEPLOYMENT_MODEL yes for this target new Must be new — manifest-pointer reads from the active Release

Provisioning prerequisites

Strengths

Trade-offs


github (mirror to a customer repo)

What it writes

Pushes the compiled site + JSON sidecars + global metadata to a GitHub repo via git:

/index.html
/blog/post-1/index.html

/_meta/pages/index.json
/_meta/pages/blog/post-1.json
/_meta/types.json                  ← content-type definitions
/_meta/theme.json                  ← site theme
/_meta/routes.json                 ← route map (when present)
/_meta/release.json                ← this deploy: releaseId, deploymentId, validFrom, env

One commit per deploy. Push-only — we never read the repo back, so there's no conflict-resolution path.

Per-page sidecar JSON shape

For each page artifact at /article/my-post/index.html, the sidecar lives at /_meta/pages/article/my-post.json:

{
  "id": "article:my-post",
  "slug": "my-post",
  "title": "My Post",
  "contentType": "article",
  "path": "/article/my-post/index.html",
  "fields": { ... all field values ... },
  "references": { "author": ["author:alice"], ... },
  "deployedAt": "2026-04-28T12:00:00.000Z"
}

The customer can fork the repo and reconstruct the site from these sidecars — that's the lock-in escape hatch.

Env vars

Var Required Default Purpose
STATICOWL_DEPLOY_TARGET yes Set to github
STATICOWL_GITHUB_TOKEN yes PAT or fine-grained token with contents: write on the target repo
STATICOWL_GITHUB_REPO yes owner/name shape (e.g., octocat/my-site)
STATICOWL_GITHUB_BRANCH no main Target branch
STATICOWL_GITHUB_AUTHOR_NAME no StaticOwl Commit author name
STATICOWL_GITHUB_AUTHOR_EMAIL no bot@staticowl.com Commit author email

Bonus: free hosting via GitHub-watching services

The GitHub deploy target unlocks free hosting from anyone who watches a Git repo:

Customers get web hosting without us building anything else.

Strengths

Trade-offs

Auth + secrets safety

Token is supplied via STATICOWL_GITHUB_TOKEN. The publisher embeds it in the remote URL as https://x-access-token:TOKEN@github.com/.... If git fails, the stderr would naturally include this URL with the token visible — we redact it before logging or throwing. Verified in tests.


both

Runs static-paths and manifest-pointer simultaneously. Useful for:

The github target is independent of both — to ship to GitHub plus one of the other targets, you'd need to extend the predicate logic. For now, github is its own mode.


Deployment-model kill switch

Separate from STATICOWL_DEPLOY_TARGET is STATICOWL_DEPLOYMENT_MODEL — which controls whether the build pipeline reads from the new Deployment fact model or the legacy envPublishedAt_<envId> scalars:

Mode Behavior
new (default) Read from the active Release; write only Deployment facts
dual Read from legacy scalars; write BOTH legacy scalars AND Deployment facts; log divergences
legacy Read + write only legacy scalars; no new model touchpoints

dual is the transition mode. New customers should use new. legacy is the safe rollback target if the new model has bugs. See ADR-0019 for the full migration rationale.


See also