Back to Blog

Upgrading Mautic 5 to 6 with Heavy Customizations: A 3-Week War Story

M
Mautomic Team
10 min read

When the Mautic community announced version 6, we read the changelog with a mix of excitement and dread. Excitement because the new version brought a completely redesigned UI, modern frontend architecture, and migration to Symfony's security component. Dread because we maintain a heavily customized white-label Mautic product - custom dashboard, custom email builder plugin, rebranded interface, hardcoded infrastructure configuration - and we knew every one of those changes would intersect with our customizations.

For a vanilla Mautic installation, the 5 to 6 upgrade is manageable. Follow the migration guide, run the update commands, fix a few deprecation warnings, and you're done.

For us, it was a 3-week project with 2 dedicated developers working full-time. Here's what happened, what hurt the most, and what we'd tell other teams facing the same situation.

What Changed in Mautic 6

Mautic 6 wasn't an incremental update. It was a significant rearchitecture of several core systems:

Completely new UI. This wasn't a visual refresh - it was a structural redesign. New HTML templates, new global CSS, new component architecture. The way pages render, the way styles cascade, the way the layout is composed - all of it changed. If you had custom styles or template overrides, they were almost certainly broken.

Symfony security component migration. Mautic moved its entire authentication and authorization system from a custom implementation to Symfony's security component. Login flows, access control checks, permission evaluation, session management - the whole stack was replaced with standard Symfony security. This is architecturally the right decision (Symfony's security component is battle-tested and well-maintained), but it meant every custom access pattern we'd built needed to be rewritten.

New HTML template engine with different global styles. The template compilation process changed. Templates that worked in Mautic 5 needed to be adapted not just for visual differences but for a fundamentally different compilation pipeline.

Deprecations across the codebase. Functions, services, and patterns that were available in Mautic 5 were deprecated or removed. Each individual deprecation was small, but there were many of them scattered across the codebase.

Our Starting Point

To understand why this upgrade was a 3-week effort, you need to understand what we were upgrading:

  • A completely rebranded interface - every screen customized with the client's branding, custom colors, custom typography, reorganized menu structure, custom dashboard.
  • A custom email builder plugin - replacing Mautic's default editor, with 4 custom dynamic block types, condition management, block cloning, and custom presets.
  • Hardcoded infrastructure configuration - SQS connections, queue settings, and transport configuration enforced at the code level to prevent admin UI overrides.
  • Custom template extensions - carefully designed to extend rather than replace Mautic's templates, following the upgrade-safe pattern we established early in the project.
  • Multi-tenant deployment infrastructure - single codebase deployed across all tenant instances via Kubernetes.

All of this was running in production on Mautic 5, serving real users, sending real campaigns. The upgrade needed to preserve every customization while adopting all of Mautic 6's changes.

Week 1: Assessment and UI Triage

The first thing we did was install a clean Mautic 6 instance and compare it side-by-side with our Mautic 5 customization. The goal was to catalog every breakage before writing a single line of fix code.

The UI damage was immediately visible. Mautic 6's new frontend changed the HTML structure of virtually every page. Our custom styles - designed to override Mautic 5's CSS - were now conflicting with completely different selectors and class names. Elements were misaligned, colors were wrong, some custom components were invisible, others were overlapping content.

The first week was spent systematically working through every screen:

Template extension fixes. Because we'd followed the "extend, don't hardcode" principle from the beginning, many of our template customizations still partially worked. The base templates had changed, but our extensions were overriding specific blocks rather than replacing entire files. We needed to update block names and adjust for new template structure, but we weren't starting from scratch. This single architectural decision - made years earlier - saved us days of work.

Style conflict resolution. We went through every custom CSS rule, checking whether the selectors still matched Mautic 6's HTML structure. Many didn't. Some required simple selector updates; others needed complete rewrites because the underlying HTML components had changed.

Component visibility and layout. Our custom dashboard, reorganized menu, and hidden/modified features all needed verification. Some worked fine (the menu restructuring was done through configuration that survived the upgrade). Others were broken because the underlying components they depended on had been restructured.

We prioritized ruthlessly: what's broken and visible to users first, what's broken but functionally correct second, cosmetic issues last.

Week 2: The Symfony Security Migration

This was the hardest and most time-consuming part of the upgrade. If we could assign percentages, the security component migration consumed roughly 40% of the total effort.

Mautic 5 had its own authentication and authorization implementation. It worked, it was familiar, and we'd built our custom access patterns on top of it. Mautic 6 replaced all of this with Symfony's standard security component.

This wasn't a find-and-replace job. The entire authentication model changed:

Login flow rewrite. Our custom login page - branded with the client's identity, custom fields, modified password reset flow - was built against Mautic 5's authentication controllers. Those controllers no longer existed. We needed to reimplement the custom login flow against Symfony's security system, using firewalls, authenticators, and user providers instead of Mautic's custom auth handlers.

Access control patterns. Everywhere our code checked permissions - "can this user access this feature?" - the API had changed. Mautic 5's permission checking methods were replaced with Symfony's voter and access decision manager patterns. Every custom permission check in our codebase needed to be identified and updated.

Session management. How sessions are created, validated, and destroyed changed with the move to Symfony security. Our custom session handling (particularly important in a multi-tenant context where sessions must be properly isolated) needed adaptation.

The security migration was the kind of change where each individual fix was relatively small, but there were dozens of them scattered across the codebase. Finding them all was the challenge - a missed permission check doesn't cause an error at compile time. It causes a "forbidden" response for a specific user performing a specific action, which you might not discover until that exact scenario is tested.

