Salesforce · · 15 min read

Scratch Orgs in Salesforce

Everything you need to know about Scratch Orgs — what they are, creating them from org shapes, working with unlocked packages, and integrating scratch orgs into your development workflow.

Part 68: Scratch Orgs in Salesforce

Welcome back to the Salesforce blog series. In Part 67, we explored the Salesforce CLI — the command-line backbone of modern Salesforce development. Now it is time to put that CLI knowledge to work with one of the most powerful concepts in the Salesforce development ecosystem: Scratch Orgs.

If you have ever struggled with shared sandboxes, configuration drift, or the pain of setting up a clean development environment, scratch orgs are about to change the way you work. They represent a fundamental shift in how Salesforce developers build, test, and deliver features. Let us dive in.


What Are Scratch Orgs?

A scratch org is a temporary, fully configurable Salesforce environment that you spin up on demand. Unlike sandboxes, which are copies of your production org and can take hours or even days to provision, scratch orgs are lightweight, disposable environments that you can create in under a minute.

Here are the key characteristics that set scratch orgs apart:

  • Temporary by design. Scratch orgs have a maximum lifespan of 30 days (the default is 7 days). They are meant to be created, used for a specific task, and then deleted.
  • Source-driven. Instead of making changes directly in the org and pulling them down, you push your local source code into a scratch org. This is the foundation of the source-driven development model.
  • Fully configurable. You define the shape of your scratch org — which features are enabled, what edition it mimics, what settings are turned on — through a simple JSON configuration file.
  • Isolated. Each developer gets their own scratch org. No more stepping on each other’s toes in a shared sandbox.
  • Automatable. Because scratch orgs are created and configured via CLI commands and JSON files, the entire process can be scripted and integrated into CI/CD pipelines.

Scratch Orgs vs. Sandboxes

To understand why scratch orgs matter, it helps to compare them with traditional sandboxes.

FeatureSandboxScratch Org
Provisioning timeMinutes to hoursUnder a minute
LifespanIndefinite (until refreshed)1-30 days
Source of truthThe org itselfLocal source code
ConfigurationCopied from productionDefined in a JSON file
CostCounted against org limitsCounted against Dev Hub limits
IsolationOften sharedOne per developer/task

Sandboxes still have their place — especially for integration testing, UAT, and staging. But for day-to-day development work, scratch orgs are the way to go.

The Dev Hub

Before you can create scratch orgs, you need a Dev Hub. The Dev Hub is the parent org that manages and tracks your scratch orgs. It is typically your production org or a dedicated Developer Edition org. You enable the Dev Hub feature in Setup under Development > Dev Hub, and then authorize it through the CLI:

sf org login web --set-default-dev-hub --alias my-devhub

Once your Dev Hub is authorized, you can start creating scratch orgs against it.


The Scratch Org Definition File

Every scratch org starts with a definition file. This is a JSON file — typically named config/project-scratch-def.json — that tells Salesforce exactly what kind of org to create. Think of it as a blueprint.

Here is a basic example:

{
  "orgName": "My Scratch Org",
  "edition": "Developer",
  "features": ["EnableSetPasswordInApi"],
  "settings": {
    "lightningExperienceSettings": {
      "enableS1DesktopEnabled": true
    },
    "securitySettings": {
      "passwordPolicies": {
        "enableSetPasswordInApi": true
      }
    }
  }
}

Let us break down the key properties:

  • orgName: A human-readable name for the scratch org.
  • edition: The Salesforce edition to emulate. Options include Developer, Enterprise, Group, and Professional.
  • features: A list of optional features to enable, such as Communities, ServiceCloud, MarketingUser, MultiCurrency, and many more.
  • settings: A nested object that maps directly to Salesforce Metadata API settings. This is where you enable specific behaviors like Lightning Experience, Person Accounts, or custom security policies.

Common Features You Might Enable

Depending on your project, you may need to include various features in your scratch org definition:

{
  "orgName": "Full Feature Scratch Org",
  "edition": "Enterprise",
  "features": [
    "Communities",
    "ServiceCloud",
    "MultiCurrency",
    "PersonAccounts",
    "StateAndCountryPicklist",
    "AuthorApex",
    "API"
  ],
  "settings": {
    "communitiesSettings": {
      "enableNetworksEnabled": true
    },
    "omniChannelSettings": {
      "enableOmniChannel": true
    },
    "caseSettings": {
      "systemUserEmail": "admin@example.com"
    }
  }
}

