
Stop deploying manually. Start shipping with confidence. The complete developer roadmap for connecting Salesforce to GitHub — from zero to automated CI/CD.
Modern software development relies heavily on collaboration, automation, and continuous delivery. While Salesforce provides a powerful platform for building business applications, developers often need robust version control and team collaboration capabilities that go beyond Salesforce’s native tools.
This is where GitHub and Salesforce integration becomes essential.
By integrating GitHub with Salesforce, development teams can manage source code more effectively, automate deployments, improve collaboration, maintain version history, and implement modern DevOps practices.
Whether you’re a Salesforce administrator, developer, architect, or CTO, understanding how to connect Salesforce with GitHub can significantly improve development efficiency and deployment reliability.
In this guide, we’ll explore why GitHub-Salesforce integration matters, the benefits it offers, step-by-step integration methods, best practices, common challenges, and how to build a scalable Salesforce DevOps workflow.
What Is GitHub and Salesforce Integration?
GitHub and Salesforce integration refers to connecting Salesforce development environments with GitHub repositories to manage source code, track changes, automate deployments, and support collaborative development.
Instead of making changes directly in production environments, developers can:
- Store Salesforce metadata in GitHub
- Track code changes
- Collaborate through pull requests
- Automate deployments
- Roll back changes when necessary
- Implement CI/CD pipelines
This approach aligns Salesforce development with modern software engineering practices.
Why Integrate GitHub with Salesforce?
If your Salesforce development team is still using Change Sets to deploy code — copying configurations between orgs one sandbox at a time, without version history, without automated tests, and without any way to review changes before they hit production — you are running a very real risk every single day.
Integrating Salesforce with GitHub transforms your development workflow from a fragile, manual process into a disciplined engineering pipeline. Every change is tracked. Every deployment is reviewed. Every push to production is gated by automated tests. Rollbacks become trivial. Collaboration becomes parallel instead of sequential.
Version Control for Everything
Apex classes, Lightning components, flows, custom objects, permission sets — all tracked in Git with full history, blame, and diff support.
Automated Deployments
Merge to main and a GitHub Action automatically deploys to production. No manual steps, no forgotten configurations, no 2 AM deployments.
Pull Request Reviews
Every change goes through code review before it reaches any org. Your whole team can comment, approve, and catch bugs before they’re deployed.
Parallel Development
Multiple developers work on separate branches simultaneously, each with their own scratch org — no more “whose sandbox is this” confusion.
Read: The Ultimate Guide to Salesforce Integrations – Apps and Tools You Need
How GitHub Fits Into the Salesforce Development Lifecycle
A typical Salesforce development workflow includes:
- Development in Salesforce Sandbox
- Source code retrieval
- Code storage in GitHub
- Code review via Pull Requests
- Automated testing
- Deployment to staging
- Production deployment
GitHub becomes the single source of truth for Salesforce metadata.
Prerequisites & Tools
Before diving in, make sure you have everything in place. Attempting the integration without these foundations leads to frustrating dead ends.
| Tool / Account | What it’s for | Cost | Required? |
|---|---|---|---|
| GitHub Account | Hosts your source code and runs GitHub Actions | Free | Required |
| Salesforce Developer Org | Your Salesforce environment (Dev Hub enabled) | Free | Required |
| Node.js (v18+) | Runtime for Salesforce CLI | Free | Required |
| Salesforce CLI (sf) | Command-line tool for all Salesforce operations | Free | Required |
| VS Code | IDE with Salesforce Extension Pack | Free | Recommended |
| Git (v2.30+) | Local version control client | Free | Required |
| Salesforce Scratch Orgs | Disposable dev environments (needs Dev Hub) | Included | Recommended |
Developer Edition Org
If you don’t have a Salesforce org, create a free Developer Edition at developer.salesforce.com/signup. You’ll need to enable Dev Hub in Setup → Dev Hub to use scratch orgs, which are essential for the CI/CD pipeline we’re building.
Understanding Salesforce DX (SFDX)
Salesforce DX is the foundation of modern Salesforce development.
It enables:
- Source-driven development
- Team collaboration
- CI/CD automation
- Version control integration
Salesforce DX converts Salesforce metadata into source files that can be stored and managed in GitHub.
Without Salesforce DX, GitHub integration becomes significantly more difficult.
Also read: Salesforce Integration Strategy for Modern Enterprises
Step 1: Install & Configure Salesforce CLI
The Salesforce CLI (unified as sf) is the foundation of everything. It lets you authenticate to orgs, push and pull source code, run tests, and deploy — all from your terminal and from inside GitHub Actions.
Install the Salesforce CLI
# Install via npm (works on Mac, Windows, Linux)
npm install --global @salesforce/cli
# Verify installation
sf version
# Expected: @salesforce/cli/2.x.x linux-x64 node-v18.x.x
# Install Salesforce Extension Pack in VS Code
# Extensions panel → search "Salesforce Extension Pack" → Install
Authenticate to Your Salesforce Org
Authenticate the CLI to your Salesforce org — both your Developer Hub and your target orgs.
# Authenticate to your Dev Hub org (opens browser)
sf org login web --set-default-dev-hub --alias MyDevHub
# Authenticate to your sandbox
sf org login web --instance-url https://test.salesforce.com --alias MySandbox
# Verify connected orgs
sf org list
# Set your Dev Hub as default
sf config set target-dev-hub=MyDevHub
Pro tip — JWT Auth for CI/CD
The browser-based login is for local development only. For GitHub Actions, you’ll need JWT-based authentication (covered in Step 3) which works headlessly — no browser, no interactive login, just a certificate and a connected app.
Step 2: Set Up Your GitHub Repository
Your GitHub repository is the central source of truth for your Salesforce project. All metadata, Apex code, flows, tests, and pipeline configuration lives here.
Create a New SFDX Project
# Create a new Salesforce DX project
sf project generate --name my-salesforce-project
cd my-salesforce-project
# Initialize Git and push to GitHub
git init
git checkout -b main
gh repo create my-salesforce-project --private --source=. --remote=origin
git add .
git commit -m "chore: initial SFDX project scaffold"
git push -u origin main
Configure Your .gitignore
# Salesforce DX
.sfdx/
.sf/
.localdevserver/
# Node / npm
node_modules/
# Secrets — NEVER commit these
.env
server.key
*.pem
# OS / IDE
.DS_Store
Thumbs.db
Critical — Never commit credentials
Your server.key and any .env files containing org credentials must never be committed to Git. Use GitHub Secrets for all sensitive values. A leaked private key gives full API access to your Salesforce org.
Step 3: Connect Your Salesforce Org to GitHub
Connecting your Salesforce org to GitHub for automated deployments requires a Connected App in Salesforce and JWT-based authentication. This allows GitHub Actions to authenticate headlessly — no browser or interactive login required.
1. Generate a Self-Signed Certificate
# Generate private key and self-signed certificate
openssl genrsa -out server.key 2048
openssl req -new -key server.key -out server.csr
openssl x509 -req -sha256 -days 365 -in server.csr -signkey server.key -out server.crt
# server.key = private key → GitHub Secret (NEVER commit to Git)
# server.crt = public cert → upload to Salesforce Connected App
2. Create a Connected App in Salesforce
Navigate to App Manager
In Salesforce Setup, search for App Manager. Click New Connected App in the top right.
Fill in basic information
Name it “GitHub CI/CD”. Add your email as contact. These are identification fields only.
Enable OAuth Settings
Check “Enable OAuth Settings”. Set Callback URL to http://localhost:1717/OauthRedirect. Add scopes: api, refresh_token, and full.
Enable JWT / Digital Signature
Check “Use digital signatures”. Upload server.crt (the public certificate — safe to upload).
Save and copy your Consumer Key
Save the Connected App, then navigate to its detail page and copy the Consumer Key. You’ll add this as a GitHub Secret.
3. Add Secrets to GitHub
Go to your GitHub repo → Settings → Secrets and variables → Actions and add:
| Secret Name | Value |
|---|---|
| SALESFORCE_JWT_SECRET_KEY | Contents of server.key file |
| SALESFORCE_CONSUMER_KEY | Consumer Key value |
| SALESFORCE_USERNAME | Your Salesforce login username |
| SALESFORCE_INSTANCE_URL | e.g. https://mycompany.my.salesforce.com |
Check out: Salesforce Integration Companies vs. In-House Teams
Step 4: Structure Your SFDX Project Correctly
A well-structured SFDX project organises Salesforce metadata in a way that Git can track meaningfully — one file per component, human-readable and diffable.
my-salesforce-project/ ├── .github/workflows/ │ ├── ci.yml # PR validation pipeline │ └── deploy.yml # Production deploy pipeline ├── force-app/main/default/ │ ├── classes/ # Apex classes (.cls + -meta.xml) │ ├── lwc/ # Lightning Web Components │ ├── flows/ # Flow definitions │ ├── objects/ # Custom objects and fields │ ├── permissionsets/ # Permission sets │ └── triggers/ # Apex triggers ├── config/ │ └── project-scratch-def.json ├── sfdx-project.json ├── .forceignore ├── .gitignore └── README.md
Configure sfdx-project.json
{
"packageDirectories": [
{
"path": "force-app",
"default": true,
"package": "MySalesforceApp",
"versionNumber": "1.0.0.NEXT"
}
],
"name": "my-salesforce-project",
"namespace": "",
"sourceApiVersion": "61.0",
"sfdcLoginUrl": "https://login.salesforce.com"
}
Step 5: Working with Scratch Orgs
Scratch orgs are disposable, fully configured Salesforce environments that spin up in seconds. Each developer works in their own scratch org — no more stepping on each other’s configurations in a shared sandbox.
# Create a scratch org (valid for 7 days)
sf org create scratch \
--definition-file config/project-scratch-def.json \
--alias feature-my-feature \
--duration-days 7 \
--set-default
# Push your source to the scratch org
sf project deploy start --target-org feature-my-feature
# Open the scratch org in a browser
sf org open --target-org feature-my-feature
# Pull changes made in the org back to local files
sf project retrieve start --target-org feature-my-feature
# Delete scratch org when done
sf org delete scratch --target-org feature-my-feature --no-prompt
Also check: How to Build a CI/CD Pipeline – Step-by-Step Guide
Step 6: Build Your GitHub Actions CI/CD Pipeline
Two workflows cover the full lifecycle: one validates pull requests, one deploys on merge to main.
Workflow 1: PR Validation (ci.yml)
name: Salesforce CI — Pull Request Validation
on:
pull_request:
branches: [ main, staging ]
jobs:
validate:
runs-on: ubuntu-latest
steps:
- name: Checkout source
uses: actions/checkout@v4
- name: Install Salesforce CLI
run: npm install --global @salesforce/cli
- name: Authenticate to Dev Hub via JWT
run: |
echo "${{ secrets.SALESFORCE_JWT_SECRET_KEY }}" > server.key
sf org login jwt \
--client-id "${{ secrets.SALESFORCE_CONSUMER_KEY }}" \
--jwt-key-file server.key \
--username "${{ secrets.SALESFORCE_USERNAME }}" \
--instance-url "${{ secrets.SALESFORCE_INSTANCE_URL }}" \
--alias DevHub \
--set-default-dev-hub
- name: Create scratch org
run: |
sf org create scratch \
--definition-file config/project-scratch-def.json \
--alias ci-scratch \
--duration-days 1 \
--set-default
- name: Deploy source to scratch org
run: |
sf project deploy start \
--target-org ci-scratch \
--ignore-conflicts
- name: Run Apex tests
run: |
sf apex run test \
--target-org ci-scratch \
--test-level RunLocalTests \
--output-dir test-results \
--result-format junit \
--wait 20
- name: Upload test results
if: always()
uses: actions/upload-artifact@v4
with:
name: apex-test-results
path: test-results/
- name: Delete scratch org
if: always()
run: sf org delete scratch --target-org ci-scratch --no-prompt
Workflow 2: Production Deploy (deploy.yml)
name: Salesforce Deploy — Production
on:
push:
branches: [ main ]
jobs:
deploy-production:
runs-on: ubuntu-latest
environment: production
steps:
- name: Checkout source
uses: actions/checkout@v4
- name: Install Salesforce CLI
run: npm install --global @salesforce/cli
- name: Authenticate to Production Org
run: |
echo "${{ secrets.PROD_JWT_SECRET_KEY }}" > server.key
sf org login jwt \
--client-id "${{ secrets.PROD_CONSUMER_KEY }}" \
--jwt-key-file server.key \
--username "${{ secrets.PROD_USERNAME }}" \
--instance-url https://login.salesforce.com \
--alias Production
- name: Validate deployment first (dry run)
run: |
sf project deploy validate \
--target-org Production \
--test-level RunLocalTests \
--wait 30
- name: Deploy to Production
run: |
sf project deploy start \
--target-org Production \
--test-level RunLocalTests \
--wait 30
Protect your main branch
GitHub → Settings → Branches → Add rule for main: require pull request reviews, require CI status checks to pass, and enable “Require linear history”. This makes it impossible to push directly to production — every change must go through the full pipeline.
Also check: How to Integrate Gmail with Salesforce (Step-by-Step Guide)
Step 7: Automated Testing in the Pipeline
Salesforce requires 75% code coverage to deploy to production. But aiming for the minimum is the wrong goal — good test coverage is what catches regressions before they reach customers.
Writing a Basic Apex Test Class
@isTest
public class AccountServiceTest {
@testSetup
static void setup() {
Account acc = new Account(Name = 'Test Account', Industry = 'Technology');
insert acc;
}
@isTest
static void testAccountCreation() {
Test.startTest();
Account acc = [SELECT Id, Name FROM Account LIMIT 1];
System.assertEquals('Test Account', acc.Name, 'Name should match');
Test.stopTest();
}
@isTest
static void testBulkOperation() {
// Always test bulk scenarios — governor limits apply
List
for (Integer i = 0; i < 200; i++) {
accounts.add(new Account(Name = 'Bulk Account ' + i));
}
Test.startTest();
insert accounts;
Test.stopTest();
System.assertEquals(200, [SELECT COUNT() FROM Account WHERE Name LIKE 'Bulk%']);
}
}
Test Level Reference
RunLocalTests runs all tests not from managed packages — right for production. RunSpecifiedTests runs only named test classes — faster for PR validation. RunAllTestsInOrg includes managed package tests — rarely needed.
Step 8: Deploy to Staging & Production
A production-ready pipeline deploys in stages: feature → staging sandbox → UAT → production. Use sf project deploy validate for a dry-run before any production deployment.
# Validate against production (no changes applied)
sf project deploy validate \
--target-org Production \
--test-level RunLocalTests \
--wait 30
# Quick-deploy the validated package (skips re-running tests)
sf project deploy quick \
--use-most-recent \
--target-org Production
# Check deploy status
sf project deploy report --target-org Production
# Cancel if something went wrong
sf project deploy cancel --use-most-recent --target-org Production
Step 9: Branch Strategy for Salesforce Teams
A clear branch strategy separates functional integrations from chaotic ones. The recommended model for Salesforce teams is simplified Gitflow — structured enough for release management, simple enough to actually follow.
| Branch | Purpose | Deploys to | Protected? |
|---|---|---|---|
| main | Production-ready code at all times | Production org | Yes — PR required |
| staging | Pre-production integration & UAT | Full Sandbox | Yes — PR required |
| develop | Daily integration branch | Developer Sandbox | Optional |
| feature/[name] | Individual feature work | Scratch Org | No |
| hotfix/[name] | Emergency production fixes | Production (after review) | PR required |
Naming convention
Use descriptive, consistent branch names: feature/W-1234-lead-routing-automation, bugfix/W-1256-opportunity-trigger, hotfix/W-1270-approval-null-pointer. This makes PR history readable without needing to open Jira to understand what a branch does.
Troubleshooting Common Issues
| Error / Symptom | Likely Cause | Fix |
|---|---|---|
| JWT auth fails in Actions | Connected App not approved or username mismatch | Profile → Connected App → "Manage Policies" → set to "Admin approved" and pre-authorise the user |
| INVALID_SESSION_ID | Wrong instance URL or expired credentials | Verify SALESFORCE_INSTANCE_URL secret matches your org URL exactly (no trailing slash) |
| Scratch org creation fails | Dev Hub not enabled, or daily limit reached | Setup → Dev Hub → Enable. Check active scratch org count with sf org list --all |
| Deploy fails — missing metadata | Components in org not tracked in source | Run sf project retrieve start --manifest package.xml to pull all org metadata |
| Test coverage below 75% | Insufficient Apex test classes | Run sf apex run test --code-coverage locally to see per-class coverage before pushing |
| Workflow not triggering | YAML indentation error or wrong branch name | Use a YAML linter. Check branch names in on.push.branches match exactly |
Best Practices for GitHub and Salesforce Integration
Use Salesforce DX
Avoid legacy Change Sets whenever possible.
Keep Metadata Modular
Store components logically.
Avoid massive deployments.
Use Branch Protection
Protect:
main
from direct commits.
Require pull request approvals.
Automate Testing
Run:
- Apex tests
- Static code analysis
- Security checks
before deployments.
Maintain Documentation
Document:
- Branching strategy
- Deployment process
- Rollback procedures
Read: How SAP + Salesforce Integration Transforms Your Pipeline
Common Challenges and Solutions
Metadata Conflicts
Problem:
Multiple developers modify the same component.
Solution:
Use feature branches and frequent merges.
Deployment Failures
Problem:
Missing dependencies.
Solution:
Validate deployments before production.
Profile Complexity
Problem:
Profiles create merge conflicts.
Solution:
Use Permission Sets wherever possible.
Large Metadata Repositories
Problem:
Slow repository performance.
Solution:
Modularize metadata and use package-based development.
GitHub Integration vs Salesforce Change Sets
| Feature | GitHub Integration | Change Sets |
|---|---|---|
| Version Control | Yes | No |
| Collaboration | Excellent | Limited |
| Rollback Support | Yes | Limited |
| CI/CD Support | Yes | No |
| Code Reviews | Yes | No |
| Automation | Extensive | Minimal |
| Scalability | High | Moderate |
GitHub provides a significantly more scalable approach.
Security Considerations
When integrating GitHub and Salesforce:
Use Secrets Management
Never store:
- Passwords
- Tokens
- Certificates
inside repositories.
Use GitHub Secrets.
Enable Multi-Factor Authentication
Protect:
- GitHub accounts
- Salesforce accounts
Restrict Repository Access
Use least-privilege access principles.
Frequently Asked Questions
Why integrate GitHub with Salesforce?
GitHub provides version control, collaboration, automation, and DevOps capabilities that improve Salesforce development workflows.
Do I need Salesforce DX?
Yes. Salesforce DX is the recommended framework for integrating Salesforce with GitHub.
Can GitHub automate Salesforce deployments?
Yes. GitHub Actions can automate validation, testing, and deployments.
Is GitHub better than Change Sets?
For modern development teams, GitHub offers significantly more flexibility, scalability, and automation than Change Sets.
What is the best branching strategy for Salesforce projects?
Most teams use:
- Main
- Develop
- Feature branches
combined with Pull Requests and code reviews.
Conclusion
Integrating GitHub with Salesforce is no longer just a best practice—it's becoming a necessity for modern Salesforce development teams.
By combining Salesforce DX, GitHub repositories, branching strategies, pull requests, and CI/CD automation, organizations can improve collaboration, reduce deployment risks, accelerate development cycles, and maintain higher code quality.
Whether you're managing a small Salesforce implementation or a large enterprise environment, GitHub integration provides the foundation for scalable, efficient, and future-ready Salesforce DevOps.
Organizations that embrace GitHub-based Salesforce development gain better visibility, stronger governance, faster releases, and a more reliable path from development to production.






