Running multiple local environments
The main checkout of nexgen_v3 runs the dev environment described in Nexgen V3 local development. This page covers the three extra environments, copy1, copy2, and copy3, that git worktrees of the same repo can use. Each one has its own databases, its own backend containers, and its own ports, so work in one worktree never changes the data another worktree is testing against.
One script, docker/worktree-env.sh in the nexgen_v3 repo, creates the databases, assigns an environment to a worktree, and starts it.
How the pieces fit
The dev environment stays as it is: the main checkout, the default ports, and the databases nexgen_v3, nexgen_free_v3, nexgen_test, and nexgen_mdb_v3.
Environment N, where N is 1, 2, or 3, has:
- Three MySQL databases on the native server:
nexgen_v3_copyN(paid app),nexgen_free_v3_copyN(trial app), andnexgen_test_copyN(the isolated database for the backend tests). - One MongoDB database on the native server,
nexgen_mdb_v3_copyN, shared by that environment's paid and trial backends, the same way dev sharesnexgen_mdb_v3. - Its own Docker Compose project,
npv3-copyN, with five containers:nexgen-backend,nexgen-free-backend,api,mcp-server, andmailpit. The container names arenpv3-copyN-<service>. - The frontends, which run on the Mac with
ng serve. No frontend containers are started. - The dev ports plus N x 100.
The ports for each environment:
- Paid frontend: 4200 on dev, then 4300, 4400, and 4500.
- Trial frontend: 4201 on dev, then 4301, 4401, and 4501.
- Paid backend: 8010 on dev, then 8110, 8210, and 8310.
- Trial backend: 8011 on dev, then 8111, 8211, and 8311.
- The api service: 5050 on dev, then 5150, 5250, and 5350.
- The mcp-server: 3001 on dev, then 3101, 3201, and 3301.
- Mailpit: 8025 on dev, then 8125, 8225, and 8325.
The MySQL and MongoDB servers are shared. Only the database names differ, so one pair of servers holds the dev data and all three copies.
How a worktree knows its environment
Assigning an environment writes a line such as NPV3_ENV=copy1 to the gitignored .env file in the worktree root, together with that environment's Compose project name, URLs, and database names. Three tools read it, so commands run inside the worktree reach its own stack with no extra flags:
- Docker Compose reads the root
.envfor the project name.docker compose exec nexgen-backend ...run inside the worktree reaches thenpv3-copyNcontainers. - Playwright loads the root
.envbeforeplaywright.local.env, so the backend, frontend, Mailpit, and MCP URLs point at the worktree's own ports. - The backend containers carry
NPV3_DB_SUFFIX=_copyN. The PHPUnit setup appends it to the database a PHPUnit config names, sonexgen_testbecomesnexgen_test_copyNand the seeded config'snexgen_v3becomesnexgen_v3_copyN.
Assigning also writes these gitignored files into the worktree:
docker-compose.override.yml, which gives the stack its container names, ports, and database names, and keeps the database and frontend containers from starting.nexgen/backend/.envanddocker/nexgen-free.env, copied from the main checkout. A new worktree does not have them.nexgen/frontend/src/environments/environment.local.ts,environment-trial.local.ts, andnexgen/frontend/proxy.conf.local.json, which pointng serveat the worktree's own backends. The trackedenvironment.tsfiles are left alone.
docker/worktree-env.sh status prints which worktree has which environment, and the URLs and databases of the one it is run from.
Checking which environments are running
status shows assignments only, not whether an environment's containers are up. Each environment is a Docker Compose project, named nexgen_v3 for the dev environment and npv3-copy1 to npv3-copy3 for the worktrees, so Docker can list the running containers grouped by environment:
docker ps --format '{{.Label "com.docker.compose.project"}}\t{{.Names}}\t{{.Status}}' | sort
For a count of running and stopped containers per environment, including environments that are stopped:
docker ps -a --format '{{.Label "com.docker.compose.project"}}\t{{.State}}' | sort | uniq -c
An environment that has been assigned but never started does not appear in either list. A worktree environment that is up has five containers. The dev environment has more, because it also runs the frontends, S3, and any database containers.
Why the test database is copied too
nexgen_test cannot be shared by worktrees running at the same time. The backend feature tests rebuild it with migrate:fresh at the start of a run, which drops every table, and worktrees on different branches carry different migrations. Each environment gets its own nexgen_test_copyN. It is created empty and the first test run migrates it.
Before you start
- A working dev environment with the native databases, set up as described in Nexgen V3 local development. The copies are made from the dev databases, and the worktrees take their environment files from the main checkout.
- The
nexgen_v3commit that addeddocker/worktree-env.shon the branch each worktree is created from. A worktree on an older branch lacks the Angularlocalbuild configurations, the PHPUnit database suffix, and the Playwright URL settings. - Node.js on the Mac for
ng serve. Node.js 20 is known to work. - Disk space. Each environment adds about 2 GB of databases, and each worktree adds its own
vendorandnode_modulesfolders and its own Docker images. - Memory. Each
ng servegrows to about 4.4 GB, close to the 4 GB heap limit the script gives it. An environment's five containers together use under 1 GB.
Setting up
1. Create the databases
Run this once for each environment, from the main checkout. Each run copies the dev MySQL and MongoDB databases, which takes a few minutes.
cd ~/git/axxya/nexgen_v3
docker/worktree-env.sh db-create 1
docker/worktree-env.sh db-create 2
docker/worktree-env.sh db-create 3
For environment N, the command:
- Creates the three MySQL databases and grants
npuser,mcp_readonly, andflask_readonlythe same rights they have on the dev databases. - Copies
nexgen_v3andnexgen_free_v3into the two app databases.nexgen_test_copyNis left empty. - Creates the
nexgenandflask_writerusers on the MongoDB copy, then copiesnexgen_mdb_v3into it, indexes included.
The command is safe to run again. It skips any copy that already holds data, so it never overwrites one. The copies hold the dev data as it was when they were made. To bring one up to date later, see Resetting an environment's data.
The script connects to MySQL as root with the development password from docker-compose.yml. If the local root password differs, set NPV3_MYSQL_ROOT_PASSWORD for the command.
2. Create a worktree
Create the worktree in Supacode with the new worktree button, or from a Supacode terminal:
supacode repo worktree-new --branch ai/copy1 --base main
Supacode puts the worktree in ~/.supacode/repos/nexgen_v3/<branch name>/, so the example above lands in ~/.supacode/repos/nexgen_v3/ai/copy1. The folder keeps that name if the branch is renamed later.
A worktree created with git worktree add works with the script in the same way. It did not appear in the Supacode sidebar when this was set up, so use Supacode to create worktrees that should be managed there.
The branch name has no effect on the environment. The script never reads it.
3. Assign an environment to the worktree
From the main checkout, give the environment number and the worktree path:
cd ~/git/axxya/nexgen_v3
docker/worktree-env.sh assign 1 ~/.supacode/repos/nexgen_v3/ai/copy1
docker/worktree-env.sh status
assign writes the files listed under How a worktree knows its environment. It refuses to assign an environment that another worktree already holds, and it refuses to assign one to the main checkout.
4. Start the containers
cd ~/.supacode/repos/nexgen_v3/ai/copy1
docker/worktree-env.sh up
curl -s http://localhost:8110/version
up builds the environment's images, starts its five containers, runs composer install if the worktree has no vendor folder, and prepares the storage folders. The /version response names the database, which should be nexgen_v3_copy1 on port 8110 and nexgen_free_v3_copy1 on port 8111.
The first up on a machine is slow. The backend image is built for linux/amd64 under emulation, which took about 17 minutes the first time. Later environments reuse the cached build steps.
5. Start a frontend
serve runs in the foreground, so give it its own terminal. Start only the frontend the work needs.
cd ~/.supacode/repos/nexgen_v3/ai/copy1
docker/worktree-env.sh serve paid
Use serve trial for the trial app. The first run in a worktree does an npm install. When the compile finishes, the paid app for copy1 is at http://localhost:4300 and the trial app at http://localhost:4301. Stop a frontend with Ctrl+C.
6. Check the environment
Log in at the paid frontend with the same seeded logins as dev, for example test@axxya.com / testpass@word1. Then run a backend test and a workflow suite from the worktree:
cd ~/.supacode/repos/nexgen_v3/ai/copy1
npm install
docker compose exec nexgen-backend php artisan test tests/Feature/ShopInactiveProductTest.php
npx playwright test --project=nexgen-workflow
The npm install in the worktree root installs Playwright, and is needed once for each worktree.
Using an environment
Finding out which environment a checkout is in
docker/worktree-env.sh status
Run it from any checkout. It lists every worktree with its environment, and for the checkout it is run from it prints the URLs and database names. The main checkout reports the dev environment. A worktree with no environment reports that none is assigned, and nothing should be run from it against the dev stack.
The repo's CLAUDE.md tells a Claude Code session to run this command before it starts the apps or any tests, so a session working in a worktree uses that worktree's environment and leaves the dev databases alone.
Running commands in the containers
Inside a worktree, use docker compose exec with the service name. Compose resolves it to that environment's container.
docker compose exec nexgen-backend php artisan migrate --force
docker compose exec nexgen-free-backend php artisan migrate --force
docker compose exec nexgen-backend php artisan view:clear
docker compose logs -f nexgen-backend
docker exec npv3-nexgen-backend ... always reaches the dev stack, whichever folder it is run from. In a worktree environment, use docker compose exec nexgen-backend ..., or the full container name npv3-copyN-nexgen-backend.
The Laravel log for an environment is in nexgen/backend/storage/logs/ inside its own worktree.
Running the tests
The usual commands work unchanged inside the worktree:
docker compose exec nexgen-backend php artisan test
docker compose exec nexgen-backend ./vendor/bin/pest -c phpunit-seeded.xml
npx playwright test --project=nexgen-workflow
npx playwright test --project=nexgen-free-workflow
php artisan testruns againstnexgen_test_copyN.- The seeded guards run against
nexgen_v3_copyN. - The Playwright projects log in through the environment's backend ports and drive its frontends, so the matching frontend must be running: the paid one for the
nexgenprojects and the trial one for thenexgen-freeprojects.
The cluster projects, such as nexgen-staging-cluster, run against staging or the test cluster from any checkout. On a cluster run, Playwright ignores the worktree's local URLs and takes them from playwright.cluster.env.
Email
Each environment has its own Mailpit, which catches every email its two backends send. For copy1 it is at http://localhost:8125.
Connecting to the databases
The logins are the same as dev. Only the database names change.
mysql -h 127.0.0.1 -P 3306 -u npuser -pnppassword nexgen_v3_copy1
mongosh "mongodb://nexgen:nexgen@127.0.0.1:27017/nexgen_mdb_v3_copy1?authSource=nexgen_mdb_v3_copy1"
After pulling changes or switching branches
A copy starts with the dev schema as it was when the copy was made. If the worktree's branch adds migrations, run them in both backend containers, and run the seeders if the branch changed them:
docker compose exec nexgen-backend php artisan migrate --force
docker compose exec nexgen-free-backend php artisan migrate --force
docker compose exec nexgen-backend php artisan db:seed --force
docker compose exec nexgen-free-backend php artisan db:seed --force
The two backends mount the worktree's source folder, so PHP changes take effect as they do on dev. The api and mcp-server images hold a copy of their source. After changing either one, run docker/worktree-env.sh up again to rebuild them.
Stopping and starting
docker compose stop
docker/worktree-env.sh up
Run both from inside the worktree. stop turns the environment's containers off and keeps them. A stopped environment uses no memory, and its databases stay on the native servers.
Using several environments in one browser
Each environment's backends use their own session cookie names, so one browser can stay logged in to dev and to every copy at the same time.
Resetting an environment's data
db-reset drops the environment's four databases and copies them from dev again. Use it when test data has built up in a copy, or to pick up newer dev data.
cd ~/git/axxya/nexgen_v3
docker/worktree-env.sh db-reset 1
Everything in nexgen_v3_copy1, nexgen_free_v3_copy1, nexgen_test_copy1, and nexgen_mdb_v3_copy1 is lost. The command asks for the environment name to be typed before it does anything, and it can only ever drop a database whose name ends in _copy1, _copy2, or _copy3. The dev databases are only read.
Stop the environment's containers first, and run the migrations afterwards if the worktree's branch has any that dev does not.
Moving an environment to another worktree
Stop the containers, release the environment, then assign it to the other worktree:
cd ~/.supacode/repos/nexgen_v3/ai/copy1
docker compose stop
docker/worktree-env.sh release
cd ~/git/axxya/nexgen_v3
docker/worktree-env.sh assign 1 /path/to/other/worktree
release removes the environment lines from the worktree's .env and deletes the generated override and frontend files. The databases are left as they are, so the next worktree starts with the data the last one left. Run db-reset first for a clean start.
Running the script for another worktree
assign, release, up, and serve take a worktree path as their last argument, so the main checkout's copy of the script can drive any worktree. This is useful when a worktree's own copy of the script is older:
~/git/axxya/nexgen_v3/docker/worktree-env.sh up ~/.supacode/repos/nexgen_v3/ai/copy2
~/git/axxya/nexgen_v3/docker/worktree-env.sh serve paid ~/.supacode/repos/nexgen_v3/ai/copy2
What the environments share
- The MySQL and MongoDB servers, and their memory limits. Heavy work in one environment can slow the others.
- The Chargebee test site, the Anthropic API key, and every other key in the backend environment files, which are copied from the main checkout. Tests that create Chargebee objects in two environments are writing to the same Chargebee site.
- The V2 apps, which always use the dev databases.
Troubleshooting
ng servestops with "JavaScript heap out of memory". The build needs more heap than it was given. Set a larger limit for the command, for exampleNODE_OPTIONS=--max-old-space-size=6144 docker/worktree-env.sh serve paid.ng servereports that the configurationlocalis not set. The worktree's branch predates the script. Bring the branch up to date withmain.- "has no environment assigned".
upandserveonly run in a worktree that holds an environment. Runassignfor it first. - "is already assigned to" another worktree. Release it there first, as described under Moving an environment to another worktree.
- "docker-compose.override.yml exists and was not generated by this script". The worktree has a hand-written override file. Move it aside, then run
assignagain. - A new worktree does not appear in Supacode. It was created with plain git. Create it through Supacode instead.
- A test run changed the dev data. The run was started from a checkout with no environment, or from the main checkout. Check with
docker/worktree-env.sh status. db-createskipped a copy that is only partly loaded. An interrupted run leaves some tables behind, and the next run sees data and skips the load. Usedb-resetfor that environment.- A port is already in use. Another program holds one of the environment's ports.
lsof -nP -iTCP:8110 -sTCP:LISTENshows which.
Related
- Nexgen V3 local development for the dev environment and the native databases that these environments build on.
- Nexgen V3 databases for the staging and production databases.
- The "Worktree environments" section of the nexgen_v3 README for the same commands beside the code.