How to Run Multiple Macro Stacks in Parallel

To run multiple Macro stacks in parallel, export a unique MACRO_STACK_NAME environment variable for each instance before executing ./.cursor/stack.sh, which generates isolated Docker networks and Pulumi state files via the --stack-name argument assembled in .cursor/cloud-lib.sh.

The macro-inc/macro repository orchestrates its full development environment—databases, auxiliary services, and micro-services—through a unified stack abstraction managed by Pulumi. While the default just stack up command launches a single instance, the underlying scripts support running multiple Macro stacks in parallel by parameterizing the stack identifier, enabling isolated testing environments on a single machine.

How the Stack Architecture Enables Parallel Execution

The Macro stack system uses a three-layer architecture that separates orchestration logic from resource provisioning. Understanding this flow clarifies how parallel instances avoid conflicts.

Layer 1: Entry Point
.cursor/stack.sh serves as the primary interface. It builds local binaries, prepares environment arguments through Doppler integration, and delegates to just stack up. Critically, it sources arguments from cloud-lib.sh via the ${doppler_args[@]} array (lines 19‑22), which includes the stack identifier.

Layer 2: Argument Generation
.cursor/cloud-lib.sh contains the stack_doppler_args function that assembles the final command-line arguments for Pulumi. This function injects --stack-name $MACRO_STACK_NAME into the argument list (lines 235‑244), ensuring each invocation targets a distinct Pulumi state file and Docker project name.

Layer 3: Resource Provisioning
infra/preview/entrypoint.sh receives these arguments and invokes the Rust-based xtask stack up binary. This binary creates Docker networks, volumes, and containers prefixed with the stack name (lines 102‑106), providing complete resource isolation between instances.

Prerequisites

Before launching parallel stacks, ensure your environment meets these requirements:

  • Docker Engine and Docker Compose installed and running.
  • Doppler CLI configured (optional, but the scripts reference Doppler for secrets management).
  • The Just command runner installed to process the justfile targets.
  • Sufficient system resources (RAM, CPU) to host multiple database and service instances simultaneously.

Step-by-Step: Launching Multiple Isolated Stacks

Running parallel instances requires setting unique identifiers and optionally customizing exposed ports to avoid host-level binding conflicts.

  1. Define unique stack names – Export MACRO_STACK_NAME with a distinct value for each instance (e.g., dev1, dev2, feature-test).
  2. Adjust host ports – Export MACRO_HTTP_PORT and other port variables to prevent binding collisions on the host machine.
  3. Execute the stack script – Run ./.cursor/stack.sh for each configuration. The script backgrounds processes automatically when invoked with &.

# Build binaries once (shared across stacks)

./.cursor/stack.sh build_local_stack_binaries

# Launch Stack 1

export MACRO_STACK_NAME=dev1
export MACRO_HTTP_PORT=8081
./.cursor/stack.sh &
echo "Stack dev1 launching on http://localhost:8081"

# Launch Stack 2

export MACRO_STACK_NAME=dev2
export MACRO_HTTP_PORT=8082
./.cursor/stack.sh &
echo "Stack dev2 launching on http://localhost:8082"

Each process creates independent Docker networks (macro-dev1, macro-dev2) and volumes (macro-postgres-dev1, etc.), ensuring complete isolation.

Understanding Resource Isolation Mechanisms

Parallel stacks do not share state due to distinct naming conventions enforced at the infrastructure level.

Docker Project Isolation
The docker compose command receives the --project-name parameter derived from MACRO_STACK_NAME. This prefixes all container names, network names, and volume labels, preventing cross-stack interference.

Pulumi State Separation
Pulumi stores stack state in files named after the --stack-name argument. By passing distinct names via cloud-lib.sh, each stack maintains its own infrastructure state, allowing independent updates and destruction.

Network Segregation
As implemented in infra/preview/entrypoint.sh, each stack creates a dedicated bridge network. Containers within one stack cannot communicate with another stack’s containers unless explicitly linked through external networks.

Customizing Environment Variables for Each Stack

When running multiple instances, configure these environment variables before invoking the stack script:

Variable Purpose Example
MACRO_STACK_NAME Required. Unique identifier for the stack. export MACRO_STACK_NAME=local-dev
MACRO_HTTP_PORT Host port mapping for the web interface. export MACRO_HTTP_PORT=8080
MACRO_POSTGRES_PORT Host port for PostgreSQL access. export MACRO_POSTGRES_PORT=5432
MACRO_REDIS_PORT Host port for Redis access. export MACRO_REDIS_PORT=6379

Setting these before ./.cursor/stack.sh ensures the generated doppler_args array contains the correct overrides for that specific instance.

Stopping and Cleaning Up Specific Stacks

To tear down a specific parallel stack without affecting others, export its identifier before running the down command:


# Stop only the dev1 stack

export MACRO_STACK_NAME=dev1
just stack down

# Verify removal

docker ps | grep macro-dev1  # Should return no results

The just stack down target invokes the same xtask binary with the down command, targeting only the resources tagged with the specified stack name.

Summary

  • .cursor/stack.sh acts as the entry point that prepares binaries and delegates to the Just-based workflow.
  • .cursor/cloud-lib.sh generates the critical --stack-name argument from the MACRO_STACK_NAME environment variable, ensuring Pulumi and Docker isolate resources.
  • infra/preview/entrypoint.sh executes the actual provisioning logic, creating prefixed networks and volumes for each stack instance.
  • Port conflicts are avoided by setting MACRO_HTTP_PORT and related variables uniquely per stack.
  • Cleanup is scoped per stack using the same MACRO_STACK_NAME variable with just stack down.

Frequently Asked Questions

Can I run more than two Macro stacks simultaneously?

Yes. You can run as many stacks as your hardware resources allow. Each stack requires RAM for its PostgreSQL, Redis, and application containers, plus CPU cycles. Ensure you assign unique MACRO_STACK_NAME values and distinct host ports for each additional instance.

Do I need separate Doppler projects for each parallel stack?

No. The Doppler configuration remains shared; isolation occurs through the MACRO_STACK_NAME variable passed to Pulumi. However, if you need stack-specific secrets, you can modify cloud-lib.sh to append additional Doppler project flags based on the stack identifier before line 235.

How do I view logs for a specific stack?

Use Docker Compose commands with the project name filter. For a stack named dev1, run:

docker compose -p macro-dev1 logs -f

Alternatively, filter docker ps using grep macro-dev1 to identify specific container IDs, then use docker logs <container-id>.

Will parallel stacks conflict on internal service ports?

No. Internal container ports (e.g., Postgres 5432, Redis 6379) are isolated within each stack’s dedicated Docker network. Conflicts only occur on host-bound ports (the mappings you define with MACRO_HTTP_PORT, etc.), which is why you must specify unique host ports for each stack.

Have a question about this repo?

These articles cover the highlights, but your codebase questions are specific. Give your agent direct access to the source. Share this with your agent to get started:

Share the following with your agent to get started:
curl -s "https://instagit.com/install.md"

Works with
Claude Codex Cursor VS Code OpenClaw Any MCP Client

Maintain an open-source project? Get it listed too →