GitLab and Redmine issue tracker integration

Redmine can stay your issue tracker while self-managed GitLab hosts the code, reviews and pipelines. This page covers how to link them, what to configure, and when moving issues into GitLab makes more sense.

On this page

Keeping Redmine as the issue tracker for GitLab projects

Teams with an established Redmine instance can keep it as their issue tracker while source code, merge requests and pipelines move to self-managed GitLab. GitLab can reference issues that live in an external tracker, so a commit message or merge request description that cites a Redmine issue is linked to that issue. The supported path is GitLab's external issue tracker setting, pointed at your Redmine instance. GitLab does not manage Redmine issues natively: it cannot create, edit, assign or close them from its own interface, and Redmine's workflows, roles and custom fields stay in Redmine. The link is only as consistent as the issue references your developers write, so agree on a reference format before the first merge request is opened.

The integration rests on Redmine's REST API and on stable issue URLs. An administrator must enable the REST API in Redmine before anything else works. Redmine then accepts either HTTP basic authentication or a per-user API key, sent as a parameter or in the X-Redmine-API-Key header. Plan for a dedicated service account with read access to the projects GitLab will reference, rather than tying the integration to a person who may leave. Check that the account's project visibility matches the audience of each GitLab project, because a link can expose only what the account can see. Core resources such as issues, projects, users and time entries are marked stable in Redmine's documentation, but confirm with GitLab's current documentation which fields and API calls its external tracker setting requires in your GitLab version. You can find Redmine's API reference in the Redmine REST API documentation.

Two consequences shape the decision. First, Redmine upgrades can change the environment GitLab depends on, so confirm your Redmine line before scheduling changes. Redmine 7.0.x is the fully supported line, 6.1.x receives only bug and security fixes, and 6.0.x receives only important security updates. Second, moving issues into GitLab is a separate migration project, not a setting change. Redmine's built-in importers for Trac and Mantis are obsolete and not working, and the third-party scripts for systems such as Jira are neither tested nor supported. If you expect to consolidate on GitLab later, decide that early, because each open Redmine issue reference in historic commits will need a plan for how it resolves after the move.

What you need before connecting GitLab to Redmine

Connecting GitLab to Redmine depends on three systems agreeing: a Redmine release line you can keep patched, a REST API that a dedicated account can reach, and a GitLab version that your team can upgrade through its required stops. The cards below cover the decisions and checks to make before you configure anything, in the order most teams hit them.

Choose the Redmine release line

Redmine 7.0.x is the fully supported line and receives new features, bug fixes and security updates. Redmine 6.1.x receives only bug fixes and security updates, and 6.0.x receives only important security fixes. Redmine 5.1.x and older are unsupported, so a new deployment should target 7.0.x unless a specific plugin or constraint holds you back. Confirm the plugin compatibility you depend on before you pick the line.

Confirm Ruby and Rails support

Redmine 7.0 runs on Rails 8.1 and supports Ruby 3.2, 3.3, 3.4 and 4.0. It does not support JRuby. Check your operating system packages against these Ruby versions before you build the host, because a mismatched Ruby is a common reason an installation stalls at the first run of the application.

Pick a production database

Redmine supports PostgreSQL, MySQL 8.0 to 8.4 with transaction isolation set to READ COMMITTED, SQL Server 2012 or higher, and SQLite. The installation guide says SQLite is not suited to multi-user production use, so use PostgreSQL or a supported MySQL or SQL Server setup for any instance that GitLab writes to. Plan database backups from the start, since the upgrade procedure depends on them.

Enable the REST API

The Redmine REST API is off by default and must be enabled by an administrator under the authentication settings. It supports JSON and XML, and core resources such as issues, projects, users and time entries are marked stable. Confirm the exact administration menu path for your Redmine release, since the label can change between lines.

Create a dedicated Redmine service user

Create a Redmine user that exists only for GitLab, give it the minimum project roles needed to read and update issues, and generate its API key. Redmine accepts the key as a parameter or in the X-Redmine-API-Key header. Keep the key in a GitLab-managed secret, not in project code, and record who owns rotation, because the key grants whatever the account can see.

Check the GitLab version and upgrade stops

Self-managed GitLab must pass through required upgrade stops, which since GitLab 17.5 fall at versions x.2, x.5, x.8 and x.11. Confirm the exact GitLab version you run, the integration settings path and any version-specific behavior in the GitLab documentation for your target release before you change integration settings, and schedule the integration work after any pending upgrade stop.

Verify network path and certificate trust

