Part 67: The Salesforce CLI
Welcome back to the Salesforce series. In Part 57, we covered version control for Salesforce developers, and in Part 60, we surveyed all the major Salesforce APIs including the Metadata API and Tooling API. Now it is time to talk about the tool that ties all of that together in your daily workflow — the Salesforce CLI.
The Salesforce CLI (Command Line Interface) is the single most important tool in a Salesforce developer’s local environment. It is how you authenticate to orgs, push and pull source code, deploy metadata to production, run Apex tests, import and export data, create scratch orgs and sandboxes, and automate just about every administrative task you can think of. If you have been clicking through Setup menus and using Change Sets to move work between environments, the CLI is going to change your life.
This is Part 67 of the series. We will cover what the Salesforce CLI is, how to deploy metadata, how to import data, how to create scratch orgs and sandboxes, and the most useful commands and plugins you should know about.
What is the Salesforce CLI?
The Salesforce CLI is a free, open-source command-line tool built and maintained by Salesforce. It is installed locally on your machine — Windows, macOS, or Linux — and it provides a unified interface for interacting with Salesforce orgs from your terminal.
A Brief History
The CLI has gone through several iterations. The original tool was called the Force.com CLI, then Salesforce introduced sfdx (Salesforce DX CLI) as part of the Salesforce DX development model. In 2023, Salesforce unified everything under a single binary called sf. The sfdx commands still work as aliases, but sf is the current and recommended entry point. Throughout this post, all commands will use the sf syntax.
Installation
You can install the Salesforce CLI in several ways:
# Using npm (requires Node.js)
npm install @salesforce/cli --global
# Using Homebrew on macOS
brew install sf
# Or download the installer from
# https://developer.salesforce.com/tools/salesforcecli
After installation, verify it works:
sf --version
# @salesforce/cli/2.x.x ...
sf plugins --core
# Lists all installed core plugins
The CLI is built on top of the oclif framework and uses a plugin architecture. The core installation ships with plugins for source tracking, org management, data operations, and more. You can install additional community and official plugins to extend its capabilities.
Authentication
Before you can do anything useful, you need to authenticate to a Salesforce org. The CLI supports several authentication flows:
# Web-based login (opens a browser)
sf org login web --alias my-dev-org
# Web-based login to a sandbox
sf org login web --alias my-sandbox --instance-url https://test.salesforce.com
# Web-based login to a custom domain
sf org login web --alias my-org --instance-url https://mycompany.my.salesforce.com
# JWT-based login (for CI/CD — no browser needed)
sf org login jwt --client-id YOUR_CONNECTED_APP_ID \
--jwt-key-file server.key \
--username admin@example.com \
--alias my-ci-org
The web login flow opens your browser, you log in normally, and the CLI stores an encrypted refresh token locally. The JWT flow is what you use in CI/CD pipelines like GitHub Actions or Jenkins, where there is no browser available. You set up a Connected App in Salesforce with a certificate, and the CLI authenticates using a signed JWT assertion.
To see all authenticated orgs:
sf org list
To set a default org so you do not have to specify --target-org on every command:
sf config set target-org my-dev-org
How to Deploy Using the Salesforce CLI
Deploying metadata is the most common reason developers reach for the CLI. There are two distinct deployment models, and it is important to understand when to use each.
Source Push and Pull (Scratch Orgs and Sandboxes with Tracking)
If you are working with a scratch org or a sandbox that has source tracking enabled, you use sf project deploy start and sf project retrieve start (or their older aliases sf push and sf pull). Source tracking means Salesforce keeps a record of what has changed on both the local side and the org side, so it only syncs the differences.
# Push local changes to the scratch org
sf project deploy start
# Pull changes made in the org back to your local project
sf project retrieve start
# Check what would be pushed without actually pushing
sf project deploy preview
# Check what would be pulled without actually pulling
sf project retrieve preview
This workflow is fast and frictionless. You edit files locally, push them, test in the org, and if you made changes through the Setup UI in the org, you pull those back down. Source tracking handles the conflict detection.
Metadata Deploy (Production and Untracked Sandboxes)
For deploying to production or to sandboxes without source tracking, you use a metadata deploy. This is the equivalent of what Change Sets do, but from the command line with far more control.
# Deploy everything in your project
sf project deploy start --target-org production
# Deploy a specific directory
sf project deploy start --source-dir force-app/main/default/classes --target-org production
# Deploy specific files
sf project deploy start --source-dir force-app/main/default/classes/AccountService.cls \
--target-org production
# Deploy and run all local tests
sf project deploy start --source-dir force-app --target-org production --test-level RunLocalTests
# Deploy and run specific tests
sf project deploy start --source-dir force-app --target-org production \
--test-level RunSpecifiedTests --tests AccountServiceTest ContactServiceTest
# Validate only (check deploy without committing — like a dry run)
sf project deploy validate --source-dir force-app --target-org production \
--test-level RunLocalTests
# Quick deploy after a successful validation
sf project deploy quick --job-id 0Af... --target-org production
The --test-level flag is critical for production deployments. Salesforce requires that you run tests when deploying to production, and you have four options:
NoTestRun— No tests (only works for non-production orgs).RunSpecifiedTests— Run only the tests you name.RunLocalTests— Run all tests in your org except those in managed packages.RunAllTestsInOrg— Run every test, including managed package tests.
For most production deployments, RunLocalTests is the standard choice. If you want faster deploys during development, RunSpecifiedTests lets you target only the tests relevant to your changes.
Deploy with a Manifest (package.xml)
Sometimes you want fine-grained control over exactly which metadata types and components to deploy. That is where package.xml comes in:
<?xml version="1.0" encoding="UTF-8"?>
<Package xmlns="http://soap.sforce.com/2006/04/metadata">
<types>
<members>AccountService</members>
<members>ContactService</members>
<name>ApexClass</name>
</types>
<types>
<members>AccountTrigger</members>
<name>ApexTrigger</name>
</types>
<types>
<members>Account.Industry_Category__c</members>
<name>CustomField</name>
</types>
<version>61.0</version>
</Package>
# Deploy using a manifest
sf project deploy start --manifest manifest/package.xml --target-org production
# Retrieve using a manifest
sf project retrieve start --manifest manifest/package.xml --target-org production
If you covered the deployment post (Part 30 on deploying changes to production), you will recall that Change Sets require you to manually select components in the UI. With package.xml and the CLI, you define what to deploy in a version-controlled file that lives in your repository. This is repeatable, reviewable, and scriptable.
Checking Deploy Status
Deploys can take time, especially with large metadata sets or many tests. You can check the status of a running deploy:
# Check deploy status
sf project deploy report --job-id 0Af...
# Cancel a running deploy
sf project deploy cancel --job-id 0Af...
# Resume a deploy that was interrupted
sf project deploy resume --job-id 0Af...
How to Import Data Using the Salesforce CLI
The CLI provides several ways to move data in and out of Salesforce orgs. This is essential for seeding scratch orgs with test data, migrating records between environments, or performing bulk data operations.
Tree Import and Export
The simplest approach for small to medium data sets is the tree commands. These work with JSON files that preserve record relationships.
# Export Account records and their related Contacts
sf data export tree --query "SELECT Id, Name, Industry, \
(SELECT Id, FirstName, LastName, Email FROM Contacts) \
FROM Account WHERE Industry = 'Technology' LIMIT 50" \
--target-org my-dev-org \
--output-dir data/
# This creates JSON files like:
# data/Account.json
# data/Contact.json
# data/Account-Contact-plan.json
The plan file is key. It tells the CLI the order in which to insert records and how to resolve the relationships between them (since the IDs will be different in the target org).
# Import using the plan file
sf data import tree --plan data/Account-Contact-plan.json --target-org my-scratch-org
The tree import handles parent-child relationships automatically. It inserts the parent records first, captures the new IDs, and uses those IDs when inserting the child records. This is invaluable for setting up scratch orgs with realistic data.
Bulk Data Operations
For larger data sets — tens of thousands or millions of records — you use the bulk commands, which leverage the Salesforce Bulk API 2.0 behind the scenes:
# Bulk upsert from a CSV file
sf data upsert bulk --sobject Account --file accounts.csv \
--external-id External_Id__c --target-org my-org
# Bulk delete using a CSV of record IDs
sf data delete bulk --sobject Account --file account-ids.csv --target-org my-org
# Check the status of a bulk job
sf data bulk results --job-id 750... --target-org my-org
The CSV files follow standard Salesforce data loader conventions. The header row contains API field names, and each subsequent row is a record.
SOQL Queries from the CLI
You can run SOQL queries directly from the terminal without opening Developer Console or Workbench:
# Simple query
sf data query --query "SELECT Id, Name, Industry FROM Account LIMIT 10" \
--target-org my-org
# Output as CSV
sf data query --query "SELECT Id, Name, Email FROM Contact" \
--target-org my-org --result-format csv > contacts.csv
# Output as JSON
sf data query --query "SELECT Id, Name FROM Account" \
--target-org my-org --result-format json
# Use the Bulk API for large queries (more than 10,000 records)
sf data query --query "SELECT Id, Name FROM Account" \
--target-org my-org --bulk
This is incredibly useful for quick data checks, exporting data for analysis, or scripting data pipelines.
Single Record Operations
For individual record operations:
# Create a single record
sf data create record --sobject Account \
--values "Name='Acme Corp' Industry='Technology'" \
--target-org my-org
# Update a record
sf data update record --sobject Account --record-id 001... \
--values "Industry='Manufacturing'" \
--target-org my-org
# Delete a record
sf data delete record --sobject Account --record-id 001... \
--target-org my-org
# Get a record by ID
sf data get record --sobject Account --record-id 001... \
--target-org my-org
How to Create Scratch Orgs and Sandboxes Using the CLI
One of the most powerful capabilities of the Salesforce CLI is org management. You can create, configure, and delete scratch orgs and sandboxes entirely from the command line.
Scratch Orgs
Scratch orgs are temporary, disposable Salesforce environments that are defined by a configuration file. They are the backbone of the Salesforce DX development model. Instead of sharing a single developer sandbox with your whole team, each developer spins up their own scratch org, does their work, and deletes it when done.
First, you need a scratch org definition file. This typically lives at config/project-scratch-def.json in your project:
{
"orgName": "My Dev Scratch Org",
"edition": "Developer",
"features": ["EnableSetPasswordInApi", "Communities"],
"settings": {
"lightningExperienceSettings": {
"enableS1DesktopEnabled": true
},
"securitySettings": {
"passwordPolicies": {
"enableSetPasswordInApi": true
}
}
}
}
Then create the scratch org:
# Create a scratch org with a 30-day expiration
sf org create scratch --definition-file config/project-scratch-def.json \
--alias my-scratch --duration-days 30 --set-default
# Push your source to the new scratch org
sf project deploy start
# Assign a permission set
sf org assign permset --name My_Permission_Set
# Open the scratch org in a browser
sf org open
# When you are done, delete the scratch org
sf org delete scratch --target-org my-scratch --no-prompt
Scratch orgs require a Dev Hub — a production or business org that has the Dev Hub feature enabled. You authenticate to the Dev Hub first, then create scratch orgs from it:
# Authenticate to your Dev Hub
sf org login web --alias my-devhub --set-default-dev-hub
# Now scratch org creation uses this Dev Hub automatically
sf org create scratch --definition-file config/project-scratch-def.json \
--alias my-scratch --duration-days 7
Sandboxes
You can also create and manage sandboxes from the CLI. This is especially useful for automation — imagine a CI/CD pipeline that creates a fresh sandbox, deploys your code, runs tests, and reports results.
# Create a Developer sandbox
sf org create sandbox --name QATest \
--definition-file config/sandbox-def.json \
--alias qa-sandbox \
--target-org production
# Or create with inline options
sf org create sandbox --name DevSandbox \
--license-type Developer \
--alias dev-sandbox \
--target-org production
# Check sandbox creation status (it can take minutes to hours)
sf org resume sandbox --name QATest --target-org production
# Log in to the sandbox once it is ready
sf org login web --alias qa-sandbox --instance-url https://test.salesforce.com
# Delete a sandbox
sf org delete sandbox --target-org qa-sandbox --no-prompt
A sandbox definition file looks like this:
{
"sandboxName": "QATest",
"licenseType": "Developer",
"autoActivate": true
}
The licenseType can be Developer, Developer_Pro, Partial, or Full, depending on what your org’s contract includes.
Listing and Managing Orgs
# List all authenticated orgs
sf org list
# List only scratch orgs
sf org list --all
# Get detailed info about an org
sf org display --target-org my-scratch
# Open an org in the browser
sf org open --target-org my-scratch
# Open a specific page
sf org open --target-org my-scratch --path /lightning/setup/ApexClasses/home
Other Useful Salesforce CLI Commands
Beyond deploying and data operations, the CLI has a deep set of commands that cover nearly every aspect of Salesforce development.
Running Apex Tests
# Run all tests
sf apex run test --target-org my-org --test-level RunLocalTests --wait 10
# Run specific test classes
sf apex run test --target-org my-org --class-names AccountServiceTest ContactServiceTest
# Run a specific test method
sf apex run test --target-org my-org --tests AccountServiceTest.testInsertAccount
# Get test results
sf apex get test --test-run-id 707... --target-org my-org
# Get code coverage results
sf apex get test --test-run-id 707... --target-org my-org --code-coverage
If you have been following along since Part 43 on Apex tests, you know how important test coverage is. Running tests from the CLI is far more efficient than using the Developer Console, and it integrates naturally into CI/CD pipelines.
Executing Anonymous Apex
# Run a snippet of Apex directly
sf apex run --target-org my-org --file scripts/apex/seedData.apex
# Run inline Apex
echo "System.debug('Hello from the CLI');" | sf apex run --target-org my-org
This is great for running one-off scripts — data fixes, cache clearing, or testing small pieces of logic without creating a full class.
Viewing Debug Logs
# Tail logs in real time
sf apex tail log --target-org my-org
# List recent logs
sf apex list log --target-org my-org
# Get a specific log
sf apex get log --log-id 07L... --target-org my-org
The sf apex tail log command is a favorite. It streams debug logs to your terminal in real time as you interact with the org. No more refreshing the Debug Logs page in Setup.
Generating Source Code
The CLI can scaffold new components for you:
# Create an Apex class
sf apex generate class --name AccountService --output-dir force-app/main/default/classes
# Create an Apex trigger
sf apex generate trigger --name AccountTrigger --sobject Account \
--output-dir force-app/main/default/triggers
# Create a Lightning Web Component
sf lightning generate component --name accountCard --type lwc \
--output-dir force-app/main/default/lwc
# Create a new SFDX project from scratch
sf project generate --name my-salesforce-project --template standard
Package Development
If you are building managed or unlocked packages:
# Create a package
sf package create --name "My Package" --package-type Unlocked \
--path force-app --target-dev-hub my-devhub
# Create a package version
sf package version create --package "My Package" \
--installation-key mykey123 --wait 30 --target-dev-hub my-devhub
# Install a package in an org
sf package install --package 04t... --target-org my-org --wait 10
# List package versions
sf package version list --target-dev-hub my-devhub
Limits and Org Info
# Check your org's governor limits consumption
sf limits api display --target-org my-org
# Display org details (access token, instance URL, etc.)
sf org display --target-org my-org --verbose
# List all metadata types available in the org
sf org list metadata-types --target-org my-org
Most Useful Salesforce CLI Plugins
The plugin architecture is one of the CLI’s greatest strengths. You can install plugins to add new commands and capabilities. Here are the most useful ones.
sf-hardis
sf-hardis is a community plugin that adds a massive collection of utilities for CI/CD, org monitoring, data management, and more.
sf plugins install sfdx-hardis
# Monitor org health
sf hardis:org:monitor:all
# Compare metadata between two orgs
sf hardis:org:compare
SFDX-Git-Delta (sgd)
This plugin is essential for CI/CD pipelines. Instead of deploying your entire project on every commit, sgd generates a package.xml that contains only the metadata that changed between two git commits.
npm install --global sfdx-git-delta
# Generate a delta package between two commits
sgd --from HEAD~1 --to HEAD --output delta/
# Then deploy only the changed components
sf project deploy start --manifest delta/package/package.xml --target-org production
This dramatically reduces deployment times. Instead of deploying hundreds of components, you deploy only the five files that changed in your pull request. In Part 57 on version control, we discussed git-based workflows — this plugin is what makes those workflows practical for large orgs.
Salesforce CLI Scanner (Code Analyzer)
This plugin runs static analysis on your Apex code, finding potential bugs, security vulnerabilities, and code quality issues.
sf plugins install @salesforce/sfdx-scanner
# Scan your Apex classes
sf scanner run --target force-app/main/default/classes --format table
# Scan with specific rule categories
sf scanner run --target force-app --category "Security,Best Practices"
# Output results as HTML for reporting
sf scanner run --target force-app --format html --outfile scan-results.html
The scanner uses PMD rules under the hood and can catch issues like SOQL injection, unused variables, overly complex methods, and hardcoded IDs.
Texei SFDX Plugin
A community favorite that adds a grab bag of useful commands:
sf plugins install texei-sfdx-plugin
# Assign permission sets to multiple users
sf texei permset assign --name My_Permission_Set --target-org my-org
# Manage connected apps, sharing rules, and more
Data Move Utility (SFDMU)
For complex data migrations that involve multiple related objects, SFDMU is far more powerful than the built-in tree commands:
sf plugins install sfdmu
# Uses an export.json configuration file to define
# the full migration — objects, relationships, field mappings,
# source org, and target org
sf sfdmu run --sourceusername source-org --targetusername target-org
SFDMU handles complex record relationships, field mappings, external ID matching, and can move data between any two orgs or between a CSV source and an org.
Installing and Managing Plugins
# Install a plugin
sf plugins install <plugin-name>
# List installed plugins
sf plugins
# Update all plugins
sf plugins update
# Uninstall a plugin
sf plugins uninstall <plugin-name>
# Link a local plugin (for development)
sf plugins link /path/to/my-plugin
Section Notes
- The Salesforce CLI (
sf) is the standard tool for interacting with Salesforce orgs from the terminal. It replaces the oldersfdxbinary and unifies all commands under one tool. - Authentication can be done via browser-based web login for local development or JWT for CI/CD pipelines. Use
sf org login webandsf org login jwtrespectively. - For deploying metadata, use
sf project deploy startfor tracked scratch orgs and sandboxes, and the same command with--target-organd--test-levelflags for production deployments. The--test-levelflag controls which Apex tests run during deployment. - Validate-then-quick-deploy is a powerful pattern: validate a deployment during off-hours, then run a quick deploy during a release window to minimize downtime.
- For data operations,
sf data export treeandsf data import treehandle small to medium data sets with relationships. For large volumes, usesf data upsert bulkandsf data delete bulk. - Scratch orgs are temporary, disposable environments defined by a JSON config file. They require a Dev Hub and can be created, configured, and deleted entirely from the CLI.
- Sandboxes can also be created and deleted from the CLI using
sf org create sandboxandsf org delete sandbox. - The plugin ecosystem is one of the CLI’s greatest strengths. SFDX-Git-Delta for delta deployments, Code Analyzer for static analysis, and SFDMU for complex data migrations are must-haves for any serious Salesforce development team.
- Running Apex tests, executing anonymous Apex, and tailing debug logs from the CLI are all faster and more scriptable than their UI equivalents.
PROJECT: Use the Salesforce CLI to Deploy Metadata and Import Data
Put what you have learned into practice:
- Install the CLI. If you have not already, install the Salesforce CLI and verify the installation with
sf --version. - Authenticate to an org. Use
sf org login webto authenticate to a Developer Edition org or sandbox. - Create a project. Run
sf project generate --name cli-practice --template standardto scaffold a new project. - Create an Apex class. Use
sf apex generate class --name PracticeService --output-dir force-app/main/default/classesto generate a new class. Add a simple static method that returns a string. - Deploy to your org. Run
sf project deploy start --source-dir force-app/main/default/classes/PracticeService.cls --target-org your-orgto deploy the class. - Run it with anonymous Apex. Create a file
scripts/apex/runPractice.apexthat calls your method and prints the result withSystem.debug(). Execute it withsf apex run --file scripts/apex/runPractice.apex --target-org your-org. - Export sample data. Run
sf data export tree --query "SELECT Id, Name, Industry FROM Account LIMIT 10" --target-org your-org --output-dir data/to export Account records. - Import data to another org. If you have a scratch org or second sandbox, import the data with
sf data import tree --plan data/Account-plan.json --target-org other-org. - Install a plugin. Install the Code Analyzer with
sf plugins install @salesforce/sfdx-scannerand run a scan against your class withsf scanner run --target force-app/main/default/classes.
By completing this project, you will have hands-on experience with the core CLI workflows that professional Salesforce developers use every day.