Skip to content

Deploy to production

Production deployments should build the app, apply committed migrations, then run the web server and at least one worker.

Terminal window
pnpm run build

The build creates:

  • Directorydist/
    • Directoryui/
    • Directoryopenshop/
      • Directoryserver/

Single-app deployments usually need:

Terminal window
DATABASE_URL=postgresql://...
SHOPIFY_API_KEY=...
SHOPIFY_API_SECRET=...
HOST=https://your-app.example.com
ENCRYPTION_KEY=<64 hex characters>

Generate an encryption key:

Terminal window
openssl rand -hex 32

ENCRYPTION_KEY protects provider credentials and Shopify access tokens. Treat it as required in production.

Generate and review migrations in development or CI:

Terminal window
pnpm exec openshop migrate generate
pnpm exec openshop migrate check

Commit the generated SQL in ./drizzle, then apply committed migrations before starting production processes:

Terminal window
pnpm exec openshop migrate

openshop migrate only applies SQL from ./drizzle. It does not run generation tooling.

Configure two independently supervised services from the same build:

Service Command Replicas
Web pnpm exec openshop start One or more behind your router
Worker pnpm exec openshop worker --concurrency=5 One or more

Do not place the commands one after the other in a deploy script. Both are long-running processes, so openshop start would prevent the shell from ever starting the worker. Use separate platform process types, containers, system services, or the template’s ecosystem.config.cjs.

openshop start serves HTTP traffic and dispatches cron runs. It does not execute queued flow runs. At least one worker must be running for queued runs to complete.

Each web replica starts a scheduler. OpenShop does not currently elect one scheduler leader, so prefer one web replica when using crons, or accept and handle duplicate dispatch attempts. Flow concurrency can reject overlapping runs, but it is not a cron de-duplication guarantee.

Every web and worker replica needs the same DATABASE_URL, Shopify configuration, and ENCRYPTION_KEY. Each process creates its own PostgreSQL pool; tune PGPOOL_MAX with the total replica count and database connection limit in mind.

  • The web process starts without migration errors.
  • The worker process starts and reports no database connectivity errors.
  • The embedded admin UI loads inside Shopify.
  • A test flow run moves from queued to completed or failed with logs.

OpenShop does not apply migrations on boot. If a deploy fails before migrations are applied, roll back the app process only. If committed SQL was already applied, use your normal database rollback or forward-fix process.