GitLab must reach the Redmine base URL over the network and trust its TLS certificate, including any internal certificate authority. Test the connection from the GitLab application and Sidekiq hosts, not only from your workstation, because firewall rules and private DNS often differ between them. Confirm that Redmine answers API requests over HTTPS before you enter any credentials in GitLab.

How to configure the Redmine external tracker in GitLab

This sequence connects a self-managed GitLab instance to an existing Redmine installation so that issue numbers in commits and merge requests link back to Redmine issues. It finishes with a tested reference from both a commit and a merge request.

  1. Enable the Redmine REST API

    A Redmine administrator turns on the REST API in the administration settings, which is off by default. The administrator confirms the setting is saved and that the API is reachable from the GitLab server's network. Verify the setting by requesting a JSON issue list from the Redmine URL with a test credential, which should return data rather than an authentication error. Confirm the exact setting name and location against the Redmine REST API documentation for your version, since the wiki describes the required activation.

  2. Create a dedicated Redmine service account

    Create a separate Redmine user for GitLab, not a personal account, so that link activity and access can be audited and revoked independently. Generate its API key from the account page and store the key in a secrets manager or a protected location, because it will be entered into GitLab. Verify the account by calling the current-user endpoint with its API key, which should return that service account's profile.

  3. Set role permissions for issue viewing

    In Redmine, assign the service account a role on each project GitLab should link to, with permission to view issues and any other project the team wants referenced. Limit the role to read access, since GitLab only needs to display issue titles and state. Verify by making an API request for a specific issue in each project with the service account's key, and confirm that the issue returns. Projects without access will still produce links in GitLab, but the linked issues will not show their details to users.

  4. Configure the external issue tracker in GitLab

    A GitLab administrator opens the Redmine integration settings at instance level, or a group or project owner opens them at a lower level if only some projects use Redmine. Enter the Redmine project URL, the issue URL pattern with the issue number placeholder, and the description and prefix values your teams will recognize. Use the exact field names shown in the GitLab integration documentation for your version. Verify the save by opening a project's issue settings and checking that the external tracker is shown as active.

  5. Decide on the GitLab internal issue tracker

    Choose whether GitLab's built-in issues stay enabled alongside Redmine or are turned off for projects that use Redmine as the single tracker. Keeping both enabled can split work across two systems, so the decision should be recorded with the project owners before testing. Turning off the internal tracker only affects the project's issue list in GitLab and does not delete existing issues. Verify the choice by checking the project's navigation menu and confirming that the issue entry matches the decision.

  6. Test a commit message reference

    Push a commit to a test branch whose message includes a Redmine issue number in the format your pattern expects. Open the commit in GitLab and confirm that the issue reference is rendered as a link to the correct Redmine issue. If the link does not appear or points to the wrong address, check the URL pattern and the project URL in the integration settings. Keep the test project separate from production so that the reference can be corrected without affecting real issues.

  7. Test a merge request description reference

    Open a merge request in the same test project with a description that references a Redmine issue number, then confirm the link renders on the merge request page. Check that a user without Redmine access sees the link text but not the issue details, which confirms the permission model is working as intended. If the integration fails, the admin should receive a clear error on the settings page or in the test result, which points to the field to correct. Record the outcome and the test issue numbers so the team can repeat the check after upgrades.

Which GitLab objects link to which Redmine fields

This table maps each GitLab object, such as commit messages, merge requests and branch names, to the Redmine identifiers it can reference. Read the right-hand column as what a reader can see across the two tools, not as a status or assignee sync. Confirm each row against the GitLab and Redmine releases you deploy before you write team workflow rules around it.

GitLab objectRedmine field it can referenceWhat does not carry overWhat to confirm before go-live
Commit messageIssue ID written as #number, matched against the external tracker patternIssue status, assignee and priority are not changed by the commitPattern syntax and project prefix for your GitLab release
Merge request descriptionIssue ID reference, listed in the issue's linked activityMerge state does not move the Redmine status or close the issueWhether references in descriptions appear in Redmine as expected
Closing keyword in commit or merge requestIssue ID following a closing keyword such as ClosesRedmine does not close the issue when the merge completes unless a separate automation doesWhether your release closes Redmine issues on merge, and which tool owns the status change
Branch nameIssue ID embedded in the branch name, used for reference onlyBranch creation or deletion does not update Redmine issue fieldsWhether your team naming convention is matched by your tracker pattern
Project settings: external issue trackerRedmine base URL and project identifier, set per GitLab project or groupRedmine projects, trackers, versions and custom fields are not created in GitLabExact setting names and inheritance from group or instance defaults
Redmine project identifierUsed in the issue URL that GitLab builds for each referenceRenaming the Redmine project identifier can break existing linksWhether your Redmine identifier scheme is stable before linking starts
Users and assigneesRedmine user accounts are matched only by identity in Redmine, not by GitLab assigneesGitLab assignees, labels and milestones do not write into Redmine assignees or versionsHow your identity provider maps users across both systems
Issue statuses and workflowsRedmine workflow transitions remain under Redmine rules onlyMerge events do not trigger Redmine status transitions unless a separate automation existsWho moves issues to resolved, and whether that step is manual or automated

