Operations
Health checks
Section titled “Health checks”astropress doctor # env contract, runtime plan, schema drift warningsastropress doctor --json # machine-readable outputastropress doctor --strict # exit non-zero on any warningastropress services verify # confirm content-services keys + service originbun run audit:security # inline-handler and hardening checksastropress doctor warns on:
- missing or weak
SESSION_SECRET - scaffold-default
ADMIN_PASSWORDstill in use ADMIN_BOOTSTRAP_DISABLEDnot set- missing
.data/directory - projects still using legacy
ASTROPRESS_*PROVIDERvars
Local bootstrap
Section titled “Local bootstrap”astropress new my-siteastropress new my-site --app-host vercel --content-services supabaseastropress new my-site --app-host cloudflare-pages --content-services cloudflarecd my-sitebun installastropress doctorastropress services bootstrapastropress services verifyastropress devBackup and restore
Section titled “Backup and restore”# Create a snapshotastropress backup --project-dir <site> --out <snapshot-dir>
# Restore from a snapshotastropress restore --project-dir <site> --from <snapshot-dir>Snapshots are file-based exports. For hosted providers, supplement with provider-native point-in-time restore (Cloudflare D1 time travel, Supabase Point-in-Time Recovery).
Importing content
Section titled “Importing content”WordPress
Section titled “WordPress”astropress import wordpress --project-dir <site> --source export.xmlOptional flags:
--artifact-dir <dir>— write artifacts here (default:.astropress/import/)--download-media— download attachment assets intoartifacts/downloads/--apply-local— apply staged artifacts into the local SQLite runtime--resume— re-enter a previous import and skip already-downloaded media
The import is staged, not instant. It writes structured JSON artifacts
(content, media, comments, users, taxonomies, redirects) for review before
applying. Run without --apply-local first to inspect what will change.
Export your site from Wix’s dashboard (Settings → General → Export Site Data), then:
astropress import wix --project-dir <site> --source wix-export.csvSame flags as WordPress: --artifact-dir, --download-media, --apply-local,
--resume.
If you need to download the export programmatically, the Playwright-based
credential fetcher handles authenticated Wix dashboard access — see the
import wix --fetch flag.
Crawling any site
Section titled “Crawling any site”For sites without an export format:
astropress import crawl --project-dir <site> --url https://example.comThe crawler walks links from the start URL, extracts page titles and body HTML, and writes the results as importable content records. Useful for migrating from static generators or hand-authored HTML sites.
Content scheduling
Section titled “Content scheduling”Set a publish time in the post editor (Scheduled Publish Time field) to
queue a draft for future publication.
The SQLite runtime exposes runScheduledPublishes() which promotes all
due posts atomically:
import { runScheduledPublishes } from "astropress/sqlite-admin-runtime";
// Call this on a schedule — e.g., every 5 minutesconst published = runScheduledPublishes(db);console.log(`Published ${published} scheduled posts`);Cloudflare Workers: wire it in a scheduled handler in worker.ts:
export default { async scheduled(_event, env, _ctx) { const runtime = await createRuntime(env); await runtime.content.runScheduledPublishes(); },};GitHub Actions: use a cron workflow to hit a secured API route that
calls runScheduledPublishes().
The posts list shows a Scheduled filter tab. Posts stay draft until
runScheduledPublishes fires; cancelling clears the scheduled time.
Secret handling
Section titled “Secret handling”Before any real deployment:
- Generate a strong
SESSION_SECRET:SESSION_SECRET=$(openssl rand -hex 32) - Change
ADMIN_PASSWORDandEDITOR_PASSWORDfrom scaffold defaults - Set
ADMIN_BOOTSTRAP_DISABLED=1once named admin accounts exist - Set
TURNSTILE_SECRET_KEYbefore exposing the login form publicly
Secret rotation
Section titled “Secret rotation”Astropress supports a two-phase session-secret rotation window.
Package/runtime-managed sessions
Section titled “Package/runtime-managed sessions”- Deploy the new secret as
SESSION_SECRET - Keep the previous secret in
SESSION_SECRET_PREV - Wait for old sessions to expire naturally, or revoke them explicitly once the migration window is complete
- Remove
SESSION_SECRET_PREVin a follow-up deploy
During the rotation window:
- existing sessions signed with
SESSION_SECRET_PREVremain valid - all newly created sessions are signed only with
SESSION_SECRET
If you use the advanced root-secret override instead of SESSION_SECRET, the same pattern applies with ASTROPRESS_ROOT_SECRET and ASTROPRESS_ROOT_SECRET_PREV.
Cloudflare adapter sessions
Section titled “Cloudflare adapter sessions”- Deploy the new secret as
CLOUDFLARE_SESSION_SECRET - Keep the previous secret in
CLOUDFLARE_SESSION_SECRET_PREV - Wait for old sessions to expire naturally, or revoke them explicitly once the migration window is complete
- Remove
CLOUDFLARE_SESSION_SECRET_PREVin a follow-up deploy
During the rotation window:
- existing sessions signed with
CLOUDFLARE_SESSION_SECRET_PREVremain valid - all newly created sessions are signed only with
CLOUDFLARE_SESSION_SECRET
To revoke all active sessions immediately:
UPDATE admin_sessions SET revoked_at = CURRENT_TIMESTAMPWHERE revoked_at IS NULL;Schema migrations
Section titled “Schema migrations”Framework-managed (SQLite)
Section titled “Framework-managed (SQLite)”Most upgrades require no manual steps for SQLite deployments. On every boot,
Astropress applies any new ALTER TABLE ... ADD COLUMN statements idempotently.
Applied migrations are recorded in schema_migrations.
User-managed migrations
Section titled “User-managed migrations”Place numbered .sql files in your migrations/ directory:
migrations/ 0001_add_event_taxonomy.sql 0002_add_event_date_index.sqlastropress db migrate --migrations-dir ./migrationsastropress db migrate --migrations-dir ./migrations --dry-run # preview onlyCompanion .down.sql files are read and stored in schema_migrations.rollback_sql
for safe rollback. To apply the most recent migration’s rollback SQL:
astropress db rollback # apply rollback_sql from the last migrationastropress db rollback --dry-run # preview what would be rolled backCloudflare D1 migrations
Section titled “Cloudflare D1 migrations”astropress db migrate --target=d1 applies pending migration files to your D1
database via wrangler d1 execute. Requirements:
wrangleron PATH:bun add -g wranglerornpm install -g wrangler- Authenticated:
wrangler loginorCLOUDFLARE_API_TOKENenv var CLOUDFLARE_D1_BINDINGenv var set to your D1 binding name (default:DB)
# Preview which files would run (no writes):astropress db migrate --target=d1 --dry-run
# Apply to the remote D1 database:astropress db migrate --target=d1Supabase migrations
Section titled “Supabase migrations”Supabase projects use the Supabase CLI for
schema migrations. Astropress migration .sql files are compatible — place them in
supabase/migrations/ and use the Supabase CLI workflow:
# Link to your project (once):supabase link --project-ref <ref>
# Push pending migrations to the remote database:supabase db pushVersion upgrade procedure
Section titled “Version upgrade procedure”- Snapshot:
astropress backup --project-dir <site> --out <snapshot-dir> - Upgrade the package:
bun add astropress@latest - Run
astropress doctor— flags env contract changes and schema drift - For SQLite: run
astropress db migrate(defaults to--target=local) - For D1: run
astropress db migrate --target=d1 - For Supabase: run
supabase db push - Deploy. If migration fails, restore from snapshot and replay in isolation.
Caching
Section titled “Caching”applyAstropressSecurityHeaders() sets Cache-Control by area:
| Area | Value | Effect |
|---|---|---|
public | public, max-age=300, s-maxage=3600, stale-while-revalidate=86400 | 5 min browser / 1 hr CDN |
admin | private, no-store | Never cached |
auth | private, no-store | Never cached |
api | private, no-store | Never cached |
CDN purge on publish: Astropress fires purgeCdnCache(slug, config) on
every content publish — supports Cloudflare Cache API and generic webhook URLs:
registerCms({ cdnPurgeWebhook: "https://api.netlify.com/build_hooks/your-hook-id",});Static host publishing
Section titled “Static host publishing”Full rebuild
Section titled “Full rebuild”astropress build # full Astro build → dist/astropress publish # build + push to configured static hostIncremental rebuild
Section titled “Incremental rebuild”For sites with large page counts, use --incremental to regenerate only the
pages affected by a given set of slug changes:
astropress publish --incremental --slugs /blog/my-post,/blog/another-postecho -e "/blog/my-post\n/blog/another-post" | astropress publish --incremental --stdin| Mode | Build time | Best for |
|---|---|---|
| Full rebuild | Proportional to site size | Small sites, release deploys |
| Incremental | Proportional to changed pages | Large sites, frequent edits |
For sites with fewer than ~500 pages, full rebuild is simpler and fast enough.
Observability
Section titled “Observability”Structured logs
Section titled “Structured logs”Set LOG_LEVEL to emit structured JSON lines to stderr. Fields: level,
context, message, timestamp, requestId.
Metrics endpoint
Section titled “Metrics endpoint”GET /ap-api/v1/metrics Bearer token required (content:read scope)Returns { posts, pages, media, comments, uptime }.
Prometheus metrics
Section titled “Prometheus metrics”Enable the unauthenticated Prometheus text format endpoint:
registerCms({ monitoring: { prometheusEnabled: true },});GET /ap/metrics No authentication requiredContent-Type: text/plain; version=0.0.4Exported metrics: ap_content_total{kind}, ap_media_total, ap_uptime_seconds.
Incident handling
Section titled “Incident handling”Auth breach
- Revoke all sessions:
UPDATE admin_sessions SET revoked_at = CURRENT_TIMESTAMPWHERE revoked_at IS NULL;
- Rotate
SESSION_SECRET/SESSION_SECRET_PREVandCLOUDFLARE_SESSION_SECRET/CLOUDFLARE_SESSION_SECRET_PREV - Audit
admin_sessionsandaudit_eventsfor suspicious activity
Data loss
- Restore from the last clean backup
- Run
astropress doctorandastropress services verify - Re-apply content created since the snapshot using audit logs as reference
Admin panel down
- Run
astropress doctor— check env and service health - Check server logs for runtime errors or migration failures
- If a failed migration caused it: restore from snapshot, replay migration in isolation, then redeploy
Disaster recovery
Section titled “Disaster recovery”RTO / RPO targets
Section titled “RTO / RPO targets”| Tier | Data store | RPO | RTO |
|---|---|---|---|
| Local + SQLite | SQLite file | Last astropress backup snapshot | < 5 min |
| Cloudflare Pages + D1 | Cloudflare D1 | D1 time-travel (30 days) | < 15 min |
| Supabase | PostgreSQL | Point-in-time recovery (up to 7 days on Pro) | < 30 min |
Post-restore checklist
Section titled “Post-restore checklist”-
astropress doctor— all checks pass -
astropress services verify— database connectivity confirmed - Admin panel login succeeds
-
GET /ap/healthreturns{ "status": "ok" }