MergeMe logoMergeMe
Published on

Fix GitLab Slack Duplicate Notifications During Migration

Authors
  • Name
    Maruan

An engineer opens a merge request, and five seconds later your review channel dings twice. The first card shows the standard webhook format. The second arrives with interactive buttons from a newly authorized workspace bot. Within thirty minutes, people start muting the channel, missing real reviews, or asking whether GitLab had an outage.

GitLab Slack duplicate notifications during migration are almost a rite of passage for platform teams. Moving off the legacy integration is necessary, but flipping settings blindly creates channel noise that kills trust. When the feed fills with copies of the same event, engineers stop reading it. The fix is an orderly cutover, not a rushed toggle flip. Map the senders, separate distinct webhook payloads, pilot on an isolated repository, and cut over without breaking slash commands or CI build monitors.

Why duplicate MR alerts appear during a GitLab Slack migration

Duplicate alerts happen because two independent systems listen to the same GitLab hooks at once. Most teams run a legacy setup based on GitLab Slack notifications, which relies on simple incoming webhooks. When you adopt the newer GitLab for Slack app, you authenticate an OAuth application at the group or instance level. Unless someone disables the old service, both integrations process the same merge request event and post to the same channel.

Inheritance makes this worse. Integration inheritance is not universal across all settings, but where supported, projects and subgroups can inherit group defaults or instance-level configurations. If an admin enables the official Slack app at the root group while twenty repositories still have active legacy project-level integrations, every MR in those projects sends two messages. Managing group defaults and overrides means understanding this hierarchy before you touch production settings.

Third-party automation adds another layer. Many teams supplement notifications with custom CI pipeline jobs, review bot webhooks, or external orchestrators. When an MR updates, GitLab dispatches webhooks to external endpoints and alerts its native integrations at the same time. If nobody audited those secondary scripts, you end up with three or four identical pings per commit. Developers get tired of it fast.

Separate distinct events from the same event sent twice

Before touching any configuration, diagnose what is actually hitting your Slack channel. One merge request event posted by two services is a different problem from multiple distinct events generated by one user action. Confuse the two and you'll troubleshoot the wrong thing.

A single user click can legitimately emit three distinct events in under two seconds. Pushing a commit that resolves an existing discussion triggers a push event, a pipeline status update, and an MR state refresh. If your channel subscribes to every box, Slack shows three messages in rapid succession. That's high volume, but it isn't a duplicate. It's how notification noise in engineering channels builds up when teams subscribe to low-signal system events.

A true duplicate is the same action, such as opening an MR or adding a reviewer, producing identical alerts from separate sources. Look at the payload structure. If two cards announce that MR 412 was opened, both carry identical timestamps, and they differ in layout, bot avatar, or footer links, you're seeing dual delivery. Telling event chatter apart from dual delivery means you change the right settings instead of muting alerts your reviewers need.

Inventory every notification path before changing anything (local CSV checklist)

Don't migrate dozens of active repositories without an audit trail. Open a spreadsheet or local CSV file and document every notification path in your workspace. You need to cover three tiers: instance or root group integrations, project-level integrations, and custom repository webhooks.

Your CSV needs seven columns: Project Name, Integration Type, Target Channel, Active Events, Bot User Name, Configuration Level, and Decommission Target Date. Start in your top-level group settings and open Integrations. Record whether the GitLab for Slack app or the legacy Slack notifications service is active. Next, sample five active repositories. Check Project Settings > Integrations, and look at the Webhooks section in particular. Teams often forget that an engineer built a custom Python webhook two years ago to post MR approvals straight to a channel.

Pay attention to Slack channel permissions and access rights. Private repositories need both the legacy bot and the new app to have explicit access to private review channels. If the new app lacks channel permissions while the old webhook still has them, turning off the legacy integration cuts notifications entirely. The CSV gives you a snapshot of your setup, so you know exactly which switches to flip once the pilot starts.

Cutover flow: pilot project with the GitLab for Slack app (conceptual flow diagram)

Never flip a global Slack integration toggle for the whole company on a Tuesday morning. The safest way to prevent GitLab Slack duplicate notifications during migration is a controlled cutover on one pilot repository. Pick an active internal project where the team can tolerate a short testing window.

Picture the cutover as four states. State A is your legacy baseline: Legacy Slack Notifications is active and posting to the channel, while the GitLab for Slack app is disabled. State B is the pilot window: you enable the GitLab for Slack app on the pilot repository only and route its notifications to a temporary channel, such as #pilot-mr-notifications. During State B, you check payload fidelity, review button behavior, and user mentions without disturbing the main team feed.

State C is the cutover for that project: you re-route the new app to the real development channel and immediately disable the legacy Slack notification service in that project. State D is the post-cutover baseline: only the new app delivers messages, legacy webhooks are gone, and slash commands still work. If you'd rather skip this channel churn entirely, MergeMe takes a different approach. It focuses on consolidating code review updates to keep team channels organized. Whichever route you take, testing in a contained pilot first lets you refine the State B to State C transition before rolling it out across the codebase.