Securing the Redmine service account and the link

The GitLab to Redmine link is a trust relationship: GitLab holds a credential that can read and write Redmine data, and Redmine holds the titles and descriptions of work that may be sensitive. The cards below cover the account, the key, the network path and the visibility rules that keep that relationship narrow. Review them together before the integration goes live, because each control limits the damage from the others failing.

Give the integration user a narrow role

Create a dedicated Redmine user for GitLab rather than reusing a person's account or an administrator. Grant a custom role that can view and comment on issues in the linked projects, and nothing more. Avoid project management, user administration and global settings permissions. Ask whether your integration needs to change issue status, since a write permission widens what a leaked key can do. Review the role whenever a new project is linked.

Store the API key in a secrets manager

Redmine's REST API authenticates a user with a per-user API key, so that key is the credential GitLab presents on every call. Keep it out of plain project variables, where maintainers and some pipeline jobs can read it. Store it in an external secrets manager your team already operates, and pull it at runtime with access limited to the instance or group that needs it. Record who owns the key, where it lives and when it was last rotated.

Rotate the key on a fixed schedule

Plan rotation as a routine task, not an incident response. Generate a new key for the integration user, update the secrets manager entry, confirm that GitLab references and new issue links still resolve, then revoke the old key. Decide the interval with your security team and write it into the runbook. Also rotate immediately when someone with access leaves the team or when the key appears in a log, a ticket or a pipeline output.

Limit which projects the account reads

The integration user should see only the Redmine projects that map to GitLab projects. Redmine's role-based access control applies per project, so add the account to those projects only and leave other projects closed to it. Confirm this with a test: call the API as the integration user for a project it should not reach and check that the request is refused. Repeat the check after any project is created or made public.

Encrypt every hop with TLS

Serve Redmine only over HTTPS with a certificate chain your GitLab runners and instance trust, and make GitLab call the HTTPS address rather than an HTTP one. Check the Redmine base URL in the integration settings and confirm that redirects do not drop the API key. Keep the certificate authority trusted by GitLab's outbound connections current, and test renewal before it expires. Plain HTTP would expose the key and issue content to anyone on the path.

Enable the REST API only where needed

Redmine's REST API must be switched on by an administrator, and it is off by default for good reason. Enable it only on the Redmine instance that GitLab calls, and document that decision with the owner of the instance. If other tools use the API, review each one, because enabling the setting exposes the API to every account that holds a key. Revisit the setting when the integration is retired so the surface does not persist unnoticed.

Keep private titles off public GitLab pages

Issue references in commits and merge requests can show a Redmine issue title in GitLab. If a GitLab project is public, or visible to people outside your organization, a private Redmine title may be readable there. Before enabling the link, decide which Redmine projects may appear in public GitLab views, and check how titles render for each GitLab visibility level. Where a title is sensitive, ask for references to show only the issue number.

Roll out controls in stages

GitLab's hardening guidance covers application settings, CI/CD, configuration files and the operating system, and it notes that the guidance is a work in progress to be rolled out gradually. Apply the Redmine link controls the same way: test the narrow role and TLS path in a staging project first, then extend to production projects one group at a time. Record which settings you changed and why, so the change history can be reviewed in an audit.

Why Redmine references do not link and how to fix them

This table lists the failures most often seen when GitLab references to Redmine issues fail to link or fail to resolve, with the likely cause and the fix for each. Work down the rows in order, because a pattern or identifier mismatch hides every later problem.

