Dockerfile and a docker-compose.yml, and it runs on Railway or Vercel without modification. Pick the tab that matches where you want it. All three paths are you hosting a Store Runtime yourself, which means you also own the database, the backups, and the uptime. Those host names appear here only because you chose them.
A deployment created through 86d.app is a different arrangement: 86d provisions the machines, runs the deployment, and bills you as a Managed Deployment. That path never names the suppliers behind managed hosting, DNS, or payments. You operate 86d Cloud. See 86d Cloud plans and architecture.
- Docker (self-hosted)
- Railway
- Vercel and Neon
Compose starts PostgreSQL 16, MinIO object storage, and the Store app. Copy the environment template:Generate an auth secret:Put the printed value into Then start it:Replace The release path promotes
.env, replacing the placeholder:.env
Run a release image
Once an operator publishes a release, pull the immutable version tag for the 86d.store Store Runtime image from either registry:VERSION with the exact release version. Start the selected image with the same PostgreSQL, storage, and Store services without rebuilding source:latest only after both immutable registry tags resolve to the same multi-platform image. Pin a version or digest for a production deployment because latest moves on the next release.First boot waits for PostgreSQL, applies internals/docker/init.sql (nanoid/pgcrypto), runs Drizzle migrations, compiles curated Module tables, seeds demo data with AUTO_SEED=true, and starts the Next.js server on port 3000. Seed writes only curated Modules that compile to tables (stripe remains tier-none). A second container restart with AUTO_SEED=true must upsert the same seed-owned keys without wiping schemas or duplicating rows.Health is served at /api/health after boot. Four services come up:Compose sets
NODE_ENV=production. In production the runtime rejects a placeholder secret, a known default, anything shorter than 32 characters, and anything with too little entropy, so the app will not start until you paste a real value. Paste the output of openssl, not the command itself.Uploads go to MinIO by default and are served from your own origin at /uploads/..., so the bucket never has to be public.Port conflicts are overridable:Variables you will actually set
The full list is in Environment variables and in.env.example. These are the ones every deployment needs:
Values 86d Cloud supplies
A Managed Deployment receives86D_STORE_ID, 86D_API_URL, and 86D_WORKLOAD_CREDENTIAL together. The runtime trades the opaque credential at the Control Plane for short-lived, Store-scoped access before it fetches any managed configuration. The credential can be rotated and revoked, it carries no provider secrets, and it cannot authenticate a person.
Do not set these on a Store you host yourself. Standalone Stores use STORE_ID for local data isolation and read their configuration from the active Template.
Human sign-in to a managed Store Admin uses a separate pair, 86D_ADMIN_OAUTH_CLIENT_ID and 86D_ADMIN_OAUTH_CLIENT_SECRET. Keep every machine credential and client secret out of browser code, logs, agent output, Templates, and Store Admin settings. See managed identity.
Where uploaded files go
STORAGE_CLIENT decides where Product images, PDFs, and other uploads live.
Configure storage has the per-provider detail.
Before you point a domain at it
Work through this list after the first deploy and before anyone can reach the Storefront:- Replace the seeded Store Admin password
- Confirm
BETTER_AUTH_SECRETis a random value, not the placeholder - Set
APP_URLto the public domain - Configure one sandbox payment provider and its webhook verification values, per Payment integrations
- Upload a Product image in Store Admin to prove storage works
- Run
86d doctorand clear every failure