Quick start
This guide will help you deploy Safe Settings and configure your first repository policies in under 10 minutes.Prerequisites
Before you begin, ensure you have:- Organization admin access: Required to create and install GitHub Apps
- Admin repository: You’ll create this during setup (default name:
admin) - Deployment environment: Choose from Docker, AWS Lambda, Kubernetes, or GitHub Actions
.env:
http://localhost:3000 and follow the prompts to create your GitHub App
- GitHub App name:
safe-settings(or your preferred name) - Homepage URL: Your repository or documentation URL
- Webhook URL:
https://your-domain.com/api/github/webhooks - Webhook secret: Generate with
openssl rand -base64 32
- Actions: Read-only
- Administration: Read & Write
- Checks: Read & Write
- Commit statuses: Read & Write
- Contents: Read & Write
- Custom properties: Read & Write
- Environments: Read & Write
- Issues: Read & Write
- Metadata: Read-only
- Pull requests: Read & Write
- Variables: Read & Write
- Administration: Read & Write
- Custom properties: Admin
- Members: Read & Write
- Branch protection rule
- Check run
- Check suite
- Create
- Custom property values
- Member
- Pull request
- Push
- Repository
- Repository ruleset
- Team
When installing the app, select All repositories to allow Safe Settings to manage all repos in your organization.
# .env file
APP_ID=123456
WEBHOOK_SECRET=your-webhook-secret-from-step-1
PRIVATE_KEY="$(cat path/to/private-key.pem)"
# Or use PRIVATE_KEY_PATH instead:
# PRIVATE_KEY_PATH=/path/to/private-key.pem
PRIVATE_KEY takes precedence over PRIVATE_KEY_PATH. For production, base64-encode the private key and use PRIVATE_KEY.# Admin repository name (default: admin)
ADMIN_REPO=admin
# Configuration path within admin repo (default: .github)
CONFIG_PATH=.github
# Settings file name (default: settings.yml)
SETTINGS_FILE_PATH=settings.yml
# Scheduled sync (cron syntax)
CRON="0 * * * *" # Run every hour
# Logging level
LOG_LEVEL=info # trace, debug, info, warn, error
# Pull request comments (default: true)
ENABLE_PR_COMMENT=true
# Block manual repo renaming (default: false)
BLOCK_REPO_RENAME_BY_HUMAN=true
Docker
AWS Lambda
For production-ready AWS Lambda deployment, use the SafeSettings-Template:
- Click “Use this template” on GitHub
- Configure AWS credentials in GitHub Secrets
- Set environment variables in
serverless.yml - Deploy with GitHub Actions
Local
For local development, use smee.io to forward webhooks:
# Create a private admin repository
gh repo create your-org/admin --private
# Clone it locally
gh repo clone your-org/admin
cd admin
admin/
├── .github/
│ ├── settings.yml # Organization-wide settings
│ ├── suborgs/ # Sub-organization configs
│ │ ├── frontend-team.yml
│ │ └── backend-team.yml
│ └── repos/ # Repository-specific configs
│ ├── api-service.yml
│ └── web-app.yml
├── deployment-settings.yml # Runtime configuration (optional)
└── README.md
# Organization-wide repository defaults
repository:
# Make repositories private by default
private: true
# Enable features
has_issues: true
has_projects: true
has_wiki: false
# Default branch
default_branch: main
# Auto-delete merged branches
delete_branch_on_merge: true
# Merge strategies
allow_squash_merge: true
allow_merge_commit: true
allow_rebase_merge: true
# Standard labels for all repositories
labels:
- name: bug
color: CC0000
description: Something isn't working
- name: enhancement
color: 336699
description: New feature or request
- name: documentation
color: 0075ca
description: Improvements or additions to documentation
# Branch protection for default branch
branches:
- name: default
protection:
required_pull_request_reviews:
required_approving_review_count: 1
dismiss_stale_reviews: true
require_code_owner_reviews: true
required_status_checks:
strict: true
contexts: []
enforce_admins: false
restrictions: null
Use
name: default in branch protection to automatically apply rules to each repository’s default branch (main, master, etc.).Create
deployment-settings.yml in the admin repository root to restrict which repositories Safe Settings manages:# Restrict Safe Settings to specific repositories
restrictedRepos:
exclude:
- admin
- .github
- safe-settings
- test-*
include:
- prod-*
- api-*
# Custom validation rules
configvalidators:
- plugin: collaborators
error: "Admin role cannot be assigned to collaborators"
script: |
console.log(`Validating collaborator: ${JSON.stringify(baseconfig)}`)
return baseconfig.permission !== 'admin'
overridevalidators:
- plugin: branches
error: "Branch protection cannot be weakened"
script: |
if (baseconfig.protection?.required_pull_request_reviews?.required_approving_review_count &&
overrideconfig.protection?.required_pull_request_reviews?.required_approving_review_count) {
return overrideconfig.protection.required_pull_request_reviews.required_approving_review_count >=
baseconfig.protection.required_pull_request_reviews.required_approving_review_count
}
return true
Safe Settings only processes changes on the default branch. Changes on other branches are validated in dry-run mode.
.github/settings.ymlWhat’s next?
Test with a pull request
Create a PR with configuration changes to see dry-run validation in action
Add sub-org configurations
Create team-specific settings in
.github/suborgs/Configure specific repositories
Override settings for individual repos in
.github/repos/Set up scheduled sync
Configure
CRON environment variable for drift preventionCommon issues
Settings aren't being applied
Settings aren't being applied
Check:
- GitHub App is installed with “All repositories” access
- Admin repository name matches
ADMIN_REPOenvironment variable (default:admin) - Configuration files are in
.github/directory on the default branch - Repository is not in the
restrictedReposexclude list - Check the Safe-Settings check run for error details
Webhook events not received
Webhook events not received
Check:
- Webhook URL is correct in GitHub App settings
- Webhook secret matches
WEBHOOK_SECRETenvironment variable - Safe Settings is running and accessible at the webhook URL
- Recent deliveries tab in GitHub App settings shows successful deliveries
Private key authentication failed
Private key authentication failed
Check:
APP_IDmatches your GitHub App’s ID- Private key is correctly formatted (include BEGIN/END lines)
PRIVATE_KEYorPRIVATE_KEY_PATHis set correctly- For
PRIVATE_KEY, ensure newlines are preserved or base64-encoded
Branch protection not applying
Branch protection not applying
Remember:
- Use
name: defaultto apply to the default branch - All required fields must be present (don’t omit any protection keys)
- Set unwanted protections to
nullexplicitly - Status check contexts must exist before adding them
Next steps
How It Works
Learn about the architecture, webhook events, and configuration merging
Configuration Reference
Explore all available settings and configuration options