Cloudflare Pages and Oracle Typesense
This guide documents the free hosted docs path for OpenKeep:
- Cloudflare Pages serves the static Docusaurus site
- an Oracle Cloud Always Free VM runs Typesense
- the Cloudflare GitHub App builds and deploys the docs site natively from GitHub
- GitHub Actions reindexes search after production docs changes land on
main
This matches the current docs app in apps/docs, which is a static Docusaurus build with optional Typesense-backed search.
Architecture
- Cloudflare Pages builds
apps/docsdirectly from GitHub. - Cloudflare Pages injects the browser-facing Typesense configuration into the Docusaurus build.
- Cloudflare deploys the docs site to the configured production domain.
- GitHub Actions waits for the public docs URL to become reachable after changes land on
main. - GitHub Actions scrapes the deployed docs site and refreshes the Typesense collection.
Prerequisites
- a Cloudflare account
- an Oracle Cloud account with an Always Free VM
- a Cloudflare Pages project
- a public DNS name for the docs site such as
docs.example.com - a public DNS name for Typesense such as
typesense-docs.example.com
Oracle VM Setup
Recommended shape:
- Ubuntu 24.04 ARM instance
- at least one persistent block volume or durable boot disk space for
/data - inbound
80and443open - a strong random
TYPESENSE_API_KEY
Install Docker on the VM, then create /opt/openkeep-typesense/docker-compose.yml:
services:
typesense:
image: typesense/typesense:27.1
restart: unless-stopped
command: --data-dir /data --api-key=${TYPESENSE_API_KEY} --enable-cors
volumes:
- ./data:/data
expose:
- "8108"
caddy:
image: caddy:2.8
restart: unless-stopped
ports:
- "80:80"
- "443:443"
volumes:
- ./Caddyfile:/etc/caddy/Caddyfile:ro
- caddy_data:/data
- caddy_config:/config
depends_on:
- typesense
volumes:
caddy_data:
caddy_config:
Create /opt/openkeep-typesense/Caddyfile:
typesense-docs.example.com {
reverse_proxy typesense:8108
}
Create /opt/openkeep-typesense/.env:
TYPESENSE_API_KEY=replace-with-a-long-random-admin-key
Start the services:
docker compose up -d
Verify from your machine:
curl https://typesense-docs.example.com/health
Expected result:
{"ok":true}
Cloudflare Pages Setup
Install the Cloudflare GitHub App on this repository only, then configure the Pages project to use native GitHub builds.
Recommended Pages build settings:
- Framework preset:
None - Root directory:
/ - Build command:
pnpm install --frozen-lockfile && pnpm docs:build - Build output directory:
apps/docs/build
Set the production domain to the same value you will store in DOCS_SITE_URL, for example:
https://docs.example.com
Add these Cloudflare Pages environment variables for both Production and Preview as needed:
DOCS_SITE_URL: production or preview docs URLTYPESENSE_COLLECTION_NAME: collection alias, usuallyopenkeep-docsTYPESENSE_HOST: public Typesense host without protocol, for exampletypesense-docs.example.comTYPESENSE_PORT: public Typesense port, usually443TYPESENSE_PROTOCOL: public Typesense protocol, usuallyhttpsTYPESENSE_SEARCH_API_KEY: a search-only key for the docs collection
TYPESENSE_SEARCH_API_KEY must be a search-only key, not the admin key. The Docusaurus build embeds it into the client-side search config.
Generate or rotate that key from a machine that can reach Typesense:
export TYPESENSE_HOST=typesense-docs.example.com
export TYPESENSE_PORT=443
export TYPESENSE_PROTOCOL=https
export TYPESENSE_ADMIN_API_KEY=replace-with-admin-key
export TYPESENSE_COLLECTION_NAME=openkeep-docs
export TYPESENSE_SEARCH_API_KEY_FILE=/tmp/openkeep-docs-search-key
node scripts/docs/bootstrap-typesense-search-key.mjs
Then read the generated value and paste it into the Cloudflare Pages environment variable:
tr -d '\n' < /tmp/openkeep-docs-search-key
GitHub Repository Configuration
Add these GitHub Actions secrets:
TYPESENSE_ADMIN_API_KEY: the Oracle-hosted Typesense admin key
Add these GitHub Actions variables:
DOCS_SITE_URL: public docs URL, for examplehttps://docs.example.comTYPESENSE_COLLECTION_NAME: collection alias, usuallyopenkeep-docsTYPESENSE_HOST: public Typesense host without protocol, for exampletypesense-docs.example.comTYPESENSE_PORT: public Typesense port, usually443TYPESENSE_PROTOCOL: public Typesense protocol, usuallyhttps
These values are consumed by .github/workflows/reindex-docs-search.yml.
Deployment Flow
Push to main or run the workflow manually.
Cloudflare Pages will:
- build the docs with the browser-facing Typesense config from Pages environment variables
- deploy the static site from
apps/docs/build
GitHub Actions will:
- install dependencies
- wait for the production docs site to become reachable
- reindex the deployed site with
pnpm docs:search:index:remote
Local Dry Run
You can test the remote indexing path before relying on CI:
export TYPESENSE_PUBLIC_HOST=typesense-docs.example.com
export TYPESENSE_PUBLIC_PORT=443
export TYPESENSE_PUBLIC_PROTOCOL=https
export TYPESENSE_ADMIN_API_KEY=replace-with-admin-key
export TYPESENSE_COLLECTION_NAME=openkeep-docs
export DOCSEARCH_START_URL=https://docs.example.com
export DOCSEARCH_SITEMAP_URL=https://docs.example.com/sitemap.xml
export DOCSEARCH_STOP_URL=https://docs.example.com/search
pnpm docs:search:index:remote
Operational Notes
- the docs build embeds a search-only key, not the admin key
- the search-only key now lives in Cloudflare Pages environment variables
- if you rotate the search-only key, update the Pages environment variable before the next build
- the Typesense admin API is reachable from GitHub Actions, so use TLS and a strong admin key
- the reindex workflow waits for the deployed sitemap before scraping to reduce stale-search races
- repeated indexing clears the active alias first and removes stale collections after a successful scrape
Troubleshooting
If the docs deploy succeeds but search is missing:
- confirm
DOCS_SITE_URLmatches the live Pages domain - confirm
TYPESENSE_HOST,TYPESENSE_PORT, andTYPESENSE_PROTOCOLpoint to the public Oracle endpoint - confirm
TYPESENSE_SEARCH_API_KEYis set in Cloudflare Pages and is a search-only key - confirm the workflow can reach
https://<typesense-host>/health - confirm the workflow can reach
${DOCS_SITE_URL}/sitemap.xml - confirm CORS is enabled on the Typesense node
If indexing fails:
- confirm Docker is available in the GitHub-hosted runner logs
- confirm the admin key is correct
- confirm the docs site is publicly reachable before the scrape step begins