root / hooks
File hooks run asynchronously via Sidekiq on system events. They cannot block actions.
| File | Event | Action |
|---|---|---|
notify-admin.rb |
project_create, group_create, user_create, user_add_to_team | Emails the admin via GitLab's configured SMTP |
discord-failed-login.rb |
user_failed_login | Posts a Discord embed when a blocked user tries to sign in |
notify-admin-granted.rb |
user_update_for_admin | Discord alert when a user is granted admin privileges |
auto-label-projects.rb |
project_create | Creates 32 industry standard labels on every new project |
# Install
cp hooks/<hook>.rb /opt/gitlab/embedded/service/gitlab-rails/file_hooks/
chmod +x /opt/gitlab/embedded/service/gitlab-rails/file_hooks/<hook>.rb
# Validate
gitlab-rake file_hooks:validateAll hooks read from /root/.secrets/gitlab.env:
| Hook | Required env vars |
|---|---|
notify-admin.rb |
GITLAB_ROOT_EMAIL, CERT_EMAIL |
discord-failed-login.rb |
DISCORD_WEBHOOK_URL_FAILEDLOGIN |
notify-admin-granted.rb |
DISCORD_WEBHOOK_URL_ADMIN |
auto-label-projects.rb |
GITLAB_API_TOKEN_HOOKS |
Hooks exit silently if their env vars are not set.
Server hooks run synchronously during git operations and can reject pushes. They are placed in the Gitaly custom hooks directory.
Important: GitLab 16+ requires hooks in
/var/opt/gitlab/gitaly/custom_hooks/, not the legacygitlab-railspath. You must also set the directory ingitlab.rb:gitaly['configuration'] = { hooks: { custom_hooks_dir: '/var/opt/gitlab/gitaly/custom_hooks' } }Then run
gitlab-ctl reconfigure.
| File | Type | Action |
|---|---|---|
enforce-branch-naming |
pre-receive | Rejects branches not matching feature/, fix/, hotfix/, release/, chore/, docs/ |
block-file-extensions |
pre-receive | Rejects pushes containing binaries, archives, secrets (.exe, .zip, .jar, .pem, etc.) |
enforce-commit-message |
pre-receive | Requires Conventional Commits (feat:, fix:, docs:, etc.). Merge commits exempt. |
enforce-max-file-size |
pre-receive | Rejects individual files exceeding 10 MB. Suggests Git LFS or object storage. |
block-submodule-changes |
pre-receive | Rejects pushes that add or modify .gitmodules. Submodules are not used in this environment. |
detect-secrets |
pre-receive | Scans diffs for 115+ secret patterns (API keys, private keys, tokens, connection strings). See below. |
Edit the arrays/patterns at the top of each script to customize. enforce-branch-naming exempts main and master.
# Install (global, all repos)
mkdir -p /var/opt/gitlab/gitaly/custom_hooks/pre-receive.d
cp hooks/enforce-branch-naming hooks/block-file-extensions \
hooks/enforce-commit-message hooks/enforce-max-file-size \
hooks/block-submodule-changes hooks/detect-secrets \
/var/opt/gitlab/gitaly/custom_hooks/pre-receive.d/
chmod +x /var/opt/gitlab/gitaly/custom_hooks/pre-receive.d/*
chown -R git:git /var/opt/gitlab/gitaly/custom_hooksSecret Push Protection for GitLab CE. Scans only the diff (added lines) for high-confidence patterns, covering 50+ providers:
| Category | Providers |
|---|---|
| Private keys | RSA, DSA, EC, OpenSSH, PGP, PKCS8 |
| Cloud providers | AWS, GCP/Firebase, Azure, Cloudflare (current + new scannable format), DigitalOcean |
| AI providers | OpenAI, Anthropic, Hugging Face, Replicate, Groq, Cursor, Cohere, Fireworks AI, Perplexity |
| Git platforms | GitHub (PAT, OAuth, fine-grained, app), GitLab (PAT, runner, deploy, trigger, 8 more) |
| CI/CD platforms | Buildkite, CircleCI, Netlify, Fly.io, Railway, Render, Tailscale |
| Communication | Slack (bot, user, app, webhook, config), Discord (bot, webhook), Telegram |
| Payment | Stripe (live secret, restricted, publishable) |
| Infrastructure | HashiCorp Vault/Terraform, Doppler, Pulumi, PlanetScale, Supabase, Grafana, Sentry, Pinecone |
| Secret management | 1Password, Age |
| Package registries | npm, PyPI, RubyGems, Docker |
| Email/messaging | SendGrid, Mailgun, Twilio |
| SaaS/productivity | Notion, Figma, Contentful, Airtable, Datadog |
| Other | Heroku, Postman, Linear, Shopify, Twitch, Twitter/X |
| Generic | Database connection strings, passwords in URLs, env var assignments (PASSWORD, SECRET, TOKEN, etc) |
Per-repo allowlist: Add a .secret-detection-allowlist file to a repository (one regex per line, # for comments) to suppress known false positives.
Skipped files: Lock files, minified JS/CSS, test fixtures, .env.example/.env.sample/.env.template.