Defining a pool

A scratch org pool is described by a pool definition file — a JSON file kept in the repository and validated against pooldefinition.schema.json. The definition below describes a pool tagged DEV-POOL with a capacity of 20 scratch orgs.

{
    "tag": "DEV-POOL",
    "maxAllocation": 20,
    "expiry": 10,
    "batchSize": 10,
    "configFilePath": "config/project-scratch-def.json",
    "relaxAllIPRanges": true,
    "installAll": true,
    "enableSourceTracking": true,
    "retryOnFailure": true,
    "succeedOnDeploymentErrors": true,
    "fetchArtifacts": {
        "npm": {
            "scope": "myproject"
        }
    }
}

Registering the definition with the server

From v51 the definition is stored on sfp server. Push the file once, then prepare the pool by tag:

sfp server pool config push -f config/poolconfig.json \
  -r <owner>/<repo> \
  --dev-hub-username <devhub> \
  --branch main
sfp server pool scratch init -p DEV-POOL -r <owner>/<repo>

sfp server pool config push validates the file against the schema before it is stored, so a malformed definition is rejected at push time rather than mid-prepare. The --branch value determines which branch prepare checks out when it builds the pool, and --dev-hub-username determines which registered Dev Hub creates the scratch orgs.

sfp server pool scratch init also answers to the aliases sfp prepare, sfp pool prepare, sfp pool scratch prepare and sfp pool scratch init.

Pool state — membership, credentials and allocation — lives on sfp server, so --repository and --tag are required; the earlier local mode (--poolconfig/-f with --targetdevhubusername/-v) has been removed.

Configuration properties

PropertyTypeDescription
tagstringName used to identify the scratch org pool
waitTimeintMinutes the command should wait for a scratch org to be created, Default is 6 minutes
expiryintNumber of days for which the scratch orgs are active
maxAllocationintMaximum capacity of the pool
batchSizeintNumber of processes for creating scratch orgs in parallel
configFilePathstringPath to scratch org definition JSON file
succeedOnDeploymentErrorsbooleanWhether to persist scratch org to the pool for a deployment error, default:true
snapshotPoolstringName of an earlier prepared pool to build this pool on top of (multi-stage prepare). Prepare claims only as many AVAILABLE orgs from the snapshot pool as this pool is short of its maxAllocation
installAllbooleanInstall all package artifacts, in addition to the managed package dependencies
releaseConfigFilestringPath to a release config file to create pools with selected packages. Use in conjunction with installAll
enableSourceTrackingbooleanEnable source tracking by deploying packages using source:push and persisting source tracking files
disableSourcePackageOverridebooleanDisable overriding unlocked packages as source packages, Rather install unlocked packages as unlocked
relaxAllIPRangesbooleanRelax all IP addresses, allowing all global access to scratch orgs
ipRangesToBeRelaxedarrayRange of IP addresses that can access the scratch orgs
retryOnFailurebooleanRetry installation of a package on a failed deployment
maxRetryCountnumberMaximum number of times a package should be retried while deploying to a scratchorg, The default is 2
keysstringKeys for managed package dependencies, as packageA:pw123 packageB:pw123. Overridden by the --keys flag
noAnchestorsbooleanExclude second-generation managed package ancestors from the scratch org, default:false
enableVlocitybooleanApply Vlocity pack settings and run the Vlocity initial install on each org
preDependencyInstallationScriptPathstringPath to a script executed before package dependencies are installed in a scratch org. See Lifecycle scripts
postDeploymentScriptPathstringPath to a script executed after all packages (dependencies + repository) are installed. See Lifecycle scripts
fetchArtifactsboolean|objectWhere prepare sources artifacts from. See Artifact sources
fetchArtifacts.artifactFetchScriptstringPath to the shell script containing logic for fetching artifacts from a universal registry, if not using npm
fetchArtifacts.npmobjectFetch artifacts from an NPM registry
fetchArtifacts.npm.scopestringScope of the NPM package
fetchArtifacts.npm.npmrcPathstringPath to the .npmrc used to authenticate to the NPM registry. Overridden by the --npmrcpath flag

A package carrying "ignoreOnStage": ["prepare"] in sfdx-project.json is excluded from the pool, whether artifacts are fetched or built.

Checkpoint packages

