<!-- Generated from the canonical OpenPost public page. Do not edit this build artifact. -->

Title: Production Checklist
Description: Use this page before putting a real OpenPost instance behind a public domain.
Canonical: https://docs.openpo.st/configuration/production-checklist
Source: [https://docs.openpo.st/configuration/production-checklist](https://docs.openpo.st/configuration/production-checklist)

# Production Checklist

Use this page before putting a real OpenPost instance behind a public domain.
It is operator-facing: product usage lives in [User Docs](https://docs.openpo.st/usage/), while code
changes live in [Developer Docs](https://docs.openpo.st/development/).

## Baseline

- [ ] Copy the root `.env.example` to `.env` or mirror every required value in your secret manager.
- [ ] Generate fresh `OPENPOST_JWT_SECRET` and `OPENPOST_ENCRYPTION_KEY`.
- [ ] Keep both secrets at least 32 characters long.
- [ ] Store secrets outside the repository and outside container images.
- [ ] Use `<VARIABLE>_FILE` variants for Docker/Podman/Kubernetes/NixOS secrets, and leave the direct variable unset when the file value should win.
- [ ] Set `OPENPOST_APP_URL` to the public HTTPS app origin.
- [ ] Set `OPENPOST_PUBLIC_URL` to the same public HTTPS app origin unless you have a specific split-origin reason.
- [ ] Configure `OPENPOST_EMAIL_PROVIDER`, `OPENPOST_EMAIL_FROM`, and that provider's credentials; verify signup, password-reset, and one opted-in operational notification without logging codes or secrets.
- [ ] If the mail provider can report delivery, set `OPENPOST_EMAIL_DELIVERY_WEBHOOK_SECRET`, configure `POST /api/v1/email/delivery/webhook`, and verify **Sent** changes to **Delivered** only after a signed callback.
- [ ] If Google login is enabled, register the exact `/api/v1/auth/oidc/google/callback` URL and store `OPENPOST_AUTH_GOOGLE_CLIENT_SECRET` through a file-backed secret.
- [ ] Keep `OPENPOST_EXTRA_CORS_ORIGINS` explicit and do not use `*`.
- [ ] Configure a reverse proxy with HTTPS before connecting OAuth providers.
- [ ] Align reverse-proxy and CDN request-body limits with the largest video you accept, and disable request buffering for streamed uploads.
- [ ] Decide whether to enable automatic image alt text. If enabled, store `OPENROUTER_API_KEY` through `OPENROUTER_API_KEY_FILE` and review OpenRouter and model provider privacy and retention terms.
- [ ] Review the built-in meme catalog's source links and template-rights boundary. If AI meme suggestions are enabled, review the configured OpenRouter model and privacy terms; manual rendering stays inside OpenPost.
- [ ] Confirm `GET /api/v1/health` returns `{"status":"ok"}`.
- [ ] Confirm `GET /api/v1/ready` returns `{"status":"ready","database":"ok"}`.
- [ ] Use `/api/v1/health` for process liveness and `/api/v1/ready` for traffic admission, rollouts, and dependency-aware monitoring.
- [ ] Confirm `openpost instance health --instance <public-url>` succeeds against the public URL.
- [ ] Capture `openpost instance diagnostics --instance <public-url> --json` for the launch/support handoff.
- [ ] Decide whether to enable PostHog telemetry. Self-hosted instances keep it disabled unless the operator explicitly chooses an operator-owned project and updates their privacy notice.

## Self-Hosted Storage

- [ ] Keep `OPENPOST_EDITION=selfhost` or leave it unset.
- [ ] Use SQLite/local storage unless you intentionally operate Postgres/S3 yourself.
- [ ] Persist the SQLite database path, usually `/data/db/openpost.db`.
- [ ] Persist the local media directory, usually `/data/media`.
- [ ] Set `OPENPOST_MEDIA_URL` to the public media base URL.
- [ ] Back up database files, media files, and secrets together.
- [ ] Run at least one test restore before relying on the backup.

## Hosted Service Operators

- [ ] Set `OPENPOST_EDITION=cloud`.
- [ ] Set `OPENPOST_DATABASE_DRIVER=postgres`.
- [ ] Set `OPENPOST_DATABASE_URL` to the production Postgres URL.
- [ ] Set `OPENPOST_STORAGE_DRIVER=s3`.
- [ ] Set `OPENPOST_S3_REGION`, `OPENPOST_S3_BUCKET`, `OPENPOST_S3_ACCESS_KEY_ID`, and `OPENPOST_S3_SECRET_ACCESS_KEY`.
- [ ] Set `OPENPOST_S3_PUBLIC_BASE_URL` to a stable public media origin.
- [ ] Verify the S3 bucket lifecycle policy and object access model before launch.
- [ ] Set `OPENPOST_PADDLE_API_KEY`, `OPENPOST_PADDLE_ENVIRONMENT=production`, `OPENPOST_PADDLE_CLIENT_TOKEN`, `OPENPOST_PADDLE_WEBHOOK_SECRET`, and `OPENPOST_PADDLE_CHECKOUT_RETURN_URL`.
- [ ] Set the monthly and annual `OPENPOST_PADDLE_<PLAN>_<PERIOD>_PRICE_ID` values for Starter, Founder, Pro, Team, and Agency.
- [ ] Confirm the production API key and client token have live prefixes; never deploy sandbox credentials to the Hosted service.
- [ ] Configure Paddle to send customer, subscription, and `transaction.completed` events to `/api/v1/billing/paddle/webhook`; send a signed test event and confirm it is stored once and reconciled through the billing job.
- [ ] Set the minimum and maximum quantity to `1` for every Paddle plan price so buyers cannot add duplicate copies of a workspace subscription.
- [ ] Before upgrading from Whop billing, migrate every active customer to Paddle and confirm the Paddle subscription is reconciled locally. Historical Whop rows do not grant entitlements after the upgrade.
- [ ] Complete an embedded checkout smoke: plan and period selection, $0 trial start, return to OpenPost, local `trialing` status, and billing management URL.
- [ ] Confirm a new hosted user can create the bootstrap workspace and is blocked from extra workspaces before checkout.
- [ ] Confirm team invitations are blocked once active members plus pending invites reach the plan limit.
- [ ] Configure one EU PostHog production project for the app, backend, marketing site, and documentation; use separate staging and development projects.
- [ ] Enable stateful cookieless server hashing; keep raw-IP discard on after approved persistent-event country and bot enrichment; remove city, region, coordinates, postal code, and accuracy-radius properties; set event retention to no more than 12 months.
- [ ] Keep broad autocapture, session replay, heatmaps, surveys, dead clicks, console capture, network timing and bodies, and automatic browser exception capture disabled; enable only CLS, FCP, INP, and LCP Web Vitals.
- [ ] Set the PostHog project token and server, browser, and UI hosts; keep the personal API key only in CI source-map upload secrets.
- [ ] Verify `GET /api/v1/telemetry/config` exposes only the browser-safe project token and ingestion configuration.
- [ ] Verify undecided and fully off browsers send no optional events; persistent events keep cross-subdomain identity and country without raw IP; cookieless events stay personless with no geography or browser correlation headers.
- [ ] Verify a browser intent event, a server outcome event, and one sanitized test exception arrive without content, credentials, email, names, query strings, raw URLs, or precise location properties.

## Providers

- [ ] Start with Bluesky or Mastodon for the first end-to-end publish smoke.
- [ ] Update callback URLs for X, LinkedIn, Threads, Facebook, Instagram, TikTok, and YouTube to the production HTTPS app origin.
- [ ] Add Facebook through `OPENPOST_PROVIDER_APPS` or the instance-admin provider app API if Facebook Pages publishing is enabled, and confirm `OPENPOST_MEDIA_URL` serves public HTTPS media for media posts.
- [ ] Add Instagram through `OPENPOST_PROVIDER_APPS` or the instance-admin provider app API if Instagram professional publishing is enabled, and confirm `OPENPOST_MEDIA_URL` serves public HTTPS media.
- [ ] Add TikTok through `OPENPOST_PROVIDER_APPS` or the instance-admin provider app API if short-form video publishing is enabled, and confirm `OPENPOST_MEDIA_URL` serves public HTTPS media.
- [ ] Add YouTube through `OPENPOST_PROVIDER_APPS` or the instance-admin provider app API if video uploads are enabled, and confirm the Google Cloud project has YouTube Data API v3 enabled.
- [ ] Configure Mastodon servers in `MASTODON_SERVERS` if you need fixed self-hosted Mastodon apps.
- [ ] Confirm custom Mastodon instance registration works if you rely on dynamic Mastodon connections.
- [ ] Limit each new social network until OAuth, media, publishing, token refresh, retries, and API limits pass a live account test.
- [ ] Create one test account connection per enabled provider.
- [ ] Publish a private or low-risk test post with and without media for every enabled provider.

## Product Smoke

- [ ] Create the first admin account.
- [ ] Confirm email-and-password signup cannot create a session until the six-digit code is accepted, and confirm resend invalidates the prior code.
- [ ] Confirm Google can create a new account, then link and unlink Google from an existing password account without email-based auto-linking.
- [ ] Decide whether to set `OPENPOST_DISABLE_REGISTRATIONS=true`.
- [ ] Create a workspace.
- [ ] Invite a second user, accept the link, and confirm both members appear in **Settings → Workspace → Members**.
- [ ] Connect at least one social account.
- [ ] Upload a small image and confirm it appears in the media library.
- [ ] If automatic alt text is enabled, add an image without alt text to the text-and-thread composer, confirm OpenPost fills its shared alt text, and confirm a manual edit is not replaced.
- [ ] Create a draft and scheduled post from the web app.
- [ ] Create a draft or scheduled post through the CLI.
- [ ] Create a draft or scheduled post through MCP if assistant access is enabled.
- [ ] Confirm scheduled publishing creates and completes a background job.

## Operations

- [ ] Point dependency-aware uptime monitoring at `/api/v1/ready`; keep container or orchestrator liveness on `/api/v1/health`.
- [ ] Confirm logs include startup configuration, database readiness errors, provider publish failures, and MCP tool-call failures.
- [ ] Document your deployment rollback path.
- [ ] Document where database backups, media backups, and secret backups live.
- [ ] Verify the release artifact or container image matches the version you intended to deploy.
