> ## Documentation Index
> Fetch the complete documentation index at: https://namespace.so/docs/llms.txt
> Use this file to discover all available pages before exploring further.

# Creating Many Instances Concurrently

> Fan out instance creation with a bounded worker pool, run work on each, and destroy them when done.

Instances are created one call at a time, and `CreateInstance` returns while the machine boots.
That makes fan-out a client-side concern: launch the calls concurrently, bound how many are in flight, and destroy what you created.

This is the shape behind sharded test runs and one-sandbox-per-task agent workloads.

<Steps titleSize="h3">
  <Step title="Bound the concurrency">
    Do not launch an unbounded number of creations. A workspace has concurrency limits, and exceeding them fails calls that a bounded pool would have completed.

    <CodeGroup>
      ```typescript TypeScript theme={null}
      const COUNT = 100;
      const CONCURRENCY = 20;

      async function pool<T>(items: T[], limit: number, fn: (item: T) => Promise<void>) {
        const queue = [...items];
        const workers = Array.from({ length: limit }, async () => {
          for (let item = queue.shift(); item !== undefined; item = queue.shift()) {
            await fn(item);
          }
        });
        await Promise.all(workers);
      }
      ```

      ```go Go theme={null}
      count := 100
      maxConcurrent := 20

      var (
      	mu          sync.Mutex
      	instanceIDs []string
      	wg          sync.WaitGroup
      )

      sem := make(chan struct{}, maxConcurrent)
      for i := 0; i < count; i++ {
      	wg.Add(1)
      	sem <- struct{}{}
      	go func(idx int) {
      		defer wg.Done()
      		defer func() { <-sem }()

      		id, err := runOne(ctx, computeClient, token, idx)

      		mu.Lock()
      		defer mu.Unlock()
      		if id != "" {
      			instanceIDs = append(instanceIDs, id)
      		}
      		if err != nil {
      			fmt.Fprintf(os.Stderr, "[%3d] FAIL: %v\n", idx, err)
      		}
      	}(i)
      }

      wg.Wait()
      ```
    </CodeGroup>

    <Warning>
      Every instance counts against your workspace concurrency limit while it exists, and each one bills for its lifetime, not for the work it did. Keep the deadline short and destroy instances explicitly rather than leaving them to expire.
    </Warning>
  </Step>

  <Step title="Do the work on each instance">
    One unit of work is a create, an exec, and a record of the instance id so it can be destroyed later.
    Going straight from `CreateInstance` to `RunCommandSync` avoids a separate readiness wait, as described in [run a command in an instance](/docs/platform/instances/examples/run-commands).

    <CodeGroup>
      ```typescript TypeScript theme={null}
      async function runOne(idx: number): Promise<string> {
        const created = await computeClient.compute.createInstance({
          shape: { virtualCpu: 1, memoryMegabytes: 2048, machineArch: "amd64" },
          documentedPurpose: `shard ${idx}`,
          deadline: timestampFromDate(new Date(Date.now() + 60 * 60 * 1000)),
          containers: [
            { name: "ubuntu", imageRef: "ubuntu:latest", args: ["sleep", "3600"] },
          ],
        });

        const instanceId = created.metadata!.instanceId;
        const endpoint = created.extendedMetadata?.commandServiceEndpoint;
        if (!endpoint) {
          throw new Error("no command service endpoint");
        }

        // Open a command client against `endpoint` and run the shard here.

        return instanceId;
      }
      ```

      ```go Go theme={null}
      func runOne(ctx context.Context, computeClient compute.Client, token api.TokenSource, idx int) (string, error) {
      	resp, err := computeClient.Compute.CreateInstance(ctx, &computepb.CreateInstanceRequest{
      		Shape: &computepb.InstanceShape{
      			VirtualCpu:      1,
      			MemoryMegabytes: 2 * 1024,
      			MachineArch:     "amd64",
      		},
      		DocumentedPurpose: fmt.Sprintf("shard %d", idx),
      		Deadline:          timestamppb.New(time.Now().Add(1 * time.Hour)),
      		Containers: []*computepb.ContainerRequest{{
      			Name:       "ubuntu",
      			ImageRef:   "ubuntu:latest",
      			Entrypoint: []string{"sleep", "3600"},
      			Args:       []string{},
      		}},
      	})
      	if err != nil {
      		return "", fmt.Errorf("create: %w", err)
      	}

      	endpoint := resp.ExtendedMetadata.GetCommandServiceEndpoint()
      	if endpoint == "" {
      		return resp.Metadata.InstanceId, fmt.Errorf("no command service endpoint")
      	}

      	// Open a command client against `endpoint` and run the shard here.

      	return resp.Metadata.InstanceId, nil
      }
      ```
    </CodeGroup>

    Pass `labels` on the create request if you want to find these instances later. `ListInstances` filters on them with `labelFilter`.
  </Step>

  <Step title="Destroy the instances">
    Destroy concurrently as well. Use a context that is not already cancelled, so a cancelled run still cleans up after itself.

    <CodeGroup>
      ```typescript TypeScript theme={null}
      await pool(instanceIds, CONCURRENCY, async (instanceId) => {
        await computeClient.compute.destroyInstance({ instanceId, reason: "run complete" });
      });
      ```

      ```go Go theme={null}
      var destroyWg sync.WaitGroup
      for _, id := range instanceIDs {
      	destroyWg.Add(1)
      	go func(id string) {
      		defer destroyWg.Done()
      		_, err := computeClient.Compute.DestroyInstance(context.Background(), &computepb.DestroyInstanceRequest{
      			InstanceId: id,
      			Reason:     "run complete",
      		})
      		if err != nil {
      			fmt.Fprintf(os.Stderr, "  destroy %s: %v\n", id, err)
      		}
      	}(id)
      }
      destroyWg.Wait()
      ```
    </CodeGroup>
  </Step>
</Steps>

## Source

`go/benchmark` in [github.com/namespacelabs/examples](https://github.com/namespacelabs/examples) runs this pattern over 100 instances and reports percentile timings for the create and exec phases.
It also sets `experimental.privateFeature`, which is gated by the Namespace team and not available by default; the pattern above does not need it.

See [resource limits](/docs/platform/compute/resource-limits) for workspace concurrency limits.


This documentation is built and hosted on [Mintlify](https://mintlify.com), a developer documentation platform.