Before you start
- A self-managed or Dedicated GitLab™ instance (not GitLab.com)
- GitLab administrator access — system hooks are instance-wide, so registering one is an admin action
- Jira administrator access, to install the app and open its configuration screen
- Python 3.6+ to run the setup script. No third-party packages needed — deliberately, so you can read the whole script before running it
- Tested on GitLab 19.2.6. The features it relies on — system hooks carrying push and merge request events, the Secret token, and the push branch filter — are long-standing, and GitLab's own documentation names no minimum version for them. Older releases are untested rather than unsupported
What it does, and what it doesn't
It links three things into Jira's Development panel: commits, branches and merge requests. That is the whole scope, and the edges are worth knowing before you set it up.
- It does not import history. Nothing from before you set it up will appear. The app links what arrives through the hook, from the moment the hook exists — it has no access to your instance, which is the entire point of the design, so it cannot read backwards. To link an existing commit, push something that references the issue again; a new commit on the same branch is enough. An existing merge request links itself on its next update — a comment, a change of state, or a push to its source branch.
- Builds and deployments are never filled in. Jira's Development panel has sections for them. This app writes nothing there and they stay empty. Build and deployment information needs a separate integration; this app does not compete for those sections.
- Tag pushes are not ingested. Leave “Tag push events” unticked on the hook.
- The Jira project keys field is an allowlist, and it narrows. Leave it empty and every project is linked. Put PLAT in it and only PLAT-123 is linked — MOBL-4 is then ignored. So whenever you create a new Jira project, add its key here, or commits referencing it are skipped with no error anywhere. The skip is recorded on the configuration screen's ingest health, not in Jira.
- One installation covers one Jira site. One shared secret, one web trigger URL. A GitLab system hook is instance-wide, so that single hook covers every project on your instance — including projects created next year.
- Do not point two GitLab instances at one installation. GitLab numbers projects per instance, and this app uses that project number to identify the repository in Jira. Two instances therefore collide: project 5 on one and project 5 on the other become the same repository in Jira, their commits merge under one entry, and the name and URL are whichever instance sent the last event. Nothing warns you — both hooks carry the same secret and both are accepted. Use one installation per GitLab instance.
- One GitLab instance into two Jira sites is fine. Install the app on each site and you get two web trigger URLs, two secrets and two hooks, with nothing shared between them. Both sites will receive every event from the instance, so use each installation's Jira project keys field to decide which projects appear where.
Setup, in five steps
- Install the app from the Atlassian Marketplace.
- Open Jira Settings → Apps → GitLab (self-managed) configuration. Generate your shared secret, copy the web trigger URL, add your Jira project keys. Treat both the URL and the secret as credentials.
- Download the setup script — its full source is on that page, so you can read every line before you run it. Run it with --dry-run first to see exactly what it would change, without writing anything. Then run it for real. It asks for the web trigger URL, the shared secret, and a GitLab administrator token — read from a secure prompt, never a flag or an environment variable, and never written to disk or printed.
- The script makes exactly one write call to your instance and never deletes a hook.
- Push a commit whose message contains a Jira issue key. Open that issue in Jira — the commit appears in the Development panel.
Verifying it worked
Two places show the truth. In the app: the configuration screen shows ingest health. In GitLab: Admin Area → System Hooks → Edit → Recent Deliveries shows the HTTP status your instance actually received — 200 accepted, 401 means the shared secret doesn't match (regenerate it and re-run the script), 503 means no secret has been generated yet.
Note: once accepted, a commit can take anywhere from a few minutes to a few hours to appear in Jira's Development panel — that's Atlassian's own rendering delay, not a sign of a problem. GitLab's delivery record is the authoritative check, not an empty panel.
Rotating the secret
The setup script is safe to re-run any time. Regenerate the secret on the configuration screen and run the script again — the live hook is repaired automatically.
Troubleshooting
- 401 from the script: the admin token is invalid, expired, or missing the api scope.
- 403 from the script: the token isn't an administrator's — system hooks require admin rights.
- Certificate errors: pass your internal CA with --ca-bundle.
- Commits not appearing: check GitLab's Recent Deliveries first, then the app's ingest health, then allow for Atlassian's rendering delay.
- Nothing from before setup has appeared: and it never will — the app does not import history. See “What it does, and what it doesn't” above. Push a new commit referencing the issue, or wait for the merge request's next update.
- Recent Deliveries shows 200, but nothing reaches Jira: three causes, and none of them announces itself. First, the hook has the wrong triggers — “Repository update events” is on by default and is not one this app acts on, while “Push events” and “Merge request events” must both be ticked; a hook with the wrong triggers still answers Test with 200. Second, the branch filter is empty — ticking “Push events” reveals a Branch filter, which on GitLab 19.2 defaults to an empty wildcard pattern; set it to All branches. Third, the issue's project key is not in your list — see the allowlist note above. The setup script sets the first two correctly; these are the traps for a hook registered by hand.
- Builds or deployments are not showing: they never will. This app writes commits, branches and merge requests only.
- A commit mentioned an issue that didn't exist yet: nothing is lost — it's retried at least once a day and links within a day of the issue being created. Retrying continues for 14 days of licensed operation; after that it's shown as “expired, never linked” on the configuration screen.
- Licence lapsed: the configuration screen stays fully usable. New events aren't written to Jira until you're licensed again.