The full list of available features and settings is documented in the Salesforce DX Developer Guide. When in doubt, start minimal and add features as your project demands them.


How to Create a Scratch Org from an Org Shape

One of the most practical ways to create a scratch org is from an org shape. An org shape captures the configuration of an existing org — the features enabled, the settings configured, the licenses available — and uses that as the template for new scratch orgs.

This is incredibly useful when you want your scratch orgs to closely mirror your production environment without manually specifying every feature and setting in the definition file.

Step 1: Enable Org Shape

First, you need to enable org shape in your source org (usually production or a sandbox). Navigate to Setup > Org Shape and enable the feature. Alternatively, you can do this through the CLI:

sf org create shape --target-org my-production-org

This command captures the shape of the target org and stores it in the Dev Hub.

Step 2: List Available Org Shapes

You can verify that the org shape was created successfully:

sf org list shape

This returns a list of all org shapes associated with your Dev Hub, including their status and the source org ID.

Step 3: Reference the Org Shape in Your Definition File

Once the org shape is available, you reference it in your scratch org definition file using the sourceOrg property:

{
  "orgName": "Shape-Based Scratch Org",
  "sourceOrg": "00D1t000000XXXXX",
  "settings": {
    "lightningExperienceSettings": {
      "enableS1DesktopEnabled": true
    }
  }
}

The sourceOrg value is the 15 or 18 character org ID of the org whose shape you want to replicate. When you use sourceOrg, you do not need to specify edition or features separately — those are inherited from the shape.

Step 4: Create the Scratch Org

Now create the scratch org using the standard command:

sf org create scratch \
  --definition-file config/project-scratch-def.json \
  --set-default \
  --alias shape-scratch \
  --duration-days 14

The resulting scratch org will have the same features, settings, and edition as the source org. You can still override specific settings in the definition file if needed.

When to Use Org Shapes

Org shapes are particularly valuable for:

  • Large orgs with complex configurations. Instead of tracking dozens of features and settings manually, the org shape captures everything automatically.
  • Consistency across teams. Every developer creates scratch orgs from the same shape, reducing configuration drift.
  • Onboarding new team members. New developers can spin up a correctly configured scratch org without understanding every nuance of the org’s setup.

Keep in mind that org shapes are periodically refreshed, so changes to the source org will eventually be reflected in new scratch orgs. However, this is not instantaneous — if you make changes to the source org, you may need to recreate the shape.


How to Create a Scratch Org from a GitHub Repo

A common workflow in modern Salesforce development is to store your entire project — source code, configuration, and scratch org definition — in a Git repository. Creating a scratch org from a GitHub repo is straightforward once you understand the project structure.

Prerequisites

Before you begin, make sure you have:

  • The Salesforce CLI installed (covered in Part 67)
  • Git installed and configured
  • A Dev Hub authorized
  • A Salesforce DX project in a GitHub repository

Step 1: Clone the Repository

Start by cloning the repository to your local machine:

git clone https://github.com/your-org/your-salesforce-project.git
cd your-salesforce-project

Step 2: Verify the Project Structure

A well-structured Salesforce DX project will have the following layout:

your-salesforce-project/
  sfdx-project.json
  config/
    project-scratch-def.json
  force-app/
    main/
      default/
        classes/
        triggers/
        lwc/
        objects/
        ...
  scripts/
    setup.sh

The sfdx-project.json file is the project manifest. It defines the package directories and other project-level settings:

{
  "packageDirectories": [
    {
      "path": "force-app",
      "default": true
    }
  ],
  "namespace": "",
  "sfdcLoginUrl": "https://login.salesforce.com",
  "sourceApiVersion": "60.0"
}

Step 3: Create the Scratch Org

With the repository cloned and the project structure in place, create your scratch org:

sf org create scratch \
  --definition-file config/project-scratch-def.json \
  --set-default \
  --alias my-feature-org \
  --duration-days 7

Step 4: Push Source to the Scratch Org

Once the scratch org is created, push your local source code into it:

sf project deploy start