SymptomLikely causeFix
No link rendered in a commit or merge requestThe issue tracker pattern or project URL in GitLab does not match the reference text, so GitLab never recognizes the reference.Compare the reference format in commit messages with the configured pattern and the Redmine URL. Test with one known issue before changing many projects.
No link rendered for a project-scoped referenceThe Redmine project identifier used in the reference does not match the identifier configured for the project in Redmine.Confirm the exact project identifier in Redmine's project settings. Correct either the reference format or the configured identifier so they agree.
403 or 401 errors when GitLab queries RedmineThe REST API is disabled, or the API key or basic credentials belong to a user without permission to view the issue.Ask a Redmine administrator to enable the REST API. Use a dedicated service account with a valid API key and confirm it can open the issue in a browser.
Redmine project not visible to the service accountThe service account is not a member of the project, or its role lacks the permission to view issues in that project.Add the service account to each linked project with a role that can view issues. Check the project's visibility and module settings in Redmine.
TLS certificate errors from RedmineRedmine uses a certificate issued by a private certificate authority that GitLab's host does not trust.Install the private CA certificate on the GitLab host and its runners where required. Confirm the trust chain with a request from that host before retrying the integration.
Issue IDs point to the wrong issueRedmine issue numbers are unique within one Redmine instance only, and references without a project identifier can resolve to a different project than intended.Use project-qualified references where the pattern supports them. Document the reference convention for teams that work across several Redmine projects.
Links break after a Redmine upgrade or URL changeThe configured base URL, host name or path changed, or the new version changed the link format, so stored URLs no longer resolve.Update the Redmine URL in the GitLab integration settings after any move. Recheck a sample of links after each upgrade, following the upgrade steps for backups and plugin migration.

When moving issues into GitLab is the better choice

Moving issues into GitLab is the better choice when the work is mostly engineering work that already lives in merge requests and pipelines, and when your team would rather not run a second system to patch and back up. GitLab-native issues sit next to code review, pipelines and releases, so a developer sees the issue, the branch, the test results and the deployment in one place. The trade-off is that Redmine offers things GitLab issues do not replace in the same way: its flexible role-based access control, custom workflows, Gantt charts, calendars and wiki. If planners or non-engineering stakeholders rely on those, keeping Redmine is usually the lower-risk option, with the integration covering the link between the two systems.

Be aware that moving is not a simple import. The GitLab import documentation lists Jira and FogBugz as issue sources, but Redmine is not listed as a direct import source. Redmine's own migration wiki describes its built-in importers for Trac and Mantis as obsolete and not working, and says the third-party migration scripts it lists are neither tested nor supported. A move therefore needs a planned export from Redmine, a written field mapping for statuses, trackers, custom fields, assignees and attachments, and a test run on a copy of the data. Confirm with your team which Redmine fields must survive the move and whether closed history needs to be kept at all.

Decide before any build work starts. Ask who relies on Redmine workflows today, how many active projects link to it from commits, and whether existing issue references in code would break if the issue numbers change after a move. Plan a cutover window where new work goes to one tracker only, and keep Redmine read-only for an agreed period so that references and audit trails remain available. If the answer is that Redmine stays, the work moves to making the link reliable and keeping Redmine's upgrade and plugin maintenance on a schedule.

Questions teams ask before connecting GitLab and Redmine

Does connecting GitLab to Redmine cost anything in licenses?

Redmine itself has no license fee. It is free software under the GNU General Public License v2, and it has no paid edition. The cost of the integration comes from hosting Redmine, the implementation work, any plugins you choose, upgrades and the people who maintain both systems. GitLab is priced separately: GitLab Free costs 0 US dollars, Premium costs 29 US dollars per user per month billed annually, and Ultimate is custom priced, with all three tiers available for self-managed installations. Your GitLab tier decision does not depend on Redmine, but ask any quote to separate one-time implementation fees from recurring hosting, plugin and support costs.

How long does a GitLab and Redmine link take to build?

The duration depends on the scope you agree in writing before work starts. The main drivers are the number of GitLab projects that need the link, how many Redmine projects and trackers map to them, whether you use custom fields or workflows that the link must respect, and the identity and network constraints around both systems. For example, a single team with one Redmine project and a reachable Redmine server is a much smaller job than dozens of projects spread across several Redmine instances behind separate access controls. Ask for the timeline to be tied to named deliverables.

What access and people do we need to provide?

You need an administrator on the Redmine side who can enable the REST API and create a dedicated integration user with access to the relevant projects. You also need a GitLab administrator who can configure instance or group integration settings and approve changes to project settings. Name one owner on your side who makes decisions about tracker mapping, reference formats and who may close issues from merge requests. Test environments for both systems are useful before changing production projects.

Can our own team connect GitLab to Redmine without a partner?

Yes, if you have people comfortable with both platforms. The GitLab side is largely configuration in the project or group integration settings, and the Redmine side needs the REST API enabled and a suitable user. The harder parts are agreeing the reference conventions, handling access and secrets correctly, and testing edge cases such as renamed projects, closed issues and restricted visibility. Teams that handle this internally usually document the decisions and test them against a non-production copy first. A partner is most useful when the scope spans several systems or regulated controls.

