Scratch Org Pools
This skill activates when a team needs to reduce CI pipeline latency by pre-creating scratch orgs into a pool that parallel jobs can claim instantly, rather than provisioning a fresh org per job. The canonical pool manager is CumulusCI; the Salesforce CLI does not natively support pooling. This skill covers pool sizing, Dev Hub limit planning, CumulusCI pool commands, CI integration, and monitoring.
Before Starting
Gather this context before working on anything in this domain:
- What Dev Hub edition is in use and what are the current allocation limits? Pool sizing is constrained by the daily creation limit and maximum active org limit. A Developer Edition Dev Hub (6/day, 3 active) cannot meaningfully support pooling.
- How many parallel CI jobs run per day, and what is the peak concurrency? This determines pool size. Each concurrent job consumes one pooled org; orgs are not shared between jobs.
- Is CumulusCI already installed and configured? CumulusCI is the only mainstream tool that provides scratch org pool management. If the project uses raw
sfCLI only, CumulusCI must be added before pooling is possible.
Core Concepts
1. Why Pools Exist
Scratch org provisioning takes 2-8 minutes depending on edition, features, and whether sample data is enabled. In a CI system running 10+ parallel jobs per pull request, this wait compounds into 20-80 minutes of idle pipeline time. A pool pre-creates orgs during off-peak hours so that when a CI job starts, it claims an already-provisioned org in seconds.
Each pooled org is a standard scratch org. There is no special "pooled" org type in Salesforce. The pool is a management abstraction maintained by CumulusCI in its keychain.
2. CumulusCI Pool Architecture
CumulusCI manages pools through a set of commands that interact with the Dev Hub:
cci org pool create— Creates a batch of scratch orgs and stores them in the CumulusCI keychain. Each org is created from the project's scratch org definition file.cci org pool list— Shows all available pooled orgs with their expiration dates.cci org pool get— Claims one org from the pool for use. The org is removed from the pool and becomes a regular CumulusCI org.cci org pool prune— Removes expired or failed orgs from the pool.
Pooled orgs are not initialized until claimed. When a job runs cci org pool get, CumulusCI initializes the org (runs flows like dev_org or ci_org) on first use. This means the pool stores bare scratch orgs with no metadata deployed — initialization happens at claim time.
3. Dev Hub Limits and Pool Math
Every pooled org counts against the Dev Hub's active org limit and daily creation limit. Pool sizing must account for:
| Factor | Formula Component |
|---|---|
| Peak parallel jobs | Minimum pool size = peak concurrency |
| Org expiration | Orgs expire after duration-days; pool must be replenished before expiry |
| Daily creation limit | Total orgs created per day (pool replenishment + ad-hoc) must stay under the daily limit |
| Buffer | Add 20-30% buffer for failed creations and retries |
Example: A team runs 8 parallel CI jobs, each consuming one org. Orgs are created with 1-day duration. The pool needs at least 8 orgs. With 30% buffer, target 11 orgs per replenishment cycle. On an Enterprise Dev Hub (40 daily creates, 20 active), this is sustainable. On a Developer Dev Hub (6 daily, 3 active), it is impossible.
4. Pool Lifecycle
The pool lifecycle follows a create-claim-prune cycle:
- Replenish — A scheduled job (cron, GitHub Actions schedule, or CI cron trigger) runs
cci org pool createto fill the pool to the target size. - Claim — Each CI job runs
cci org pool getto claim an org. If the pool is empty, the job falls back to creating a scratch org on demand. - Use — The job deploys metadata, runs tests, and collects results.
- Dispose — After use, the org is deleted (not returned to the pool). Pooled orgs are single-use.
- Prune — Periodically run
cci org pool pruneto remove expired or failed entries from the keychain.
Common Patterns
Pattern 1: Basic Pool Setup with CumulusCI
When to use: Team has CumulusCI configured, uses GitHub Actions or similar CI, and wants to reduce org provisioning wait time.
How it works:
# Step 1: Create a pool of 10 scratch orgs
cci org pool create dev 10
# Step 2: Verify pool contents
cci org pool list dev
# Step 3: In a CI job, claim one org from the pool
cci org pool get dev --org ci_pool_org
# Step 4: Run the CI flow against the claimed org
cci flow run ci_feature --org ci_pool_org
# Step 5: Delete the org after use
cci org scratch_delete ci_pool_org
# Step 6: Prune stale entries
cci org pool prune dev
Why not create orgs on demand: On-demand creation adds 2-8 minutes per job. With 10 parallel jobs, that is 20-80 minutes of pipeline time wasted on provisioning alone.
Pattern 2: Scheduled Pool Replenishment in GitHub Actions
When to use: Pool must be automatically refilled on a schedule so CI jobs always have orgs available.
How it works:
# .github/workflows/pool-replenish.yml
name: Replenish Scratch Org Pool
on:
schedule:
- cron: '0 4 * * *' # Daily at 4 AM UTC
workflow_dispatch: {} # Allow manual trigger
jobs:
replenish:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- name: Install CumulusCI
run: pip install cumulusci
- name: Authenticate Dev Hub
run: |
echo "${{ secrets.SFDX_AUTH_URL }}" > auth.txt
sf org login sfdx-url --sfdx-url-file auth.txt --alias DevHub --set-default-dev-hub
- name: Prune expired orgs
run: cci org pool prune dev
- name: Replenish pool to target size
run: cci org pool create dev 10
- name: Report pool status
run: cci org pool list dev
Pattern 3: CI Job with Pool Fallback
When to use: CI jobs should use pooled orgs when available but fall back to on-demand creation if the pool is empty.
How it works:
#!/bin/bash
# claim-or-create.sh — used in CI pipeline steps
# Try to claim from pool
if cci org pool get dev --org ci_org 2>/dev/null; then
echo "Claimed org from pool"
else
echo "Pool empty — creating on demand"
cci org scratch dev ci_org --days 1
fi
# Run CI flow
cci flow run ci_feature --org ci_org
# Always clean up
cci org scratch_delete ci_org
Decision Guidance
| Situation | Recommended Approach | Reason |
|---|---|---|
| Dev Hub is Developer Edition (6/day, 3 active) | Do not pool; use on-demand creation | Limits are too low for a meaningful pool |
| Enterprise Dev Hub, 5-10 parallel CI jobs | Pool of 12-15 orgs, replenish daily | Fits within 40/day limit with room for developer use |
| Unlimited Dev Hub, 20+ parallel jobs | Pool of 25-30 orgs, replenish twice daily | Higher limits allow larger pools; twice-daily replenishment keeps pool fresh |
| ISV with Partner Business Org | Pool of 30-50 orgs, replenish every 8 hours | PBOs have 200+ daily creates; larger pools are feasible |
| Team does not use CumulusCI | Evaluate adding CumulusCI or build custom pool script | No native sf CLI pool support exists; CumulusCI is the standard tool |
| Pool orgs expire before being claimed | Reduce --duration-days replenishment interval or increase frequency |
Orgs created with 1-day duration must be claimed within 24 hours |
Recommended Workflow
Step-by-step instructions for an AI agent or practitioner setting up scratch org pools:
- Audit Dev Hub limits — Query
SELECT COUNT() FROM ActiveScratchOrgin the Dev Hub to determine current usage, and confirm the edition to know the daily creation and active org caps. - Determine pool size — Calculate peak CI concurrency, add 30% buffer, and verify the total fits within the Dev Hub's daily and active limits.
- Install and configure CumulusCI — Ensure
cumulusci.ymlexists and thedevorg config points to the correct scratch org definition file. - Create the initial pool — Run
cci org pool create dev <count>and verify withcci org pool list dev. - Set up scheduled replenishment — Create a CI workflow (cron job) that runs pool prune and pool create on a regular schedule.
- Integrate pool claims into CI jobs — Replace
sf org create scratchin CI pipelines withcci org pool getand add a fallback for empty pools. - Monitor and tune — Track pool hit rate (claims vs. fallbacks) and adjust pool size and replenishment frequency based on actual usage.
Review Checklist
Run through these before marking work in this area complete:
- Dev Hub edition supports the planned pool size (daily and active limits verified)
- Pool size accounts for peak concurrency plus 30% buffer
- CumulusCI is installed and
cumulusci.ymlhas correct org definitions - Replenishment schedule runs during off-peak hours and before the next CI peak
- CI jobs include fallback logic for empty pools
- CI jobs delete orgs after use (pooled orgs are single-use)
-
cci org pool pruneruns before each replenishment to clear stale entries - Team members know not to manually create orgs that consume pool allocation
Salesforce-Specific Gotchas
Non-obvious platform behaviors that cause real production problems:
Pooled orgs count against active limits even before being claimed — Every org in the pool is a real active scratch org in the Dev Hub. A pool of 20 orgs uses 20 of the Dev Hub's active org slots, leaving fewer for developers and other pipelines.
Daily creation limit is a rolling 24-hour window, not a midnight reset — If the pool replenishment job creates 30 orgs at 4 AM, you cannot create another 30 at 4 PM on an Enterprise Dev Hub (40/day). Plan replenishment timing around the rolling window.
Failed pool creations still count against the daily limit — If the Dev Hub rejects a creation request (e.g., unsupported feature in the definition file), it still consumes one daily create. A misconfigured definition file can burn through the daily limit producing nothing.
Pool orgs are not initialized until claimed — CumulusCI stores bare scratch orgs in the pool. Metadata deployment and data loading happen at claim time. This means the "instant" claim still requires initialization time — the savings come from skipping the 2-8 minute provisioning step, not from having fully deployed orgs ready.
There is no native
sfCLI pool command — The Salesforce CLI does not include any pool management functionality. Pool management requires CumulusCI or a custom script that tracks orgs externally. Do not advise practitioners to look forsf org poolcommands.
Output Artifacts
| Artifact | Description |
|---|---|
| CumulusCI pool commands | Commands for creating, listing, claiming, and pruning the pool |
| Pool sizing worksheet | Calculation of pool size based on Dev Hub limits and CI concurrency |
| Replenishment workflow YAML | Scheduled CI workflow for automatic pool maintenance |
| CI pipeline integration | Modified CI job steps using cci org pool get with fallback |
| Dev Hub allocation audit | SOQL queries to monitor active org usage against limits |
Related Skills
scratch-org-management— Single-org lifecycle, definition files, allocation troubleshooting; use this when the problem is not about pooling but about individual org provisioningorg-shape-and-scratch-definition— Definition file authoring and Org Shape configuration; the definition file feeds into pool creationgithub-actions-for-salesforce— Full GitHub Actions CI/CD setup; pools integrate into existing pipeline workflowsenvironment-strategy— Broader environment planning across sandboxes, scratch orgs, and Dev Hubs; pools are one component of the strategy