This deploys all the metadata from your force-app directory to the scratch org.

Step 5: Run Setup Scripts

Many projects include post-creation setup scripts that load sample data, assign permission sets, or configure the org for development:

# Assign a permission set
sf org assign permset --name MyCustomPermSet

# Import sample data
sf data import tree --files data/sample-accounts.json data/sample-contacts.json

# Run anonymous Apex for additional setup
sf apex run --file scripts/setup-apex.apex

Automating the Entire Flow

A common practice is to wrap the entire scratch org creation and setup process in a single shell script:

#!/bin/bash

echo "Creating scratch org..."
sf org create scratch \
  --definition-file config/project-scratch-def.json \
  --set-default \
  --alias dev-scratch \
  --duration-days 7

echo "Pushing source..."
sf project deploy start

echo "Assigning permission sets..."
sf org assign permset --name MyCustomPermSet

echo "Loading sample data..."
sf data import tree --files data/sample-data-plan.json

echo "Opening scratch org..."
sf org open

echo "Done. Your scratch org is ready."

Save this as scripts/create-scratch-org.sh, make it executable with chmod +x, and any developer on the team can set up a fully configured development environment with a single command. This is the power of source-driven development.


How to Create a Scratch Org with Unlocked Packages

Unlocked packages are one of the most important concepts in the Salesforce packaging ecosystem. They let you organize your metadata into modular, versioned units that can be installed independently. Working with unlocked packages in scratch orgs requires a few extra steps, but the payoff is significant.

Understanding Unlocked Packages

An unlocked package is a group of related metadata (Apex classes, custom objects, Lightning components, etc.) that is versioned and deployed as a single unit. Unlike unmanaged packages, unlocked packages:

  • Have version tracking
  • Support upgrades
  • Can define dependencies on other packages
  • Are the recommended packaging model for enterprise Salesforce development

Step 1: Define Your Packages in sfdx-project.json

Your sfdx-project.json file defines the package structure:

{
  "packageDirectories": [
    {
      "path": "force-app/core",
      "default": true,
      "package": "CorePackage",
      "versionName": "Spring Release",
      "versionNumber": "1.2.0.NEXT"
    },
    {
      "path": "force-app/features",
      "package": "FeaturesPackage",
      "versionName": "Spring Release",
      "versionNumber": "1.1.0.NEXT",
      "dependencies": [
        {
          "package": "CorePackage",
          "versionNumber": "1.2.0.LATEST"
        }
      ]
    }
  ],
  "namespace": "",
  "sfdcLoginUrl": "https://login.salesforce.com",
  "sourceApiVersion": "60.0",
  "packageAliases": {
    "CorePackage": "0Ho...",
    "CorePackage@1.2.0-1": "04t...",
    "FeaturesPackage": "0Ho...",
    "FeaturesPackage@1.1.0-1": "04t..."
  }
}

Step 2: Create the Scratch Org

Create the scratch org as you normally would:

sf org create scratch \
  --definition-file config/project-scratch-def.json \
  --set-default \
  --alias package-dev \
  --duration-days 14

Step 3: Install Package Dependencies

If your package depends on other packages (including other unlocked packages or managed packages from AppExchange), you need to install those dependencies before pushing your source:

# Install a dependency package by version ID
sf package install \
  --package 04t... \
  --target-org package-dev \
  --wait 10 \
  --publish-wait 10

# Or install by alias if defined in sfdx-project.json
sf package install \
  --package "CorePackage@1.2.0-1" \
  --target-org package-dev \
  --wait 10

Step 4: Push Source and Develop

With dependencies installed, push your source and begin developing:

sf project deploy start

Step 5: Create a New Package Version

When you are ready to package your changes, create a new package version:

sf package version create \
  --package CorePackage \
  --installation-key my-secret-key \
  --wait 15 \
  --code-coverage

The --code-coverage flag ensures that your package meets the 75% code coverage requirement. The command will build the package version and run all associated tests.

You can check the status of the version creation:

sf package version create list

And once the version is ready, promote it for production installation:

sf package version promote --package "CorePackage@1.3.0-1"

Automating Dependency Installation

For projects with multiple package dependencies, automating the installation order is essential. Here is a script that handles this:

#!/bin/bash

