> ## Documentation Index
> Fetch the complete documentation index at: https://topten.mintlify.site/llms.txt
> Use this file to discover all available pages before exploring further.

# Deployment

> How Live Product Insights runs in production: the single always-on Fly.io instance, SQLite on a persistent volume, migration-driven Docker boots, environment variables, and post-deploy verification.

## Production topology

The app runs as a **single, always-on instance** on Fly.io: a shared-CPU machine with 512 MB of memory, a persistent volume mounted at `/data`, and auto-stop disabled. The always-on requirement is functional, not incidental: storefront trackers send heartbeats every few seconds and the counter widget polls continuously — a sleeping machine would freeze live counts.

**SQLite is the production database**, stored on the persistent volume. This is a deliberate trade-off: it keeps the stack simple and honest for a single-instance app, and it defines the scaling boundary described below.

## Docker image

The image is `node:22-alpine` with OpenSSL. It installs full dependencies (build tooling lives in `devDependencies`), builds the app, prunes dev dependencies, and on boot runs:

```bash theme={null}
prisma generate && prisma migrate deploy && react-router-serve ./build/server/index.js
```

Deploys are therefore **migration-driven**: every boot applies pending migrations before serving traffic.

## Deploy flow

```bash theme={null}
shopify app deploy
```

This registers the current app version: access scopes, webhook subscriptions, the app proxy mapping, and theme extension assets. After changing app configuration (scopes, webhooks, proxy), a deploy is what makes the change live for installed stores.

## Environment variables

| Variable | Purpose |
| - | - |
| `SHOPIFY_API_KEY` / `SHOPIFY_API_SECRET` | App credentials; the secret verifies proxy and webhook HMACs |
| `SCOPES` | Comma-separated access scopes granted on install |
| `SHOPIFY_APP_URL` | Public app URL used for OAuth redirects and App Bridge |
| `SHOP_CUSTOM_DOMAIN` | Optional custom shop domain support |
| `DATABASE_URL` | SQLite file path (in production, on the persistent volume) |
| `SCHEDULER_DISABLED` | Set to `1` to disable the in-process scheduler |

> [!WARNING]
>
> Never commit secret values. The repository's convention is that secrets exist only in the deployment environment or the Shopify CLI session — see [Security](https://topten.mintlify.site/development/security.md).

## Operational constraints

* **Single instance only, for now.** Rate limiting, view-dedupe locking, and the order-sync throttle are in-memory and per-process, and SQLite allows one writer. Running multiple instances requires moving rate limiting and dedupe locks to a shared store (e.g. Redis) and the database to a multi-writer engine. The rate limiter documents this boundary in code.
* **The scheduler lives inside the web process.** Cron jobs run on the same machine as request handling. Scaling out means either keeping one instance or splitting the scheduler into a dedicated worker with the scheduler flag inverted on the web machines.
* **Backups = the database file.** Back up the SQLite file on the persistent volume on a schedule; because rollups and retention pruning rewrite it, take backups from a quiesced moment (e.g. shortly after the nightly maintenance window) or use SQLite's backup API.

## API version alignment

The application code pins its Admin API version independently of the app configuration file's webhook API version. When upgrading either, check both and keep them aligned — a mismatch means webhooks are delivered in one API version while the app queries in another.

## Post-deploy verification

**1. App loads in admin**

Open the app from a store's admin; the dashboard renders with the navigation menu.

**2. Proxy smoke test**

Send a correctly signed request to the app proxy stats endpoint and expect `200` with a JSON count; an unsigned request must return `400`. (Test helpers reproduce the signing algorithm exactly.)

**3. Webhooks flowing**

Place a test order on a store with order webhooks enabled and confirm the order webhook is received in logs; check product and inventory webhooks after an edit.

**4. Scheduler firing**

Confirm insight generation and order catch-up logs appear on their 30-minute cadence, and that the nightly maintenance window (rollups, pruning) runs.


This documentation is built and hosted on [Mintlify](https://mintlify.com), a developer documentation platform.