Version Requirements CodeRabbit supports GitLab 16.x and above. Version 15.x may experience unexpected issues such as review comments not being posted or the sign-up process not working at all. We recommend upgrading your GitLab instance to obtain the intended experience.
Getting Started
To integrate your self-managed GitLab with CodeRabbit, we require specific information for the initial setup within your domain. Once this setup is complete, you can log in directly using the OAuth2 flow.
Visit CodeRabbit login page
Enter your GitLab instance URL
Enter the URL of your self-managed GitLab instance and click Submit . We’ll check our database for an existing record of your organization and start the login process if found. If your self-managed GitLab instance is not found, we’ll initiate the onboarding process.
Choose onboarding method
You can choose between automated or manual onboarding based on your security
preferences and administrative access.
Onboarding Options
Automated Onboarding (Recommended)
Why do we need the Admin Access Token? The admin access token is required to set up a new CodeRabbit bot user within your self-managed instance. The token is needed only once during the initial setup process. Once generated, you can set its minimum expiration period. This is the standard approach used by other products in this category. Note: This does not automatically install the CodeRabbit app across all projects. You will add CodeRabbit manually to the projects you wish to integrate.
Manual Onboarding
For the manual onboarding process, you need to create the CodeRabbit user and the OAuth2 GitLab application .
Creating CodeRabbit User
This feature will work with any user from your organization, but we strongly recommend creating a dedicated user called CodeRabbitAI . This ensures clarity about which user is used for our application and allows for better fine-grained access control.
Create the user
Log in with an instance admin account and follow the steps provided in the
GitLab
documentation
to create a new user.
Retrieve user information
After the user is created, retrieve the User ID from that user’s profile.
Generate access token
Generate an access token for this
user. The access token is used to post reviews on merge requests.
Recommendations for the CodeRabbit user: - Use “CodeRabbitAI” as the
username for easy identification - Use the CodeRabbit
logo as the profile picture for easy
recognition - Ensure the user has appropriate permissions for the repositories
you want to integrate
If you prefer, you can create a Group Access Token which will create a
dedicated user on your behalf. For more information, see Group Access
Token .
Creating OAuth2 Application
For self-managed GitLab, we recommend creating an instance-wide application unless you want the reviews to be limited to a single group or user.
Follow the steps outlined in the GitLab documentation for creating the application.
OAuth2 Application Requirements:
Scopes: api read_user email openid
Callback URL: https://app.coderabbit.ai/login
Generating Personal Access Token
GitLab offers an option to generate a personal access token for adding a new user and setting up the application in the self-managed instance.
Access user settings
On the left sidebar, select your avatar, then select Edit profile .
Navigate to Access Tokens
On the left sidebar, select Access Tokens .
Create new token
Select Add new token .
Configure token settings
Enter a name and expiry date for the token
We need this for the initial setup, so the minimum expiry time is sufficient
If you do not enter an expiry date, it defaults to 365 days from the current date
Select the required scopes: api, read_api, read_user
Generate and save token
Select Create personal access token and note down the token as it will
only be displayed once.
Paste the details and click submit
Submit the form.
We will handle the setup process for you.
On subsequent visits, your setup will be automatically detected, allowing for direct login.
Allow list CodeRabbit IP address
Use this CodeRabbit IP if your instance requires IP allow listing.
35.222.179.152/32, 34.170.211.100/32
Manual Webhook Installation
For administrators managing many GitLab projects, you can use a script to bulk-install webhooks across all projects.
Login to CodeRabbit UI
Login to CodeRabbit UI through your GitLab self-managed instance.
Access account settings
On the bottom left sidebar, select Account .
Navigate to Webhook Secret
On the left sidebar, select Webhook Secret .
Set webhook secret in CodeRabbit UI
Input a webhook secret. This secret will be used by CodeRabbit to verify incoming webhook events from your GitLab instance.
Run script to install webhooks
Use a script to install webhooks across your GitLab projects or groups. Below is a sample script that requires:
Your GitLab host URL
The CodeRabbit webhook URL: https://coderabbit.ai/gitlabHandler
The webhook secret you created in Step 1
A GitLab access token with API permissions
#!/usr/bin/env bash
## gitlab-webhook.sh
# Add a webhook to one project, or every project in a subgroup tree
set -euo pipefail
usage () {
cat << EOF
Usage:
$0 -h <gitlab-host> -u <webhook-url> -s <webhook-secret> \\
[-t <access-token>] [-A <auth-header>] [-p <project> | -g <group>] [-v]
Required:
-h GitLab host (e.g. gitlab.example.com)
-u Webhook endpoint URL to receive POSTs
-s Webhook secret token (used for signature verification)
Authentication (one of):
-t Access token (PAT, project, group or OAuth). If omitted, \$ GITLAB_TOKEN is used
-A Auth header to use. Default detects:
PAT → "PRIVATE-TOKEN"
anything else → "Authorization: Bearer"
Scope (choose one):
-p Project ID or full path (e.g. 42 or group/app)
-g Group ID or full path, recurse through all subgroups & projects
Options:
-v Verbose output (show individual project IDs in final summary)
EOF
exit 1
}
HOST = "" HOOK_URL = "" HOOK_SECRET = ""
TOKEN = "${ GITLAB_TOKEN :- }" AUTH_HEADER = ""
PROJECT = "" GROUP = "" VERBOSE = false
while getopts "h:u:s:t:A:p:g:v" opt ; do
case " $opt " in
h ) HOST = $OPTARG ;;
u ) HOOK_URL = $OPTARG ;;
s ) HOOK_SECRET = $OPTARG ;;
t ) TOKEN = $OPTARG ;;
A ) AUTH_HEADER = $OPTARG ;;
p ) PROJECT = $OPTARG ;;
g ) GROUP = $OPTARG ;;
v ) VERBOSE = true ;;
*) usage ;;
esac
done
# Mandatory checks
[[ -z $HOST || -z $HOOK_URL || -z $HOOK_SECRET ]] && usage
[[ -n $PROJECT && -n $GROUP ]] && usage
[[ -z $PROJECT && -z $GROUP ]] && usage
# Token handling
if [[ -z $TOKEN ]]; then
echo "[ERROR] No access token provided. Use -t or set \$ GITLAB_TOKEN" >&2
exit 1
fi
# Choose header if not forced
if [[ -z $AUTH_HEADER ]]; then
if [[ $TOKEN == glpat- * || $TOKEN == "PAT-" * ]]; then
AUTH_HEADER = "PRIVATE-TOKEN"
else
AUTH_HEADER = "Authorization: Bearer"
fi
fi
API = "https://${ HOST }/api/v4"
CURL_BASE = ( curl -sSf --header "${ AUTH_HEADER }: ${ TOKEN }" )
# Track processed projects to avoid duplicates
declare -A PROCESSED_PROJECTS
WEBHOOK_PROJECTS = ()
EXISTING_WEBHOOK_PROJECTS = ()
TOTAL_PROJECTS_FOUND = 0
PROJECTS_PROCESSED = 0
url_encode () {
local string = " $1 "
printf '%s' " $string " | sed 's/\//%2F/g; s/ /%20/g; s/@/%40/g; s/:/%3A/g; s/#/%23/g; s/?/%3F/g; s/&/%26/g; s/=/%3D/g; s/+/%2B/g'
}
fetch_paginated () {
local url = $1
local page = 1
local per_page = 100
while true ; do
local paginated_url
if [[ " $url " == * "?" * ]]; then
paginated_url = "${ url }&per_page=${ per_page }&page=${ page }"
else
paginated_url = "${ url }?per_page=${ per_page }&page=${ page }"
fi
local response
response = $( "${ CURL_BASE [ @ ]}" " $paginated_url " 2> /dev/null ) || {
echo "[ERROR] Failed to fetch page $page from $url " >&2
return 1
}
if [[ " $response " == "[]" || " $response " == "null" ]]; then
break
fi
local page_results
page_results = $( echo " $response " | jq -r '.[].id' 2> /dev/null ) || {
echo "[ERROR] Failed to parse JSON response from page $page " >&2
return 1
}
if [[ -z " $page_results " ]]; then
break
fi
local page_count
page_count = $( echo " $page_results " | wc -l )
TOTAL_PROJECTS_FOUND = $(( TOTAL_PROJECTS_FOUND + page_count ))
echo "[PROGRESS] Found $page_count projects on page $page (total: $TOTAL_PROJECTS_FOUND )" >&2
echo " $page_results "
local item_count
item_count = $( echo " $response " | jq '. | length' 2> /dev/null ) || 0
if [[ " $item_count " -lt " $per_page " ]]; then
break
fi
(( page ++ ))
done
}
create_hook () {
local pid = $1
if [[ -n "${ PROCESSED_PROJECTS [ $pid ] :- }" ]]; then
return 0
fi
PROCESSED_PROJECTS [ $pid ] = 1
PROJECTS_PROCESSED = $(( PROJECTS_PROCESSED + 1 ))
local encoded_pid
if [[ $pid =~ ^[0-9]+$ ]]; then
encoded_pid = $pid
else
encoded_pid = $( url_encode " $pid " )
fi
local existing_webhooks
existing_webhooks = $( "${ CURL_BASE [ @ ]}" "${ API }/projects/${ encoded_pid }/hooks" 2> /dev/null ) || {
echo "[ERROR] Failed to fetch existing webhooks for project $pid " >&2
return 1
}
if echo " $existing_webhooks " | jq -e --arg url " $HOOK_URL " '.[] | select(.url == $url)' > /dev/null 2>&1 ; then
[[ " $VERBOSE " == "true" ]] && echo "[INFO] Webhook already exists for project: $pid " >&2
EXISTING_WEBHOOK_PROJECTS += ( " $pid " )
return 0
fi
[[ " $VERBOSE " == "true" ]] && echo "[INFO] Adding webhook to project: $pid " >&2
"${ CURL_BASE [ @ ]}" --request POST \
--data-urlencode "url=${ HOOK_URL }" \
--data "token=${ HOOK_SECRET }" \
--data "push_events=true" \
--data "note_events=true" \
--data "issues_events=true" \
--data "merge_requests_events=true" \
--data "enable_ssl_verification=true" \
"${ API }/projects/${ encoded_pid }/hooks" \
> /dev/null
WEBHOOK_PROJECTS += ( " $pid " )
}
traverse_group () {
local gid = $1
local encoded_gid
if [[ $gid =~ ^[0-9]+$ ]]; then
encoded_gid = $gid
else
encoded_gid = $( url_encode " $gid " )
fi
while IFS = read -r pid ; do
[[ -n " $pid " ]] && create_hook " $pid "
done < <(
fetch_paginated "${ API }/groups/${ encoded_gid }/projects?include_subgroups=true"
)
while IFS = read -r sg ; do
[[ -n " $sg " ]] && traverse_group " $sg "
done < <(
fetch_paginated "${ API }/groups/${ encoded_gid }/subgroups"
)
}
echo "[INFO] Starting webhook processing..." >&2
if [[ -n $PROJECT ]]; then
echo "[INFO] Processing single project: $PROJECT " >&2
create_hook " $PROJECT "
else
echo "[INFO] Processing group and subgroups: $GROUP " >&2
traverse_group " $GROUP "
fi
echo "[INFO] Finished processing all projects" >&2
total_projects = $((${ # WEBHOOK_PROJECTS [ @ ]} + ${ # EXISTING_WEBHOOK_PROJECTS [ @ ]}))
if [[ $total_projects -eq 0 ]]; then
echo "[INFO] No projects were processed"
else
if [[ ${ # WEBHOOK_PROJECTS [ @ ]} -gt 0 ]]; then
if [[ " $VERBOSE " == "true" ]]; then
echo "[INFO] Webhooks installed successfully on ${ # WEBHOOK_PROJECTS [ @ ]} project(s):"
for pid in "${ WEBHOOK_PROJECTS [ @ ]}" ; do
echo " - Project ID: $pid "
done
else
echo "[INFO] Webhooks installed successfully on ${ # WEBHOOK_PROJECTS [ @ ]} project(s)"
fi
fi
if [[ ${ # EXISTING_WEBHOOK_PROJECTS [ @ ]} -gt 0 ]]; then
if [[ " $VERBOSE " == "true" ]]; then
echo "[INFO] Webhooks already existed on ${ # EXISTING_WEBHOOK_PROJECTS [ @ ]} project(s):"
for pid in "${ EXISTING_WEBHOOK_PROJECTS [ @ ]}" ; do
echo " - Project ID: $pid "
done
else
echo "[INFO] Webhooks already existed on ${ # EXISTING_WEBHOOK_PROJECTS [ @ ]} project(s)"
fi
fi
echo "[INFO] Total projects processed: $total_projects "
fi
# Make sure the script is executable:
chmod +x gitlab-webhook.sh
Example: Install webhook on a single project export GITLAB_TOKEN = "glpat-xxxxx"
./gitlab-webhook.sh \
-h "gitlab.example.com" \
-u "https://coderabbit.ai/gitlabHandler" \
-s "your-webhook-secret" \
-p 42
Example: Install webhooks on all projects in a group (including subgroups) export GITLAB_TOKEN = "glpat-xxxxx"
./gitlab-webhook.sh \
-h "gitlab.example.com" \
-u "https://coderabbit.ai/gitlabHandler" \
-s "your-webhook-secret" \
-g "mygroup/mysubgroup"