Skip to main content

Upgrading a stack9 instance

This guide describes how to upgrade an existing stack9 instance (e.g. stack9-dxp, eagers-dealership-dxp-stack9, or any other repo scaffolded from stack9-instance-template) to the latest core version and dev tooling.

The upgrade is a single command:

yarn stack:up
# or: stack9 stack up

stack9 stack up syncs the canonical files from stack9-instance-template into the instance — Vite config, Dockerfiles, root scripts, env templates — and bumps the @april9au/stack9-* package pins. It is safe to re-run; instance-owned files (e.g. your docker-compose.yml, your app.stack9.instance.tsx) are not overwritten.

If you're upgrading an old CRA-based instance for the first time, the CLI also runs a one-shot migration:

  • Removes craco.config.js, react-scripts, and other CRA artifacts
  • Renames postcss.config.js / tailwind.config.js → .cjs
  • Adds vite, @vitejs/plugin-react, dev / dev:local / build / preview scripts to apps/stack9-frontend/package.json
  • Removes lerna.json (NX is now the canonical task runner)
  • Drops apps/stack9-frontend/.env.development (replaced by the new .env.local / .env loading the stack9 CLI does before each command)

After the command finishes, review the diff, run yarn install, and boot dev (see below).


Boot dev​

yarn dev