echo "Creating scratch org for package development..."
sf org create scratch \
  --definition-file config/project-scratch-def.json \
  --set-default \
  --alias pkg-scratch \
  --duration-days 14

echo "Installing dependencies..."
# Install in order of dependency chain
sf package install --package "CorePackage@1.2.0-1" --wait 10
sf package install --package "SharedUtils@2.0.0-3" --wait 10

echo "Pushing source..."
sf project deploy start

echo "Running tests..."
sf apex run test --test-level RunLocalTests --wait 15

echo "Package development scratch org is ready."

Best Practices for Packages in Scratch Orgs

  • Always install dependencies before pushing source. If you push source that references metadata from a dependency that is not yet installed, the deployment will fail.
  • Use package aliases. Instead of hard-coding version IDs throughout your scripts, define aliases in sfdx-project.json and reference those.
  • Test package installation in scratch orgs. Before promoting a package version, install it in a fresh scratch org to verify it works correctly in isolation.
  • Version your scratch org definition alongside your package. When your package requires specific org features, those features should be in the scratch org definition file so that any developer creating a scratch org gets the right configuration automatically.

Scratch Org Management and Best Practices

Now that you know how to create scratch orgs in various ways, let us cover some practical tips for managing them effectively.

Listing and Monitoring Scratch Orgs

Keep track of your active scratch orgs:

# List all orgs including scratch orgs
sf org list

# See detailed information about a specific scratch org
sf org display --target-org my-scratch

Deleting Scratch Orgs

When you are done with a scratch org, clean it up:

sf org delete scratch --target-org my-scratch --no-prompt

This is good hygiene. Scratch orgs count against your Dev Hub’s allocation, and leaving unused scratch orgs running wastes those limits.

Pulling Changes Back

If you make changes directly in the scratch org (through the Setup UI, for example), you can pull those changes back to your local project:

sf project retrieve start

This is the counterpart to sf project deploy start. It retrieves any metadata changes from the scratch org and updates your local source files. From there, you commit those changes to version control.

Handling Scratch Org Limits

Each Dev Hub edition has a limit on how many active scratch orgs it can support. Developer Edition orgs typically allow 3-5 active scratch orgs, while enterprise production orgs can support many more. Monitor your usage:

sf org list --all

If you hit your limit, delete scratch orgs you no longer need or request a limit increase from Salesforce.

Scratch Orgs in CI/CD Pipelines

One of the greatest strengths of scratch orgs is their integration with continuous integration and continuous delivery pipelines. A typical CI pipeline might look like this:

  1. A developer pushes a feature branch to GitHub.
  2. The CI system (GitHub Actions, Jenkins, CircleCI, etc.) detects the push.
  3. The pipeline creates a new scratch org from the definition file.
  4. Source code is pushed to the scratch org.
  5. All Apex tests are run.
  6. If tests pass, the pipeline reports success. If they fail, the developer is notified.
  7. The scratch org is deleted after the pipeline completes.

This pattern ensures that every change is validated in a clean environment before it is merged, catching issues early and reducing the risk of deploying broken code.


Section Notes

  • Scratch orgs are temporary, configurable Salesforce environments designed for development and testing, with a maximum lifespan of 30 days.
  • A Dev Hub is required to create scratch orgs and must be authorized through the Salesforce CLI.
  • The scratch org definition file (project-scratch-def.json) is a JSON blueprint that defines the edition, features, and settings of the scratch org.
  • Org shapes let you capture the configuration of an existing org and use it as a template, reducing the manual effort of specifying features and settings.
  • Creating scratch orgs from a GitHub repository follows a clone, create, push, and configure workflow that can be fully automated with shell scripts.
  • Unlocked packages bring version control and modularity to Salesforce metadata. When working with packages in scratch orgs, always install dependencies before pushing source.
  • Scratch org management includes listing active orgs, cleaning up unused ones, and monitoring Dev Hub allocation limits.
  • Scratch orgs are a natural fit for CI/CD pipelines, enabling automated testing in clean environments for every code change.
  • Always store your scratch org definition files and automation scripts in version control alongside your source code so the entire team benefits from a consistent setup process.

In the next part of the series, we will continue building on these DevOps concepts and explore more advanced workflows for managing your Salesforce development lifecycle. Stay tuned.