Features

Markdown

Git export sync

Optional content-as-code path: on publish, Externa can write published item files into a git worktree and push with a deploy key. For most headless sites, prefer outbound webhooks → CI rebuild instead.

When to use

NeedPrefer
Rebuild Next/Nuxt/AstroWebhooks → deploy hook
Files in a docs/content repoGit export (this page)
One-off dumpphp artisan git-export:sync --dry-run

Enable

GIT_EXPORT_ENABLED=true
GIT_EXPORT_REMOTE_URL=git@github.com:org/content.git
GIT_EXPORT_BRANCH=main
GIT_EXPORT_SSH_KEY_PATH=/var/externa/keys/content_deploy
GIT_EXPORT_FORMAT=json
GIT_EXPORT_DRY_RUN=false
GIT_EXPORT_COLLECTION_SLUGS=*

Queue a worker. Publishing a versioned item (item.published) queues ExportCollectionItemToGitJob.

The remote may be an empty bare repository: on first export Externa initializes the branch and pushes it (#182). Clones use --template=, so locked or sandboxed filesystems work (#179).

Full sync:

php artisan git-export:sync
php artisan git-export:sync --dry-run --collection=posts

Layout

content/{collection_slug}/{item_id}.json   # or .md when GIT_EXPORT_FORMAT=markdown

Threat notes

  • Use a write-limited deploy key for the content repo only — not your app monorepo.
  • Keep the private key on disk (GIT_EXPORT_SSH_KEY_PATH); do not paste keys into Project settings or git.
  • Start with GIT_EXPORT_DRY_RUN=true to verify commits without push.
  • The worker host can push to the remote — treat that host as a secrets boundary.
  • Bulk AI imports do not auto-export every row; run git-export:sync after large imports if needed.

Out of scope

  • Bidirectional sync / pull-from-git CMS
  • PR-per-publish workflows
  • Replacing webhooks for static host rebuilds