Configuration Reference
github-webhook-server reads its main configuration from config.yaml in the server data directory. In code, that directory defaults to /home/podman/data, so the default config path is /home/podman/data/config.yaml. Relative log file names are resolved under <data_dir>/logs/.
The checked-in example file shows the top-level shape:
log-level: INFO # Set global log level, change take effect immediately without server restart
log-file: webhook-server.log # Set global log file, change take effect immediately without server restart
mcp-log-file: mcp_server.log # Set global MCP log file, change take effect immediately without server restart
logs-server-log-file: logs_server.log # Set global Logs Server log file, change take effect immediately without server restart
mask-sensitive-data: true # Mask sensitive data in logs (default: true). Set to false for debugging (NOT recommended in production)
# Server configuration
disable-ssl-warnings: true # Disable SSL warnings (useful in production to reduce log noise from SSL certificate issues)
github-app-id: 123456 # GitHub app id
github-tokens:
- <GITHIB TOKEN1>
- <GITHIB TOKEN2>
webhook-ip: <HTTP://IP OR URL:PORT/webhook_server> # Full URL with path (e.g., https://your-domain.com/webhook_server or https://smee.io/your-channel)
docker: # Used to pull images from docker.io
username: <username>
password: <password>
Repository-specific settings live under repositories:
repositories:
my-repository:
name: my-org/my-repository
log-level: DEBUG # Override global log-level for repository
log-file: my-repository.log # Override global log-file for repository
mask-sensitive-data: false # Override global setting - disable masking for debugging this specific repo (NOT recommended in production)
slack-webhook-url: <Slack webhook url> # Send notification to slack on several operations
verified-job: true
pypi:
token: <PYPI TOKEN>
events: # To listen to all events do not send events
- push
- pull_request
- pull_request_review
- pull_request_review_thread
- issue_comment
- check_run
- status
tox:
main: all # Run all tests in tox.ini when pull request parent branch is main
dev: testenv1,testenv2 # Run testenv1 and testenv2 tests in tox.ini when pull request parent branch is dev
pre-commit: true # Run pre-commit check
protected-branches:
dev: []
main: # set [] in order to set all defaults run included
include-runs:
- "pre-commit.ci - pr"
- "WIP"
exclude-runs:
- "SonarCloud Code Analysis"
container:
username: <registry username>
password: <registry_password>
repository: <registry_repository_full_path>
tag: <image_tag>
release: true # Push image to registry on new release with release as the tag
build-args: # build args to send to podman build command
- my-build-arg1=1
- my-build-arg2=2
args: # args to send to podman build command
- --format docker
Note: In
repositories, the map key is the short GitHub repository name, whilenameinside the block is the fullowner/repo.Note: This page lists keys in
config.yamlform. The sample.github-webhook-server.yamluses the same repository-level shape without the surroundingrepositories.<repo>wrapper.Note: Most repository settings replace the global value entirely. Two important exceptions are
branch-protection, which is merged with global defaults, andlabels.colors, where repository colors override only the keys you redefine.Warning: Use exact branch names for
toxandprotected-branches, and use string values such asallortestenv1,testenv2fortox. The current runner/setup code looks up branches by exact key and builds the tox command from a string value.
Global settings
Logging and diagnostics
log-level: Global application log level. Allowed values areINFOandDEBUG.log-file: Main webhook server log file. Relative names are written under<data_dir>/logs/; absolute paths are used as-is.mcp-log-file: Separate log file for the optional MCP server. Default ismcp_server.log.logs-server-log-file: Separate log file for the optional log viewer / logs server. Default islogs_server.log.mask-sensitive-data: Enables log redaction. Default istrue. When enabled, the logger masks secrets such as tokens, passwords, webhook secrets, Slack webhook URLs, and similar values.
Warning:
labels.colorsandpr-size-thresholds.*.colorexpect CSS3 color names such asgreen,orange,royalblue, anddarkred. The label code converts those names to hex internally; hex strings are not the documented input format.
Server, webhook, and security
webhook-ip: The public webhook URL that GitHub should call. Include the full path, for examplehttps://example.com/webhook_server.ip-bind: The bind address for the FastAPI / uvicorn server. If omitted, startup defaults to0.0.0.0.port: The listening port. If omitted, startup defaults to5000.max-workers: Uvicorn worker count. If omitted, startup defaults to10.webhook-secret: Optional shared secret for GitHub webhook signature verification. When set, the server validates the incomingx-hub-signature-256header and uses the same secret when it creates GitHub webhooks.verify-github-ips: Iftrue, only accept webhook requests from GitHub’s published webhook IP ranges.verify-cloudflare-ips: Iftrue, also trust Cloudflare’s published IP ranges. This is useful when traffic reaches the server through Cloudflare.disable-ssl-warnings: Iftrue, suppressurllib3SSL warnings during runtime.
Warning: IP allowlist verification is fail-closed. If
verify-github-ipsand/orverify-cloudflare-ipsare enabled but the allowlists cannot be loaded, the server aborts startup instead of accepting requests insecurely.
GitHub authentication and shared defaults
github-app-id: GitHub App ID used for app-scoped repository management. In practice this goes with awebhook-server.private-key.pemfile in the data directory and an installed GitHub App.github-tokens: List of GitHub tokens used for normal API calls. The server checks all configured tokens and picks the one with the highest remaining rate limit.docker.username: Docker Hub username used for the startuppodman loginstep.docker.password: Docker Hub password used for the startuppodman loginstep.default-status-checks: Extra check or status context names that should always be part of the generated branch-protection rules. Use exact GitHub context names.auto-verified-and-merged-users: Global default list of users or bots whose PRs can be auto-verified and auto-merged when the other merge rules are satisfied.auto-verify-cherry-picked-prs: Global default for automatic verification of cherry-picked PRs. Default istrue.create-issue-for-new-pr: Global default for creating a tracking issue when a new PR opens. Default istrue.cherry-pick-assign-to-pr-author: Global default for assigning cherry-pick PRs to the original PR author. Default istrue.allow-commands-on-draft-prs: Global default for user commands on draft PRs. Omit it to block commands on draft PRs. Set it to[]to allow all commands. Set it to a list such as["build-and-push-container", "retest"]to allow only those command names.
Tip: Repository-level
github-tokensreplace the global token list for that repository. During webhook processing, the server also adds the GitHub users behind the active API tokens to the auto-verified user list.
Labels and PR size
The sample config includes label and size settings like this:
labels:
# Optional: List of label categories to enable
# If not set, all labels are enabled. If set, only listed categories are enabled.
# Note: reviewed-by labels (approved-*, lgtm-*, etc.) are always enabled and cannot be disabled
enabled-labels:
- verified
- hold
- wip
- needs-rebase
- has-conflicts
- can-be-merged
- size
- branch
- cherry-pick
- automerge
# Optional: Custom colors for labels (CSS3 color names)
colors:
hold: red
verified: green
wip: orange
needs-rebase: darkred
has-conflicts: red
can-be-merged: limegreen
automerge: green
# Dynamic label prefixes
approved-: green
lgtm-: yellowgreen
changes-requested-: orange
commented-: gold
cherry-pick-: coral
branch-: royalblue
# Global PR size label configuration (optional)
# Define custom categories based on total lines changed (additions + deletions)
# threshold: positive integer or 'inf' for unbounded largest category
# color: CSS3 color name (e.g., red, green, blue, lightgray, darkorange)
# Infinity behavior: 'inf' ensures all PRs beyond largest finite threshold are captured
# Always sorted last, regardless of definition order
pr-size-thresholds:
Tiny:
threshold: 10 # PRs with 0-9 lines changed
color: lightgray
Small:
threshold: 50 # PRs with 10-49 lines changed
color: green
Medium:
threshold: 150 # PRs with 50-149 lines changed
color: orange
Large:
threshold: 300 # PRs with 150-299 lines changed
color: red
Massive:
threshold: inf # PRs with 300+ lines changed (unbounded largest category)
color: darkred # 'inf' means no upper limit - catches all PRs above 300 lines
labels.enabled-labels: List of label categories to allow. Valid categories areverified,hold,wip,needs-rebase,has-conflicts,can-be-merged,size,branch,cherry-pick, andautomerge. If omitted, all configurable categories are enabled. If set to[], all configurable categories are disabled. Review-state labels such asapproved-*,lgtm-*,changes-requested-*, andcommented-*are always enabled.labels.colors: Map of label names or dynamic label prefixes to CSS3 color names. Exact keys such asholdorverifiedaffect one label. Prefix keys such asapproved-orbranch-affect any label that starts with that prefix.pr-size-thresholds.<label>.threshold: Threshold used to compute the PR size label. The handler sorts thresholds ascending and picks the first bucket wheretotal_changes < threshold. Useinffor the open-ended largest bucket.pr-size-thresholds.<label>.color: CSS3 color name for that bucket.
If you do not configure pr-size-thresholds, the built-in buckets are:
STATIC_PR_SIZE_THRESHOLDS: tuple[tuple[int | float, str, str], ...] = (
(20, "XS", "ededed"),
(50, "S", "0E8A16"),
(100, "M", "F09C74"),
(300, "L", "F5621C"),
(500, "XL", "D93F0B"),
(float("inf"), "XXL", "B60205"),
)
Branch protection
branch-protection.strict: GitHub branch protectionstrictsetting. Global default istrue.branch-protection.require_code_owner_reviews: Require CODEOWNERS reviews. Global default isfalse.branch-protection.dismiss_stale_reviews: Dismiss stale reviews on new commits. Global default istrue.branch-protection.required_approving_review_count: Required approval count. Global default is0.branch-protection.required_linear_history: Require linear history. Global default istrue.branch-protection.required_conversation_resolution: Require resolved review conversations. Global default istrue. The webhook processor also uses this flag when deciding whether resolved/unresolved review-thread events should affect mergeability.
PR Test Oracle and AI features
The sample global config includes both test-oracle and ai-features:
branch-protection:
strict: True
require_code_owner_reviews: True
dismiss_stale_reviews: False
required_approving_review_count: 1
required_linear_history: True
required_conversation_resolution: True
# PR Test Oracle integration
# Analyzes PR diffs with AI and recommends which tests to run
# See: https://github.com/myk-org/pr-test-oracle
test-oracle:
server-url: "http://localhost:8000"
ai-provider: "claude" # claude | gemini | cursor
ai-model: "claude-opus-4-6[1m]"
test-patterns:
- "tests/**/*.py"
triggers: # Default: [approved]
- approved # Run when /approve command is used
# - pr-opened # Run when PR is opened
# - pr-synchronized # Run when new commits pushed
# AI Features configuration
# Enables AI-powered enhancements (e.g., conventional title suggestions)
ai-features:
ai-provider: "claude" # claude | gemini | cursor
ai-model: "claude-opus-4-6[1m]"
conventional-title:
enabled: true
mode: suggest # suggest: show in checkrun | fix: auto-update PR title
timeout-minutes: 10
resolve-cherry-pick-conflicts-with-ai:
enabled: true
timeout-minutes: 10 # Timeout in minutes for AI CLI (default: 10)
test-oracle.server-url: Base URL of the PR Test Oracle service.test-oracle.ai-provider: AI provider sent to the oracle. Allowed values areclaude,gemini, andcursor.test-oracle.ai-model: AI model sent to the oracle.test-oracle.test-patterns: Optional test-file globs sent to the oracle astest_patterns.test-oracle.triggers: Automatic oracle triggers. Valid values areapproved,pr-opened, andpr-synchronized. Default is["approved"]. Here,approvedmeans the/approvecomment command, not GitHub’s native review approval event.ai-features.ai-provider: AI CLI provider for built-in AI features.ai-features.ai-model: AI CLI model for built-in AI features.ai-features.conventional-title.enabled: Turn AI assistance for failedconventional-titlechecks on or off.ai-features.conventional-title.mode:suggestappends an AI-suggested PR title to the failed check output.fixauto-edits the PR title when the suggestion validates.ai-features.conventional-title.timeout-minutes: Timeout for the title-suggestion AI CLI call. Default is10.ai-features.resolve-cherry-pick-conflicts-with-ai.enabled: Allow AI conflict resolution during cherry-pick workflows.ai-features.resolve-cherry-pick-conflicts-with-ai.timeout-minutes: Timeout for the cherry-pick conflict resolution AI CLI call. Default is10.
Note:
/test-oraclealways works whentest-oracleis configured, even if the current event is not listed intriggers.Note: If the Test Oracle health check fails, the server posts a PR comment and skips analysis. If the AI CLI fails for
ai-features, the server logs the problem and continues without blocking the rest of the webhook flow.Warning: AI-resolved cherry-picks are never auto-verified. They always require manual review after the conflict resolution step.
Required top-level map
repositories: Required top-level map of repository-specific settings. The config loader refuses a config file that does not containrepositories.
Repository settings
The following keys are written under repositories.<short-repo-name> in config.yaml.
Identity, logging, and webhook subscription
name: Full repository name inowner/repoform. This is required.log-level: Repository-specific log level override.log-file: Repository-specific log file override.mask-sensitive-data: Repository-specific redaction override.events: GitHub webhook events to subscribe when the server creates or updates the repo webhook. If omitted, webhook registration uses*. The current processing path explicitly handlespush,pull_request,pull_request_review,issue_comment,check_run,status,pull_request_review_thread, andping.github-tokens: Repository-specific token list. This replaces the globalgithub-tokenslist for that repository.slack-webhook-url: Slack incoming webhook for notifications related to PyPI publishing and container pushes.
Checks and CI behavior
verified-job: Enable or disable the built-inverifiedcheck. Default istrue. When disabled, the repo setup code does not addverifiedto generated required checks, and merge requirements do not include the verified step.tox: Map of exact base branch name to tox env selection. Useallto run the default tox config, or a comma-separated env list such astestenv1,testenv2.tox-python-version: Optional Python version passed asuvx --python=<version> tox ....pre-commit: Enable or disable the built-in pre-commit check. Default istrue.conventional-title: Comma-separated list of allowed Conventional Commit types for PR titles, such asfeat,fix,docs. Use*to accept any type while still enforcing the format<type>[optional scope]: <description>. This validates the PR title, not commit messages.custom-check-runs[].name: Custom GitHub check-run name. It must be unique, use only safe characters, and not collide with built-in names such astox,pre-commit,build-container,python-module-install,conventional-title, orcan-be-merged.custom-check-runs[].command: Command run in the repository worktree. Environment-variable prefixes and multiline commands are supported, but the executable must exist on the server.custom-check-runs[].mandatory: Whether the custom check must pass for mergeability. Default istrue.falsechecks still run; they just stop gating merges.test-oracle.server-url,test-oracle.ai-provider,test-oracle.ai-model,test-oracle.test-patterns,test-oracle.triggers: Same meanings as the globaltest-oraclekeys. A repository-level object replaces the global object for that repository.ai-features.ai-provider,ai-features.ai-model,ai-features.conventional-title.enabled,ai-features.conventional-title.mode,ai-features.conventional-title.timeout-minutes,ai-features.resolve-cherry-pick-conflicts-with-ai.enabled,ai-features.resolve-cherry-pick-conflicts-with-ai.timeout-minutes: Same meanings as the globalai-featureskeys. A repository-level object replaces the global object for that repository.
Tip: Custom check names become valid
/retest <name>targets, and/retest allincludes them.
Branch protection and merge policy
protected-branches: Map of exact branch names that should have branch protection configured by the startup repository-setup job.protected-branches.<branch>: []: Protect the branch and use the auto-generated required status-check list.protected-branches.<branch>.include-runs: Replace the auto-generated required status-check list with exactly these contexts.protected-branches.<branch>.exclude-runs: Remove these contexts from the auto-generated required list.branch-protection.strict,branch-protection.require_code_owner_reviews,branch-protection.dismiss_stale_reviews,branch-protection.required_approving_review_count,branch-protection.required_linear_history,branch-protection.required_conversation_resolution: Same meanings as the global branch-protection keys. Repository values are merged on top of the global defaults.
When the server auto-generates required checks for a protected branch, it starts from default-status-checks, always adds can-be-merged, and then adds built-in checks that are enabled for the repo. That includes tox, verified, build-container, python-module-install, pre-commit, conventional-title, and pre-commit.ci - pr when the repo contains a .pre-commit-config.yaml.
auto-verified-and-merged-users: Repository-specific auto-verified user list. This replaces the global list for that repository.auto-verify-cherry-picked-prs: Repository override for automatic verification of cherry-picked PRs. Default istrue.set-auto-merge-prs: List of base branch names that should be put into GitHub auto-merge. The server enables GitHub auto-merge with squash merge.can-be-merged-required-labels: Extra labels that must be present before thecan-be-mergedcheck can pass.minimum-lgtm: Minimum number of reviewer/lgtmlabels required before the PR is considered mergeable. Default is0.create-issue-for-new-pr: Repository override for tracking-issue creation on new PRs.cherry-pick-assign-to-pr-author: Repository override for assigning cherry-pick PRs to the original PR author.allow-commands-on-draft-prs: Repository override for draft-PR comment commands. Omit it to block commands on draft PRs. Set it to[]to allow all commands. Set it to a list to allow only those raw command names, for examplebuild-and-push-containerorretest.
Warning: For
protected-branches, stick to[]or the object form withinclude-runs/exclude-runs. Those are the forms the branch-protection setup code actually uses.Note:
/test-oracleis exempt from the normal draft-PR command restriction and can still be triggered when configured.
Labels and PR size at repo level
labels.enabled-labels: Repository-specific enabled-label list. If you set it, it replaces the global enabled-label list for that repository.labels.colors: Repository-specific color overrides. Repo color keys override the same keys from the globallabels.colorsmap and inherit the rest.pr-size-thresholds.<label>.threshold: Repository-specific PR size threshold.pr-size-thresholds.<label>.color: Repository-specific PR size color. Repositorypr-size-thresholdsreplace the global threshold map for that repository.
Publishing, releases, and notifications
pypi.token: Enable Python package publishing on tag pushes. On a tag push, the server builds an sdist withuv build, runstwine check, and uploads withtwine upload --skip-existing. If publishing fails, the handler opens a GitHub issue.container.username: Registry username used forpodman push.container.password: Registry password used forpodman push.container.repository: Full image repository, for examplequay.io/org/repoorghcr.io/org/pkg.container.tag: Default merged-PR image tag formainandmaster. If omitted, the build code defaults tolatest. PR builds usepr-<number>, and tag pushes use the Git tag itself.container.release: Iftrue, a tag push builds and pushes a release image. Iffalse, tag pushes do not push a release image.container.build-args: Extra--build-argvalues passed topodman build.container.args: Raw extra arguments prepended topodman build, for example--format dockeror--platform=linux/amd64.
The checked-in source handles release publishing only on tag pushes:
tag = re.search(r"^refs/tags/(.+)$", self.hook_data["ref"])
if tag:
tag_name = tag.group(1)
self.logger.info(f"{self.log_prefix} Processing push for tag: {tag.group(1)}")
self.logger.debug(f"{self.log_prefix} Tag: {tag_name}")
if self.github_webhook.pypi:
self.logger.info(f"{self.log_prefix} Processing upload to pypi for tag: {tag_name}")
try:
await self.upload_to_pypi(tag_name=tag_name)
except Exception as ex:
self.logger.exception(f"{self.log_prefix} PyPI upload failed")
if self.ctx:
self.ctx.fail_step("push_handler", ex, traceback.format_exc())
return
if self.github_webhook.build_and_push_container and self.github_webhook.container_release:
self.logger.info(f"{self.log_prefix} Processing build and push container for tag: {tag_name}")
try:
await self.runner_handler.run_build_container(push=True, set_check=False, tag=tag_name)
Note:
slack-webhook-urlis used for successful PyPI publish messages and for container push success/failure notifications. It is not a general-purpose notification switch for every webhook event.