A package marked "checkpointForPrepare": true in sfdx-project.json is treated as a checkpoint during prepare. It is set per package, not in the pool definition, but it changes how succeedOnDeploymentErrors behaves.

When succeedOnDeploymentErrors is true and a package fails to deploy, prepare keeps the org only if at least one checkpoint package deployed; otherwise the org is deleted rather than added to the pool. When succeedOnDeploymentErrors is false, any failure deletes the org, so checkpoint packages add nothing.

See Checkpoint packages for the full behaviour and an example.

Artifact sources

fetchArtifacts selects where the artifacts installed into each scratch org come from.

ValueBehaviour
trueFetch from the server's internal artifact registry. Requires --repository
{ "npm": { "scope": "myproject" } }Fetch from an NPM registry using the given scope
{ "artifactFetchScript": "./fetch.sh" }Run the given script to fetch each artifact
omitted or falseBuild every package from the head of the branch instead of fetching

Prepare resolves each package's version from its latest git tag and fetches that version. A package whose artifact is missing from the registry is skipped with a warning, which surfaces later as a deployment failure.

Building rather than fetching produces a pool from the current head of the branch, which is not the same set of packages that has been validated and released. Pools are intended to be prepared from previously validated artifacts.

Lifecycle scripts

The pool definition accepts two script hooks. Both run once per scratch org, inside the org's own preparation job.

PropertyRuns
preDependencyInstallationScriptPathAfter IP ranges are relaxed and the sfpowerscripts artifact package is installed, before any package dependency is installed
postDeploymentScriptPathAfter every artifact has been deployed to the org

The full per-org sequence is:

relax IP ranges
  → install sfpowerscripts artifact package
    → preDependencyInstallationScriptPath
      → install external package dependencies
        → Vlocity configuration (if enableVlocity)
          → deploy all artifacts
            → postDeploymentScriptPath

Parameters

Both scripts receive positional parameters, in the same style as the package-level pre/post deployment scripts.

preDependencyInstallationScriptPath

PositionValue
1Username of the scratch org
2Username of the Dev Hub

postDeploymentScriptPath

PositionValue
1Username of the scratch org
2Username of the Dev Hub
3Deployment status. succeed once the deployment stage has run; empty when installAll is false, as nothing was deployed

The deployment status does not distinguish a partial deployment. When succeedOnDeploymentErrors keeps an org whose packages did not all deploy, the script still receives succeed. A script that must react to a partial deployment should check the org itself rather than this parameter.

Execution

  • Paths are resolved relative to the project directory. In server mode that is the worktree prepare checks out for the configured branch, not the directory the command was launched from.
  • Scripts run with the project directory as their working directory, under a 30 minute timeout.
  • NO_COLOR=1 and FORCE_COLOR=0 are set for the script, so sf ... --json output stays parseable by tools such as jq.
  • Scripts are executed with sh -e. A non-zero exit fails the preparation job for that scratch org: the org is deleted from the Dev Hub, counted against failedToCreate, and reported to the server as an error. succeedOnDeploymentErrors does not cover this — it applies to package deployment failures, not to hook failures.

Example

{
    "tag": "DEV-POOL",
    "maxAllocation": 20,
    "installAll": true,
    "preDependencyInstallationScriptPath": "scripts/pool/enable-deliverability.sh",
    "postDeploymentScriptPath": "scripts/pool/seed-data.sh"
}
#!/bin/bash
# scripts/pool/seed-data.sh
set -e

SCRATCH_ORG_USERNAME=$1
DEVHUB_USERNAME=$2
DEPLOYMENT_STATUS=$3

if [ -z "$DEPLOYMENT_STATUS" ]; then
  echo "No packages were deployed, skipping data seed"
  exit 0
fi

sf data import tree --plan data/seed-plan.json --target-org "$SCRATCH_ORG_USERNAME"
sf org assign permset --name Seeded_Data_Access --target-org "$SCRATCH_ORG_USERNAME"

Scripts are read from the repository at the branch prepare checked out, so they may reference other files in the repository. This differs from package-level pre/post deployment scripts, which are copied into the artifact and run detached from version control.

Schema

The JSON schema for the pool definition file is pooldefinition.schema.json in resources/schemas. It is applied by sfp server pool config push and by the local-file path of sfp server pool scratch init.

On this page