Do issue status and assignees sync between GitLab and Redmine?

Do not assume they do. The external issue tracker link in GitLab mainly lets you reference Redmine issues from commits and merge requests and link to them from GitLab, so references are the primary behavior. Synchronizing status, assignees or fields in both directions requires additional tooling, such as custom automation through webhooks or the Redmine REST API, and that work must be designed and tested. Confirm the exact behavior of the GitLab integration version you run before you promise any status update to your teams, and write down which system is the source of truth for each field.

How do upgrades of GitLab or Redmine affect the link?

Each system is upgraded on its own schedule, so both need a check after every change. GitLab ships a major release yearly, a minor release on the third Thursday of each month and patch releases twice a month, and it requires upgrade stops at versions such as 18.2, 18.5, 18.8 and 18.11. Redmine 7.0.x is the fully supported line, while 6.1.x receives only bug fixes and security updates. Its upgrade procedure backs up the database and uploaded files, copies configuration and plugins to the new installation, then runs database and plugin migrations. Plan a test of the references and any custom automation after each upgrade, and confirm plugin compatibility before you move Redmine to a new line.

When should we move issues into GitLab instead of keeping Redmine?

Keep Redmine when its workflows, roles, plugins or wiki are already central to how your teams plan work, or when many non-engineering stakeholders depend on it. Consider moving issues into GitLab when most work is engineering-led, when you want issues, merge requests and pipelines in one place, or when maintaining the link costs more than the value it provides. GitLab can import Jira issues and some other sources, but Redmine's built-in importers for Trac and Mantis are marked obsolete and not working, and third-party scripts for other systems are neither tested nor supported. Treat any Redmine export as a separate migration project with a sample import and a reconciliation step.

Keeping the link working through Redmine and GitLab upgrades

Redmine upgrades change the system that answers issue lookups, so the link can break even when GitLab stays untouched. The Redmine upgrade guide describes a sequence that protects both the database and the uploaded files before any code changes. Plan the window so that the new Redmine instance is reachable at the same address GitLab already uses, or update the external tracker setting in GitLab to match.

  • Back up first: Take a database dump and a copy of the files directory before touching the installation.
  • Carry configuration forward: Copy configuration.yml, the files directory and third-party plugins to the new installation.
  • Run migrations: Run rake db:migrate and rake redmine:plugins:migrate in the Redmine root, then clear the cache and restart the application.
  • Check the REST API: Confirm that the API is still enabled and that the API key or basic authentication account GitLab uses still works.
  • Retest references: Open a commit or merge request that cites a Redmine issue number and confirm the link resolves to the right issue.

Before you close the change, confirm that the Redmine version you install is one your team has agreed to support, and check the current support statements on the download page.

How DeployOpen scopes and delivers the Redmine link

DeployOpen plans, builds, documents and supports the link between self-managed GitLab and a self-hosted Redmine instance inside your own infrastructure. Engagements are fixed-scope, so the written scope fixes the projects, reference rules, upgrade procedure and handover before any configuration starts.

How we scope

  • Inventory which GitLab projects and groups will reference Redmine issues and which keep issues inside GitLab.
  • Confirm the Redmine version, database, authentication source and whether the REST API will be enabled for the integration.
  • Agree in writing the reference format, the Redmine roles used by the integration and the test and handover deliverables.

How we build

  • Configure the external issue tracker in a test GitLab project first, then roll the setting out to groups and projects in agreed stages.
  • Test commit and merge request references, closing keywords and link display against Redmine issues before production use.
  • Record each configuration decision, including which settings are set at instance, group or project level, in the delivered documentation.

How we hand over

  • Deliver a runbook covering configuration, expected behavior, known limits and the steps for adding a new project.
  • Walk your administrators through checking links, permissions and the issue tracker setting on existing projects.
  • Document the Redmine upgrade procedure, including the database backup and plugin migration steps, alongside the GitLab settings.

How we support

  • Ongoing support, upgrades and continuous testing are available as a retainer after handover.
  • A retainer can cover re-testing the link after GitLab minor releases and Redmine stable releases you choose to adopt.
  • Ask any provider to confirm response terms, testing scope and who owns the configuration after the engagement ends.

Sources and further reading

Talk to our team.

Tell us what you're working on, whether it's a deployment, an audit, a security test or a cyber range. You'll speak with an engineer who can help you scope it.

  • 30-minute call: free, with no obligation.
  • NDA on request: we can sign before you share details.
  • Clear next steps: a scope and plan after the call.