One-Command Demo — Setup Guide
This guide walks through booting the full OpenLinker stack in Docker with a single
command and clicking through an end-to-end PrestaShop → OpenLinker → Allegro
flow. It expands on the short “One-command demo” section in the root
README.md.
The demo boots, from a clean checkout: PostgreSQL, Redis, MySQL, a seeded PrestaShop (with the OpenLinker module pre-mounted), and the full OpenLinker app tier — API + Worker + admin UI.
Local evaluation only. Credentials are intentionally not production-safe and the stack is not hardened (no TLS, default passwords). Do not expose it publicly.
Need to make the demo reachable under a real domain instead (reverse proxy + TLS, rotated credentials, DNS)? See the public-domain demo deployment guide.
Table of Contents
Section titled “Table of Contents”- Prerequisites
- Environment variables (
.env) - Boot the stack
- Service URLs & credentials
- Wire the PrestaShop connection
- Wire the Allegro connection (optional)
- Verify the end-to-end flow
- Troubleshooting
- Teardown
- Running on a shared server / multiple instances
1. Prerequisites
Section titled “1. Prerequisites”- Docker and Docker Compose ≥ 2.24 (
docker compose version). The demo overlay uses!reset/ merge features from 2.24+. - pnpm and Node.js LTS (only to run the
pnpm demo:*wrapper scripts). - ~4 GB free disk for the built images and a few minutes for the first build (the whole monorepo is compiled inside the image; the build is from your local checkout, not a pre-built registry image).
- Free host ports:
8090(UI),3000(API),8080(PrestaShop),8081(phpMyAdmin),8082(WooCommerce),5432(Postgres),6379(Redis),3306(MySQL),3307(WooCommerce MySQL).
⚠️ Shared volumes with the dev stack. The demo shares the same Compose project (
openlinker) and data volumes aspnpm dev:stack:up. On a machine that already runs the local dev stack, the demo reuses and can clobber that data — the two flows are not isolated. Stop (and, if needed, wipe) one before running the other:pnpm dev:stack:downorpnpm demo:down -v.
2. Environment variables (.env)
Section titled “2. Environment variables (.env)”Docker Compose auto-loads a root .env file for ${VAR} interpolation. One
variable is required; everything else has a working default baked into the
Compose files.
Required
Section titled “Required”| Variable | Why | Example / how to generate |
|---|---|---|
OPENLINKER_CREDENTIALS_ENCRYPTION_KEY | Encryption key for the integration_credentials store. Migration 1795000000000-encrypt-integration-credentials fails closed under NODE_ENV=production if it is unset and aborts the whole boot; the API/Worker also need it to decrypt connection credentials at runtime. Base64-encoded 32 bytes. | openssl rand -base64 32 |
Create your .env
Section titled “Create your .env”.env is gitignored — never commit real secrets. .env.example ships an empty
OPENLINKER_CREDENTIALS_ENCRYPTION_KEY= line, so drop it and append the
generated value instead of >> .env directly — appending onto the example as-is
would leave two lines with the same key (harmless, since env-file parsing is
last-wins, but confusing to anyone editing .env by hand):
grep -v '^OPENLINKER_CREDENTIALS_ENCRYPTION_KEY=' .env.example > .envecho "OPENLINKER_CREDENTIALS_ENCRYPTION_KEY=$(openssl rand -base64 32)" >> .envExample resulting .env (the value below is an example — generate your own):
# Local demo secret — gitignored, never committed.OPENLINKER_CREDENTIALS_ENCRYPTION_KEY=rp92Yn0oNBH+VTIdcIpsXGJ8mcMg18w5mdNUPpLMoP8=Already set for you (baked into the Compose files — no action needed)
Section titled “Already set for you (baked into the Compose files — no action needed)”These are hardcoded in docker-compose.yml / docker-compose.demo.yml with
demo-safe values. Listed here so you know what’s in play if you want to override:
| Variable | Value in the demo | Purpose |
|---|---|---|
NODE_ENV | production | Production posture for the app tier. |
OL_BOOTSTRAP_ADMIN_PASSWORD | admin | Keeps the seeded admin login admin / admin under NODE_ENV=production. |
OL_CORS_ORIGIN | http://localhost:8090 | API CORS allow-list — must match the UI origin or login fails with a CORS “NetworkError”. |
WEB_URL | http://localhost:8090 (tracks WEB_HOST_PORT) | Base URL used to build confirm-email / reset-password links emailed to users - stays in sync with the web service’s published port instead of a second hardcoded default. |
VITE_API_BASE_URL | http://localhost:3000 | Baked into the UI bundle at build time — the browser-reachable API origin. |
DB_* / REDIS_* | postgres / redis (service names) | App tier reaches Postgres/Redis over the Compose network. |
JWT_SECRET, JWT_EXPIRES_IN | dev values | Auth token signing. |
OL_PII_HASH_SALT | dev value | Customer-identifier hashing in sync jobs. |
VITE_API_BASE_URLis a build-time input — changing it requires rebuilding thewebimage (pnpm demo:uprebuilds when the arg changes).
Optional: session recording on a public demo instance
Section titled “Optional: session recording on a public demo instance”OpenLinker ships no telemetry by default. Session recording (PostHog)
only activates on an instance where the operator has both set
OL_DEMO_MODE=true and explicitly configured OL_POSTHOG_KEY — a self-hosted
install or a local pnpm demo:up run never contacts PostHog. When both are
set, GET /system/config surfaces a demoIntegrations.posthog block that the
frontend uses to load posthog-js (dynamically, so it never ships in the
default bundle) — and only after the visitor accepts the consent prompt shown
in the demo banner.
| Variable | Purpose |
|---|---|
OL_POSTHOG_KEY | PostHog project API key (publishable, write-only ingestion key — never a personal/private key). Unset by default. |
OL_POSTHOG_HOST | PostHog ingestion host. Defaults to https://eu.posthog.com when OL_POSTHOG_KEY is set. |
Product events need one manual step
Section titled “Product events need one manual step”The two variables above enable session recording only. Named product
events (demo_offer_create_attempted, demo_orders_viewed, …) are a separate,
DB-only setting with no env var — an env-configured demo emits none of them
until an admin turns them on. After the stack is up:
- Sign in as an admin and go to
/settings→ PostHog. - Enable the Product events master toggle.
- Tick the event groups you want (they are derived from the event catalog, so the list is always current).
Both the master toggle and an event’s own group must be on before that event
fires. Until then the instrumentation is inert — the demo works normally, it
just reports nothing. See docs/analytics-events.md
for the catalog and the group model.
Only run session recording against synthetic seed data. This is a hard requirement, not a precaution. Recording masks passwords only (#1877) — every other input value and all rendered page text is captured verbatim, because a replay that masks everything shows layout and no content, which makes the recording useless.
So a demo instance pointed at real shop data ships real buyer PII to PostHog cloud: customer names and addresses on the orders and customers surfaces, and tax IDs on the KSeF / invoicing surfaces — plus the non-text signal that was always exposed (order IDs in URLs, product image content, layout).
Never set OL_POSTHOG_KEY on an instance connected to a live
PrestaShop / Allegro / Erli / WooCommerce store with real customer data.
If you need recordings against a realistic dataset, seed it synthetically
rather than relaxing this rule.
A visitor who accepted the consent prompt can revoke it at any time from the demo banner (“Disable” next to “Analytics on”).
See ADR-032 (docs/architecture/adrs/032-demo-only-vendor-neutral-analytics-config-seam.md, merging via #1410) for the design rationale.
3. Boot the stack
Section titled “3. Boot the stack”pnpm demo:up # builds the images and starts everything (first run: several minutes)pnpm demo:logs # follow the logspnpm demo:down # stop the stack (add -v to also wipe the data volumes)pnpm demo:up wraps:
docker compose -f docker-compose.yml -f docker-compose.demo.yml up -d --build \ postgres redis mysql phpmyadmin woocommerce-mysql woocommerce prestashop migrate api worker webBoot order is enforced by depends_on (not the CLI service list):
Postgres healthy → one-shot migrate runs to completion → api + worker
start; PrestaShop and WooCommerce auto-install in parallel; web is static
(nginx) and needs nothing. Migrations run automatically — no manual
migration:run.
Wait until the app tier is healthy:
docker compose -f docker-compose.yml -f docker-compose.demo.yml pscurl -s http://localhost:3000/v1/health # expect {"status":"ok",...}PrestaShop and WooCommerce take the longest (auto-install + seed / plugin download); give them a few minutes on the first run.
4. Service URLs & credentials
Section titled “4. Service URLs & credentials”| Service | URL | Credentials |
|---|---|---|
| OpenLinker admin UI | http://localhost:8090 | admin / admin |
| OpenLinker API | http://localhost:3000 | — (JWT via the UI) |
| PrestaShop storefront | http://localhost:8080 | — |
| PrestaShop admin | http://localhost:8080/**admin-dev** | demo@prestashop.com / prestashop_demo |
| phpMyAdmin | http://localhost:8081 | root / root |
| WooCommerce storefront | http://localhost:8082 | — |
WooCommerce admin (wp-admin) | http://localhost:8082/wp-admin | admin / admin123 |
The PrestaShop admin folder is
admin-dev(the post-install step renames the randomized install folder), not/admin.
5. Wire the PrestaShop connection
Section titled “5. Wire the PrestaShop connection”The PrestaShop ↔ OpenLinker connection is configured manually in the UI. Two manual PrestaShop-side steps first, then the OpenLinker side.
5.1 Enable the PrestaShop Webservice + generate a key
Section titled “5.1 Enable the PrestaShop Webservice + generate a key”- Log in to PrestaShop admin (
http://localhost:8080/admin-dev). - Advanced Parameters → Webservice → set Enable PrestaShop’s webservice = Yes → Save.
- Add new webservice key → Generate! → set Status = Enabled → grant
permissions (the simplest is to check the “all” / view+modify boxes for the
resources you’ll sync:
products,combinations,stock_availables,orders,customers,addresses, …) → Save. Copy the generated key.
5.2 Create the connection in OpenLinker
Section titled “5.2 Create the connection in OpenLinker”In the UI: Connections → New → PrestaShop.
| Field | Value | Notes |
|---|---|---|
| Connection name | e.g. Demo PrestaShop | Free text. |
| Shop URL | http://prestashop | Not http://localhost:8080. The API/Worker containers reach PrestaShop over the Compose network by its service name; localhost inside a container is the container itself. |
| Storefront URL | http://prestashop | Same reason — OpenLinker downloads product image bytes server-side (then re-uploads them to the marketplace CDN), so the image base must be container-reachable. |
| Webservice key | (paste from 5.1) | |
| Shop ID | (blank) | Single-shop install. |
The demo pre-configures PrestaShop so the http://prestashop host resolves the
shop without a canonical-domain redirect (a prestashop-domain ps_shop_url
row + PS_CANONICAL_REDIRECT=0, added by a post-install step), so the
Verify credentials step (GET /api/products?limit=1) succeeds.
Known limitation: with the Storefront URL set to
http://prestashop, image download works from the app tier, but your browser can’t resolveprestashop, so product thumbnails in the OpenLinker UI won’t render. This is a tracked follow-up (decoupling the server-side download base from the browser display base). For the demo, prefer a working image sync over rendered thumbnails.
Allegro vs. Erli — the Storefront URL requirement differs. The guidance above (
http://prestashop) only holds for Allegro: OpenLinker downloads the product image bytes server-side and re-uploads them to Allegro’s CDN, so the URL only needs to be container-reachable, not public.Erli does not re-upload images — its servers fetch the product image URL directly, so for an Erli connection the Storefront URL must be a real, publicly-reachable HTTPS URL.
http://prestashopwill silently fail image sync to Erli. For local testing, front PrestaShop with a quick tunnel (e.g.cloudflared tunnel --url http://localhost:8080); for a persistent deployment, use the reverse-proxy/public-domain overlay (docker-compose.proxy.yml) described indocs/public-domain-demo-deployment-guide.md.
storefrontBaseUrlis a single, connection-level setting shared by every destination synced from that PrestaShop master. If you’re running both Allegro and Erli off the same connection, set it to the public HTTPS URL — a public URL also satisfies Allegro’s (looser) container-reachability requirement, but a container-internal URL does not satisfy Erli’s.
After connecting, OpenLinker syncs the seeded catalog (6 products + variants). See them under Products.
6. Wire the Allegro connection (optional)
Section titled “6. Wire the Allegro connection (optional)”Allegro is an external service — the credentials come from an app you register in the Allegro developer portal; they cannot be seeded. Use the sandbox for testing.
- Create a sandbox account at
https://allegro.pl.allegrosandbox.pl. - In the sandbox developer portal
(
https://apps.developer.allegro.pl.allegrosandbox.pl), create an app of type “application with a browser-based flow (Authorization Code)” — only that type has a Client Secret. - Register the redirect URI exactly as the wizard shows:
http://localhost:8090/integrations/allegro/connect/callback - In OpenLinker: Connections → New → Allegro, paste Client ID + Client Secret, and on the Environment step choose Sandbox. Complete the OAuth redirect.
- Seller defaults — before creating offers, fill the seller-defaults section
on the Allegro connection’s edit page: ship-from location, Responsible
Producer (GPSR), and safety information (GPSR). Offer creation fails with
SELLER_DEFAULTS_NOT_CONFIGUREDuntil these are set. The values come from your Allegro account.
Required category parameters (e.g. “Stan”/Condition). Most Allegro categories require offer parameters like
Stan. In the single-offer wizard (Listings → “Create offer”) you pick a category and fill the parameter step (setStan = Nowy). Supplying these in the bulk flow requires #1367 / #1370.
7. Verify the end-to-end flow
Section titled “7. Verify the end-to-end flow”- Products synced:
Productslists the 6 seeded PrestaShop items, each with aprestashopsource mapping. - Create an offer (with an Allegro sandbox connection): Listings →
“Create offer” → pick a product/variant → resolve/pick the Allegro category →
fill required parameters (
Stan = Nowy) → submit. The Worker processes themarketplace.offer.createjob; watch it with:Terminal window docker compose -f docker-compose.yml -f docker-compose.demo.yml logs -f worker - Sync jobs: the
Diagnostics/ sync-jobs view shows job status and outcome.
8. Troubleshooting
Section titled “8. Troubleshooting”| Symptom | Cause | Fix |
|---|---|---|
Boot aborts; migrate exits 1 with OPENLINKER_CREDENTIALS_ENCRYPTION_KEY is required | The required key is unset. | Set it in .env (section 2), then pnpm demo:up again. |
UI login fails: NetworkError when attempting to fetch resource | API CORS doesn’t allow the UI origin. | Ensure OL_CORS_ORIGIN matches the UI origin (http://localhost:8090; already set in the demo overlay). Hard-refresh the browser. |
mysql reported unhealthy, prestashop/phpmyadmin don’t start | On a slow host, MySQL first-boot init exceeded the healthcheck window. | The demo sets start_period: 180s; if it still trips, retry pnpm demo:up — the second start (initialized volume) is fast. |
PrestaShop admin 404 at /admin | The install folder is renamed to admin-dev. | Use http://localhost:8080/admin-dev. |
Connection Verify credentials fails / webservice 301-redirects | Shop URL set to localhost, or Host≠shop-domain redirect. | Use http://prestashop for Shop URL (section 5.2). Confirm Webservice is enabled and the key is active. |
Offer fails IMAGE_DOWNLOAD_FAILED | Image base not container-reachable (Storefront URL was localhost). | Set the connection’s Storefront URL to http://prestashop and re-sync products. |
Offer fails SELLER_DEFAULTS_NOT_CONFIGURED | Allegro seller-defaults missing. | Fill ship-from location / Responsible Producer / safety information on the connection edit page (section 6). |
Offer fails PARAMETER_REQUIRED (Stan, …) | A required Allegro category parameter has no value. | Set it in the offer wizard’s parameter step (single-offer wizard renders it). |
Useful commands:
docker compose -f docker-compose.yml -f docker-compose.demo.yml ps # service statesdocker compose -f docker-compose.yml -f docker-compose.demo.yml logs migrate # migration outputdocker compose -f docker-compose.yml -f docker-compose.demo.yml logs -f api workercurl -s http://localhost:3000/v1/health9. Teardown
Section titled “9. Teardown”pnpm demo:down # stop the stack, keep data volumespnpm demo:down -v # stop and WIPE the data volumes (fresh start next time)Because the demo shares the openlinker Compose project with pnpm dev:stack:up,
-v wipes volumes shared with the dev stack too — see the warning in section 1.
10. Running on a shared server / multiple instances
Section titled “10. Running on a shared server / multiple instances”By default every published port binds to 127.0.0.1 and every container name
is prefixed openlinker- — safe on a single-user laptop, but two things to
know before running this on a shared/public host (#1400):
- Loopback binding is the default, not an accident.
postgres/redis/mysql/phpmyadmin/prestashop/apiall publish on127.0.0.1so they’re never reachable from outside the host just because a firewall wasn’t configured — put a reverse proxy in front ofweb(andapi, if the browser needs to reach it directly) for anything beyond localhost access. Override the bind interface withOL_BIND_ADDRESSin.envonly if a service genuinely needs to be reachable beyond loopback. - Running a second instance alongside an existing one (e.g. a per-PR
preview environment, or a staging + demo pair on the same host): set a
distinct
COMPOSE_PROJECT_NAME(renames the project and everycontainer_name) plus distinct*_HOST_PORToverrides (POSTGRES_HOST_PORT,REDIS_HOST_PORT,MYSQL_HOST_PORT,PHPMYADMIN_HOST_PORT,PRESTASHOP_HOST_PORT,WOOCOMMERCE_MYSQL_HOST_PORT,WOOCOMMERCE_HOST_PORT,API_HOST_PORT,WEB_HOST_PORT) in that instance’s own.env— see.env.examplefor the full list and defaults.
pnpm dev:stack:seed-woocommerce/pnpm dev:stack:wc-credentialsinvokedocker exec openlinker-woocommercedirectly and don’t yet respect a customCOMPOSE_PROJECT_NAME— a known limitation when running a second instance, tracked as a documented gap rather than fixed here.