doc/administration/server_hooks.md
{{< details >}}
{{< /details >}}
{{< history >}}
{{< /history >}}
Git server hooks run custom logic on the GitLab server. You can use them to run Git-related tasks such as:
Git server hooks use pre-receive, post-receive, and update Git server-side hooks.
GitLab administrators configure server hooks using the gitaly command, which also:
If you don't have access to the gitaly command, alternatives to server hooks include:
For GitLab Helm chart instances, see information about global server hooks in the Gitaly chart.
[!note] Geo doesn't replicate server hooks to secondary nodes.
/var/opt/gitlab/gitaly/config.toml on Linux package instances), and the
repository relative path for the repository.To set server hooks for a repository:
Create tarball containing custom hooks:
Write the code to make the server hook function as expected. Git server hooks can be in any programming language.
Ensure the shebang at the top reflects the language type. For example, if the script is in Ruby the shebang is
probably #!/usr/bin/env ruby.
pre-receive server hook, the filename should be pre-receive with no extension.pre-receive server hook, the directory name should be pre-receive.d. Put the files for the hook in that
directory.Ensure the server hook files are executable and do not match the backup file pattern (*~). The server hooks be
in a custom_hooks directory that is at the root of the tarball.
Create the custom hooks archive with the tar command. For example, tar -cf custom_hooks.tar custom_hooks.
Run the hooks set subcommand with required options to set the Git hooks for the repository. For example:
cat custom_hooks.tar | sudo -u git -- /opt/gitlab/embedded/bin/gitaly hooks set --storage <storage> --repository <relative path> --config <config path>
A path to a valid Gitaly configuration for the node is required to connect to the node and provided to the --config flag.
Custom hooks tarball must be passed through stdin. For example:
cat custom_hooks.tar | sudo -u git -- /opt/gitlab/embedded/bin/gitaly hooks set --storage <storage> --repository <relative path> --config <config path>
If you are using Gitaly Cluster (Praefect), you must run hooks set subcommand on all Gitaly nodes.
If you implemented the server hook code correctly, it should execute when the Git hook is next triggered.
If you use Gitaly Cluster (Praefect), an individual repository may be replicated to multiple Gitaly storages in Praefect. Consequently, the hook scripts must be copied to every Gitaly node that has a replica of the repository. To accomplish this, follow the same steps for setting custom repository hooks for the applicable version and repeat for each storage.
The location to copy the scripts to depends on where repositories are stored. New repositories are created using
Praefect-generated replica paths which are not the hashed storage path. To identify the replica path,
query the Praefect repository metadata by using
-relative-path option to specify the expected GitLab hashed storage path.
To create a Git hook that applies to all repositories, set a global server hook. Global server hooks also apply to:
<id>.wiki.git.<id>.design.git.Before creating a global server hook, you must choose a directory for it.
{{< tabs >}}
{{< tab title="Linux package (Omnibus)" >}}
The directory is set in gitlab.rb under gitaly['configuration'][:hooks][:custom_hooks_dir]. You can either:
/var/opt/gitlab/gitaly/custom_hooks directory by uncommenting it.{{< /tab >}}
{{< tab title="Self-compiled (source)" >}}
gitaly/config.toml under the [hooks] section. However, GitLab honors the
custom_hooks_dir value in gitlab-shell/config.yml if the value in gitaly/config.toml is blank or non-existent./home/git/gitlab-shell/hooks.{{< /tab >}}
{{< /tabs >}}
To create a global server hook for all repositories:
pre-receive server hook, the directory name should be pre-receive.d.#!) at the top reflects the language type. For example, if the script is in Ruby the shebang is probably #!/usr/bin/env ruby.*~).If the server hook code is properly implemented, it should execute when the Git hook is next triggered. Hooks are executed in alphabetical order by filename in the hook type subdirectories.
To remove server hooks, pass an empty tarball to hook set to indicate that the repository should contain no hooks. For example:
cat empty_hooks.tar | sudo -u git -- /opt/gitlab/embedded/bin/gitaly hooks set --storage <storage> --repository <relative path> --config <config path>
GitLab can execute server hooks in a chain. GitLab searches for and executes server hooks in the following order:
<project>.git/custom_hooks/<hook_name>: Per-project hooks. This location is kept for backwards compatibility.<project>.git/custom_hooks/<hook_name>.d/*: Location for per-project hooks.<custom_hooks_dir>/<hook_name>.d/*: Location for all executable global hook files except editor backup files.In a server hooks directory, hooks:
You can pass any environment variable to server hooks, but you should only rely on supported environment variables.
The following GitLab environment variables are supported for all server hooks:
| Environment variable | Description |
|---|---|
GL_ID | GitLab identifier of user or SSH key that initiated the push. For example, user-2234 or key-4. |
GL_PROJECT_PATH | GitLab project path. |
GL_PROTOCOL | Protocol used for this change. One of: http (Git push using HTTP), ssh (Git push using SSH), or web (all other actions). |
GL_REPOSITORY | GitLab project ID with a prefix of project-. For example, project-1234 |
GL_USERNAME | GitLab username of the user that initiated the push. |
The following Git environment variables are supported for pre-receive and post-receive server hooks:
| Environment variable | Description |
|---|---|
GIT_ALTERNATE_OBJECT_DIRECTORIES | Alternate object directories in the quarantine environment. |
GIT_OBJECT_DIRECTORY | GitLab project path in the quarantine environment. |
GIT_PUSH_OPTION_COUNT | Number of push options. |
GIT_PUSH_OPTION_<i> | Value of a specific push option where <i> is from 0 to one less than the value defined in GIT_PUSH_OPTION_COUNT. |
When server hooks reject a push, provide clear error messages to help users understand why the push was rejected and how to fix the issue. Custom error messages appear in the GitLab UI and in the user's terminal when a hook declines a push.
Without custom error messages, users only see generic messages like (pre-receive hook declined).
Clear error messages help users:
To display a custom error message, your script must:
stdout or stderr.GL-HOOK-ERR: with no characters appearing before the prefix.For example:
# Bad: Generic message
echo "GL-HOOK-ERR: Commit rejected.";
# Good: Specific message with action
echo "GL-HOOK-ERR: Commit rejected: Commit message must include an issue reference (for example, #1234).";
When working with Git server hooks, you might encounter the following issues.
pre-receive hook declinedWhen a user pushes to a GitLab repository, they might receive an error message that includes
(pre-receive hook declined). For example:
! [remote rejected] main (pre-receive hook declined)
error: failed to push some refs to 'https://gitlab.example.com/group/project'
This error indicates that a pre-receive hook rejected the push. Pre-receive hooks run before any references are updated in the repository. Git provides three server-side hooks that can reject pushes:
pre-receive: Runs before any references are updated. Can reject the entire push.update: Runs once per branch being updated. Can reject individual branches.post-receive: Runs after all references are updated. Cannot reject pushes, but can cause
errors if the hook fails.The (pre-receive hook declined) error usually comes from either the pre-receive or update
hook. To identify the issue:
Check the output immediately before the (pre-receive hook declined) message. The output often
contains information about why the push was rejected. For example:
remote: GitLab: The default branch of a project cannot be deleted.
! [remote rejected] main (pre-receive hook declined)
Check the Gitaly logs for more details about why the hook failed:
sudo grep PreReceiveHook /var/log/gitlab/gitaly/current | jq .
If the repository has custom server hooks configured, review the custom hook code for issues.
The following are common causes of pre-receive hook failures:
git push --mirror when the source repository has a
different default branch than the target repository.To help users understand hook failures use custom error messages to provide clear feedback about why a push was rejected. Custom error messages appear in the GitLab UI and in the user's terminal.