That single command:

  1. Loads .env.local and .env into process.env (the stack9 CLI's env loader)
  2. Brings up postgres + redis + the backend container in Docker (docker-compose.dev.yml + docker-compose.dev.backend.yml)
  3. Runs the frontend Vite dev server on the host
  4. Wires Ctrl+C to stop both processes and the docker containers cleanly (scripts/dev.mjs)

Frontend defaults to http://localhost:3333; backend to http://localhost:3336. Override via STACK9_PORT and STACK9_API_PORT in .env.local.

To run multiple instances side-by-side, also bump STACK9_PG_PORT, STACK9_REDIS_PORT, STACK9_CONSOLE_PORT, and set STACK9_PROJECT_NAME to a unique value.


Configuring secrets — two equally-supported paths​

Both paths are fully optional and mutually compatible. Pick whichever fits your team.

Path A — manual .env.local​

Copy .env.example → .env.local and fill in the values your team already shares (in 1Password, a team wiki, etc.). The platform reads every variable from .env.local at boot — no AWS round-trip required.

This is the right default for solo developers, security-restricted environments, or teams that don't use AWS SSM Parameter Store.

Path B — yarn setup (AWS SSM Parameter Store)​

For teams already on AWS, the yarn setup workflow pulls shared secrets from SSM Parameter Store into .env.local for you:

aws sso login --profile <profile>
yarn setup --profile=<profile>

This generates .env.local from /stack9/<project>/local-shared/* and CDK outputs (per-developer S3 buckets, SQS queues, etc.). Re-run any time to refresh; existing values in .env.local are preserved unless you pass --force.

The supporting yarn secrets:* commands (stack9 secrets push/list/rm/migrate) push, list, prune, and migrate snapshots — see the secrets workflow reference at the bottom of this page.

Neither yarn install nor yarn dev invokes yarn setup. The SSM path is strictly opt-in.


APP_* configuration lives in env-vars.json, not .env.local​

Important. Application-level config (APP_* keys consumed by your stack9-stack at runtime — APP_DXP_*, APP_S9_*, etc.) does not live in .env.local and is not pulled from SSM by yarn setup. It lives in:

  • packages/stack9-stack/src/env-vars.json — committed baseline, read by the platform on boot
  • packages/stack9-stack/src/env-vars.local.json — gitignored per-developer overrides (copy env-vars.local.json.example to start)

Sensitive APP_* values must be marked "sensitive": true and stored as { iv, encryptedData } in env-vars.json — the platform decrypts them at runtime.

In local development the legacy APP_SECRETS env-var blob is blocked: setting it in .env / .env.local would silently bypass env-vars.json, so the core refuses to start. Deployed environments may still receive APP_SECRETS in the process environment, for example from secrets manager via Octopus; Stack9 ignores it in favour of env-vars.json.


Switching between local and npm-live mode​

By default, apps/stack9-frontend consumes @april9au/stack9-{ui, react,sdk} from the dist/ ESM bundles in node_modules — fast startup, no rebuild loop, but no HMR for those packages.

If you need to test a stack9-monorepo change against a real instance without publishing a prerelease, use STACK9_LOCAL mode:

yarn dev:local
# ↔ STACK9_LOCAL=true yarn dev

When STACK9_LOCAL=true, the Vite config aliases each @april9au/stack9-{ui,react,sdk} import to its src/ inside a sibling stack9-monorepo checkout — edits hot-reload directly without rebuild or publish.

Sibling-repo layout it expects (relative to apps/stack9-frontend/):

../../../stack9-monorepo/packages/stack9-ui/src/index.tsx
../../../stack9-monorepo/packages/stack9-react/src/index.tsx
../../../stack9-monorepo/packages/stack9-sdk/src/index.ts

Override the path via STACK9_LOCAL_PATH in .env.local if your checkout differs.

yarn dev (no :local) returns to package mode. The toggle is per-session — no yarn install needed to switch.

STACK9_LOCAL is an inner-loop convenience, not a release workflow. Publish + pin remains the canonical cross-repo path; see DEVELOPMENT.md in stack9-monorepo.


Secrets workflow reference​

CommandWhat it does
yarn setupPull shared parameters from SSM into .env.local. Idempotent. Add --env=<name> to load a teammate snapshot.
yarn secrets:pullRefresh shared parameters only (no CDK lookup).
yarn secrets:pushPush current .env.local shared values back to SSM. Add --env=<name> for a named snapshot, --dry-run to preview, --prune to delete orphans.
yarn secrets:listList every snapshot under /stack9/<project>/local*. Add --stale to filter to >90 days.
yarn secrets:rmDelete every parameter under a snapshot prefix.
yarn secrets:migrateOne-time bootstrap: scan existing .env* files → SSM Parameter Store. Run once per team; teammates only need yarn setup after that.

Common knobs​

KnobWhereDefaultPurpose
STACK9_PORT.env.local3333Frontend Vite dev server port
STACK9_API_PORT.env.local3336Core API HTTP port
STACK9_CONSOLE_PORT.env.local3334Admin console port (host-side mapping only — internal port is hardcoded to 3334)
STACK9_PG_PORT.env.local5432Postgres host-side port
STACK9_REDIS_PORT.env.local6379Redis host-side port
STACK9_PROJECT_NAME.env.local<repo-name>Docker Compose project name + SSM prefix /stack9/<project>/.... Bump per-instance.
STACK9_HOSTNAME.env.locallocalhostHostname for SERVER_BASE_URL and auth-cookie domain
STACK9_LOCALshell or env-fileunsetWhen true, frontend Vite resolves @april9au/stack9-{ui,react,sdk} to local src/
STACK9_LOCAL_PATH.env.local../../../stack9-monorepoSibling stack9-monorepo path (relative to apps/stack9-frontend/)

Troubleshooting​

  • yarn dev warns [with-env] Warning: AUTH_SECRETS is not set — populate AUTH_SECRETS in .env.local (see .env.example for shape) or run yarn setup --profile=<p>.
  • Container ports collide between instances — set unique STACK9_PROJECT_NAME and bump all STACK9_*_PORT values in .env.local. with-env.mjs will automatically stop conflicting containers from other compose projects on the same ports.
  • Vite "does not provide an export" at boot — usually a duplicate copy of @april9au/stack9-react is loaded. Check the dedupe list in apps/stack9-frontend/vite.config.ts.
  • STACK9_LOCAL boot crash — usually a CJS dep that Vite's stub doesn't synthesise a default export for. Add the dep to the local- src optimizeDeps.include block in vite.config.ts.