When a duplicate slips into your review channels during cutover, you need to find its source in seconds. Slack leaves forensic clues on every posted card if you know where to look. Don't guess which service sent it. Inspect the bot profile, footer metadata, and timestamps.

First, check the sender name and avatar in Slack. The legacy integration usually posts under a generic webhook bot name, often incoming-webhook or a custom bot identity created years ago. The official GitLab for Slack app uses the application name set during your workspace OAuth installation. Click the profile name on each card. The native app opens an application profile pane with integration details. An incoming webhook shows only an automated app configuration panel, with no interactive slash command bindings.

Second, check the timestamp and MR link format. Right-click the timestamp on both messages and copy the link. Paste them into a text editor and compare the trailing link syntax and any hidden markdown payloads. The two integrations format their alerts differently, making it easier to distinguish between them. If both messages arrive with identical second-level timestamps but different formatting, a project webhook and an inherited group integration are firing together. Once you know the sender, you know which configuration screen to open to silence the duplicate.

Event checklist and disabling only the duplicated path (keep slash commands and CI alerts)

The most common mistake during migration is deleting an integration outright when only one event was duplicated. Integrations bundle several capabilities. You might rely on the legacy integration for Slack slash commands or deploy notifications while using the new app only for merge request review routing.

Open the legacy integration settings under Project > Settings > Integrations > Slack Notifications and scroll to the triggers section. Uncheck Merge request events and Confidential merge request events. Leave Deployment events, Pipeline events, or Issue events checked if your team still uses the legacy formatting for those pipelines. Then save. That resolves the duplicate MR alert right away and leaves the build monitors your ops engineers depend on alone.

On self-managed instances, admins also need to check whether slash commands run through the standalone Slack slash commands service. If engineers type /gitlab issue new or check pipeline status from inside Slack, removing the whole service record breaks those habits. Keep slash commands working and disable only the dual-delivery MR hooks. Limiting the change to merge request triggers protects team habits and kills the double ping on code reviews.

Rollback plan that preserves configuration, then expand project by project

Every production migration runbook needs an instant rollback. If the new Slack app formats notifications badly, drops reviewer assignments, or hits authentication timeouts, you should be able to restore the legacy alert pipeline within thirty seconds. Keep the legacy credentials in place and toggle only the active state.

Don't erase the Webhook URL from the legacy integration screen. To disable the legacy service, uncheck the Active checkbox at the top of the page and click Save. If the new app misbehaves, go back to that page, check the Active box, save, and your original alerts resume. Nobody has to scramble for an archived Slack incoming webhook URL in the middle of an incident.

Once the pilot has run for forty-eight hours with no missing notifications and no duplicates, expand the cutover. Group the remaining repositories into batches of five to ten projects and migrate one batch per day, using the same procedure: verify channel access, activate the new app, disable the legacy MR trigger, and confirm the sender identity in Slack. Going repository by repository avoids a workspace-wide alert outage and keeps your development channels clean.

Conclusion

Migrating your GitLab notification pipeline should speed up your engineers, not bury your review channels in duplicates. Separate dual-delivery bugs from multi-event triggers, catalog every webhook in a CSV audit, and roll out through an isolated pilot project. Your developers will keep reading the channel.

If you want to move past static webhook alerts entirely, look at MergeMe. It provides Slack notifications for GitHub pull requests and GitLab merge requests as one updating card per PR or MR. Instead of posting a new message for every review update, MergeMe edits the original message in place, so channels stay readable. Connect your GitHub.com, GitLab.com, or self-hosted GitLab instance to a single Slack workspace, start on the free Hobby plan, or deploy with full control through self-hosted infrastructure.

FAQs

Why did my Slack channel start getting two messages for every GitLab merge request?

This dual delivery occurs when both the legacy GitLab Slack notifications integration and the newer GitLab for Slack app are active simultaneously. Because both integrations listen to the same MR system hook, each service dispatches its own message to your channel. Disabling the merge request trigger in the legacy integration stops the duplication immediately.

How can I tell whether a duplicate message came from a webhook or the official Slack app?

Click the bot username on the message in Slack. The official GitLab for Slack app opens an application details pane outlining features such as notifications, slash commands, and GitLab Duo. A legacy webhook displays a simple bot configuration profile with an incoming-webhook tag. You can also examine the message style: while incoming webhooks can support Block Kit as well, older configurations often rely on legacy Slack attachment cards.

Can I keep GitLab slash commands active while migrating notifications?

Yes. Slack slash commands and automated event notifications can be managed independently. When editing the legacy integration settings in GitLab, uncheck the Merge request events trigger without disabling the slash command integration or wiping your service credentials. This silences duplicate MR alerts while keeping your engineers slash commands fully operational.

Does MergeMe support self-hosted GitLab instances alongside Slack?

Yes. MergeMe works with GitHub.com, GitLab.com, and self-hosted GitLab, posting into Slack all connected in one workspace. For organizations with strict data residency or SSO mandates, MergeMe also provides a self-hosted option that teams can run on their own infrastructure by enquiry.