Skip to content

Installation

There are two ways to bring the system up, and both end in the same place: the wizard in your browser. That is the idea underneath — starting and configuring are different jobs. Starting can happen with Docker, with npm, or with a button in some cloud panel; configuring always happens the same way, and it happens without a terminal.


Terminal window
docker compose -f docker-compose.full.yml up -d

That is it. It brings up four pieces: Postgres, the backend, the admin panel and the storefront.

The first run takes a few minutes, because it builds the three images and migrates the entire database. After that it starts in seconds. Opening the panel drops you into the wizard: four checks, your store name, your account and, if you want it, a demo catalogue so you can watch the whole loop work.

To follow the startup:

Terminal window
docker compose -f docker-compose.full.yml logs -f backend

Copy .env.docker.example to .env and change two things:

  1. The domains (TIENDA_URL, PANEL_URL, BACKEND_URL). CORS and the links in your emails are derived from these.

  2. The Postgres user and password. The examples are pcreative, which is fine for trying it locally and not for a server.

    ⚠️ If you are coming from an earlier install, the user was written into the volume the first time the database started and cannot be changed afterwards: put the one you already had in your .env, or the backend will not be able to get into its own database.

You can leave the session secrets empty: the container generates a set on first run and keeps them in its volume. Set them by hand only if you are going to run several backend replicas — each one would generate its own, and sessions would stop being valid the moment a request landed on a different replica.

Change the .env, restart, done:

Terminal window
docker compose -f docker-compose.full.yml up -d

None of this forces an image rebuild, and that is not what you get by default with Next: variables that reach the browser are baked in at build time, so correcting a tax ID would mean rebuilding the whole panel. Here the server reads its configuration at startup and hands it to the browser with each page (apps/admin/src/lib/config.ts), which is the pattern Next documents for “build once, deploy anywhere”. The same goes for the store name, the brand colour and the tax details on your invoices.

The only thing still tied to build time is the storefront theme.

Set TEMA=<folder under themes/> in the .env and rebuild the storefront:

Terminal window
docker compose -f docker-compose.full.yml build tienda
docker compose -f docker-compose.full.yml up -d

The panel reads the theme catalogue straight off disk (./themes is mounted), so nothing needs rebuilding on that side.

Four volumes, and it is worth knowing which is which before deleting any of them:

Volume What is in it
db The database. Everything.
static Images uploaded from the panel.
datos Generated secrets and the publishable key.

What Docker does under the hood, by hand. You need Node 22 or later and a Postgres.

Terminal window
npm ci # ALWAYS ci, never install
npm run build -w @pcreative/commerce-backend # compiles to apps/backend/dist
cd apps/backend
cp .env.example .env # DATABASE_URL and little else
npm run preparar # installs or migrates, and prepares the shop
npm start # runs the compiled build

preparar looks at the database and decides on its own: if it is empty it creates the whole schema (173 tables, with nothing else installed); if it already has things, it applies whatever is missing. Either way it leaves the shop ready to sell: the store row, a sales channel and the publishable key the storefront needs to talk to it. It is idempotent — you can run it on every start, which is exactly what the container does.

Then the setup wizard in the browser, same as with Docker.

Behind a process manager or a load balancer

Section titled “Behind a process manager or a load balancer”

The server has two health endpoints, and they are not the same thing:

  • /salud → 200 as long as the process responds. For “is it alive?”.
  • /listo → 200 only if it can serve customers. It switches to 503 the moment shutdown begins, and also if any plugin failed to load at startup — a server selling at a different price is not ready.

The process manager watches /salud to decide whether to restart; the load balancer watches /listo to decide whether to send traffic. The other way round loses requests: a balancer watching /salud keeps sending customers to a server that is already leaving, and a manager watching /listo kills the process while it finishes a payment. With PCC_MARGEN_CIERRE_MS=5000 the server waits that long between flagging /listo and closing the port, so the balancer can step aside.

Same, without compiling and reloading on save:

Terminal window
docker compose up -d # Postgres only
npm ci
cd apps/backend && npm run dev # tsx watch
cd apps/admin && npm run dev

  • An npx create-pcreative-commerce command for people who would rather not know Docker exists. Today it would be a thin wrapper around the above.
  • Deploy buttons for Coolify, Railway or DigitalOcean.
  • Shared hosting. And this one is not coming: Node is not installed on the €3 hosting plans where half of WooCommerce lives. It is the structural disadvantage against PHP, and a better installer does not fix it.