Unchain my heart

20 Aug, 2025·
Valentijn Verhallen
Valentijn Verhallen
· 6 min read

Revisiting an old friend

Every developer has one; That personal tool you built years ago, still running somewhere, too useful (or too sentimental) to throw away. For me, that’s Unchained. A YAML-driven CMS I built in 2019 with Symfony 4 components and a minimal Bootstrap UI. The idea was simple: configuration and extensions defined in plain text, a custom routing kernel, and zero ORM assumptions. It worked beautifully — fast, lightweight, and entirely my own.

As time passed, the world moved on. By 2025, Unchained had quietly turned into a CVE museum — still functional, but increasingly fragile. Bootstrap 4 and other front-end packages were all outdated. Each install brought a flood of deprecation warnings and security notices.

Sometimes projects aren’t abandoned because they fail, only because we outgrow them. So, after years of patch-only maintenance, I decided to bring Unchained back to life with a full distribution reboot. To modernize it, and to rediscover the joy that started it all.

This wasn’t a rewrite — it was an intentional preservation.
Unchained’s core idea still holds up: the user defines the application, not the framework.

Why keep it alive?

Maintaining Unchained was never about user growth or external recognition. It was about continuity. It was about the fact that this tool still mattered - to me.
Even as the surrounding technology evolved, I found myself reaching for it every day. To abandon it would be to discard a lot of work, knowledge and personal value.

As user experience became increasingly lacking, the interface more cumbersome to work with, it became apparent the project had to overcome a lot of technical debt before anything could change. So the decision to perform a major update wasn’t technical convenience — it was a deliberate choice to keep the project alive and usable.

The 2025 reboot

The update was extensive. Because both front and back-end were heavily out of date, the project had to incorporate a full big-bang distribution upgrade. I deliberately chose not to upgrade in steps, because that would mean refactoring the same pieces of code multiple times.

The reboot ofcourse also includes a complete reimagination of the containers and deployment config. The application is now built on the php8.4-fpm-alpine backend, which can easily be bound with a proxy of your choice. The code comes with a straight-forward NGINX config.

Shortly after this upgrade, I quickly implemented a few long-awaited features, which I’ll include in the story.

Upgrading to Symfony 7

Symfony 7.3 components replace the old Symfony 4 components. This was a grand effort, as a lot of configurations and package signatures changed. It meant rewriting the entire Kernel, ultimately cleaning up configuration files. As some of the bootstrapping changed, I had to refactor all services, managers and configuration loaders, to be able to bind with the environment.

Symfony 7 also introduced the ability to bind with PHP 8.4, making use of the latest and greatest features. This gave room to clean up and refine the application architecture.

Bootstrap 5 - time to get flexy

The old front-end was built on frugal Bootstrap 4 templates, the used had to extend. This was one of the major flaws for new users. The update introduces a lot more (configurable) layout options, bringing a little more life into the application.

I was hesitant to upgrade to Bootstrap 5 at first, because it meant revisiting all templates and styling to work with flex-box. Technical, and moreover security concerns pushed me over the edge. The upgrade pushed all front-end packages to the latest version, enabling a much better user experience.

User experience upgrades

Whilst implementing the upgrades, I imagined I could improve on UX with a few simple adaptations. My personal installation of Unchained made use of a lot of extensions and overrides. I thought it’d be best to merge those, as this is the UI I grew accustomed to, and this would be the best layout to represent the app.
Bootstrap 5 also enabled to make use of dark/light themes easily.

This resulted in the following UI upgrades:

  • Two build-in dashboard styles (textual / blocky)
  • Light/dark theme
  • Two menu styles (top / aside)
  • Configurable visibility for titles, images, and content

Version 1.2.1 upgrades:

  • Improve dashboards:
    • Better sorting / filtering
    • Fix resetting on scroll on mobile
    • Add native translation
    • Restyle action buttons
  • Add optional live-search for forms
  • Simplify UI
  • Grouping cross-referenced fields – This allows to group a foreign field by a common ancestor (e.g. Stables - Horses - Rides)
  • Field conditions – Activate a field conditionally by the value of another

Technical QOL upgrades

After adding sports related records for a few years, I always wanted to get more insights, using average scores. But these were never implemented. It turned out that I needed only a few simple changes to allow basic SQL calculations.

This resulted in the following QOL upgrades:

  • Calculate min/max values – useful for finding date extremes
  • Calculate average numbers – useful for average scores

Version 1.2.1 upgrades include:

  • Calculate the sum of numbers – useful for totals in overviews
  • Add the ability to archive records – Archived records are hidden from the dashboards (configurable) and forms. This cleans up the UI in case of members leaving, death etc.
  • Rewritten routing system – better enabling locale switches

Decisions along the way

Working on a legacy project like this involves constant trade-offs. Some key decisions stood out:

  1. Preserve flexibility over standardization – Unchained’s custom configuration and runtime system gives freedom to the end-user. I chose to reinforce it rather than replace it with an ORM or strict framework conventions.
  2. Prioritize security and stability – The update removed critical vulnerabilities, rebuilt front-end dependencies, and ensured the system could continue running safely.
  3. Limit scope to meaningful improvements – I added themes, configuration enhancements, and dashboard options, but avoided unnecessary features that wouldn’t be used.
  4. Balance effort vs. personal value – This project has a single “user”: me. Every hour spent was measured against its actual usefulness and my enjoyment in maintaining it.

In other words: every choice was about sustaining the life of the project, not modernizing for modernization’s sake.

Conclusion

Maintaining Unchained reminded me of a subtle truth: software has value beyond its user base. Sometimes, the act of keeping something alive is as important as its functional purpose.

For me, the distribution reboot was a chance to close a chapter and start a new one. It allowed the project to continue serving its purpose without becoming a liability.

What comes next

Although Unchained will certainly serve a niche group, the reboot has opened possibilities for an actual open source project. I’ve already opened up the repository to accommodate contributions and initiated a few issues, of which I resolved a few in v1.2.1.

There’s still some ground to cover to make this project a success, and an end-user friendly product. For now, it’s enough to know that Unchained CMS is alive and secure, and that a piece of my past work continues to be useful.

Sometimes the most important updates aren’t about new features or frameworks — they’re about commitment, care, and continuity. This was one of those updates.