Week 3: Integration Testing and Edge Cases

With the UI fixes done and the security migration complete, the third week focused on making sure everything actually worked together.

Email builder plugin verification. Our custom email builder is the most complex piece of custom code in the platform. We tested every dynamic block type - Dynamic Image, Dynamic HTML, Dynamic Button, Dynamic Text - across multiple scenarios: creating new emails, editing existing campaigns, using conditions and segment-based content, cloning blocks, loading presets.

The builder survived the upgrade better than expected, primarily because it was built as an isolated plugin rather than a core modification. Its dependencies on Mautic internals were limited to well-defined interfaces, most of which remained stable across the version change.

Multi-tenant deployment validation. We ran the upgraded codebase through our full deployment pipeline - Argo CD sync waves, database migrations, health checks. The deployment infrastructure didn't care about the Mautic version; it cares about the Helm chart, the Docker image, and the health endpoint. The upgrade was invisible to the deployment layer.

Regression testing across tenant configurations. Different tenants have slightly different configurations - different enabled features, different custom fields, different segment structures. We tested representative configurations to ensure the upgrade didn't break tenant-specific setups.

Edge case hunting. The last few days were dedicated to the scenarios that automated tests miss: "what happens when a user with limited permissions tries to access the custom dashboard?" "Does the email builder handle dynamic content conditions correctly when the contact has no branch assignment?" "Do exported reports still format correctly with the new UI styles?"

The Single-Codebase Advantage

Here's the fact that made this entire upgrade feasible as a 3-week project instead of a 3-month one: all tenants share a single codebase.

Our platform currently runs multiple instances, and it's designed to scale to 300-500. But because every instance runs identical code - with differences only in configuration (database credentials, branding settings, enabled features) - we did the upgrade once. Not once per tenant. Once, period.

When the upgraded codebase was ready and tested, we deployed it through our standard batch deployment pipeline. The canary group got it first. We verified. Then the next wave. And the next. Within a day, every tenant was running Mautic 6.

If our architecture had used per-tenant customizations - different code for different tenants - the upgrade would have been 3 weeks multiplied by the number of unique customizations. That scenario doesn't scale. A single codebase with configuration-driven differences is the only architecture that makes major version upgrades manageable at scale.

Biggest Challenges, Ranked

Looking back, here's how we'd rank the difficulty of each challenge:

  1. Symfony security component migration - the most time-consuming and highest-risk change. Affects authentication, authorization, sessions. Errors here mean users can't log in or access features they should be able to. Testing coverage must be thorough.
  1. UI and style conflicts - tedious and time-consuming, but predictable. You open each screen, see what's broken, fix it. The work is mechanical rather than intellectually difficult, but there's a lot of it.
  1. Deprecations - scattered across the codebase, each individually small. The challenge is finding them all. Some generate warnings in logs, others cause subtle behavior changes. A comprehensive test suite is essential.
  1. Template engine changes - required understanding the new compilation process and adjusting our build pipeline. Once understood, the fixes were straightforward.

Advice for Teams Facing a Similar Upgrade

If you're maintaining a heavily customized Mautic installation and facing a major version upgrade, here's what we'd recommend:

Start with a clean Mautic 6 install. Before touching your customized code, understand what vanilla Mautic 6 looks like. Know the baseline before you try to fix the differences.

Catalog every customization before starting. Make a list of every file you've modified, every template you've extended, every service you've overridden. This inventory is your work list. Without it, you'll discover customizations by surprise - usually when something breaks in production.

Prioritize the security migration. Start with authentication and access control. Everything else depends on users being able to log in and access the right features. If the security migration isn't solid, nothing else matters.

Test early, test across roles. Don't save integration testing for the end. After each major fix, test with different user roles - admin, editor, viewer. Permission-related bugs are invisible until a specific role hits a specific feature.

Budget realistically. For a heavily customized instance, budget 2-3 weeks with 2 experienced developers. Less customization means less time, but don't assume a weekend will be enough if you've made significant modifications to the core.

Extend, don't hardcode - starting now. If you haven't adopted an extension-based approach to template customization, now is the time. Before the next upgrade, refactor your hardcoded template copies into proper extensions. Your future self will thank you.

Was It Worth It?

Absolutely. Mautic 6's new UI is cleaner and more modern. The Symfony security component is a better foundation for authentication than the custom implementation it replaced. The deprecation cleanup forced us to modernize patterns that were overdue for updating.

Three weeks and two developers is a real investment. But the alternative - staying on Mautic 5 as it ages out of community support, missing security patches, falling behind on features - is a much more expensive path in the long run.

Major version upgrades of heavily customized open-source software are hard. But they're manageable with the right architecture (extend, don't hardcode), the right team (developers who know both Mautic and Symfony), and the right expectations (this is weeks of work, not days).

Planning a Mautic Upgrade?

If you're running a customized Mautic installation and facing a version upgrade - whether it's 5 to 6 or any future major version - we can help. We've done it, we've documented the pitfalls, and we can help your team navigate the process efficiently.

[Book a free consultation](https://www.droptica.com/contact/) to discuss your Mautic upgrade strategy.

M

Written by

Mautomic Team

The Mautomic team brings together experienced marketing automation specialists, developers, and consultants dedicated to helping businesses succeed with Mautic.

Need Help with Mautic?

Our team of experts is ready to help you implement and optimize your marketing automation.

Get in Touch