The State of Grav: Where We Are and Where We're Going

An honest look at the journey from 1.7 to what comes next

6 mins

I want to take a moment to write honestly about where Grav is, how we got here, and what's coming next. The short version is that Grav 1.7 has been the backbone of this project for the past five-plus years, it's been remarkably stable, and we've reached a natural ceiling that means it's time for a generational change. The longer version is worth telling properly, because the path through the 1.8 beta wasn't smooth and I think the community deserves a transparent account of what happened.

This post is the first in a series. It's the "where are we and why are we here" post. The next post in the cycle is the announcement of what comes next, but I didn't want to ship that without first giving an honest backstory.

The PHP Ceiling

Grav 1.7 runs on a stack that, when we shipped it in early 2021, was current and well-supported. Symfony, Twig, the entire dependency tree was on the latest stable lines of the time, and we picked PHP 7.3 as the minimum because we wanted to stay broadly compatible. Over the years since, we've quietly raised the supported PHP ceiling, and the most recent 1.7 patch releases run cleanly all the way through PHP 8.3.

That's where the ceiling sits. The 1.7 library stack cannot go further than 8.3. Not because of anything we did, but because the versions of Symfony and Twig that 1.7 was built on are themselves end-of-life, and they can't be transparently swapped for newer ones without breaking other things in the stack. PHP 8.3 itself is no longer in active support (it's in security-fixes-only mode now), and PHP 8.4 and 8.5+ are where the modern PHP ecosystem lives.

This isn't a Grav-specific problem. It's the natural lifecycle of a long-lived dependency tree. To stay current, secure, and performant going forward, the entire stack needs to move.

The 1.8 Beta Journey

Grav 1.8 was the first attempt at this. The plan was straightforward on paper: take 1.7, upgrade every core library to the latest version, set PHP 8.3 as the minimum (giving us a clean crossover with the latest 1.7), and support cleanly through PHP 8.5+. The idea was a one-time library bump that would buy years of forward compatibility on a modern stack.

We've been actively developing 1.8 for the better part of two years. The library upgrades themselves went well. The new vendor stack is solid, the test suite passes, and the code is cleaner than it's ever been. From a pure "does the new code work" perspective, 1.8 was a success.

The challenge was never the new code. The challenge was the upgrade experience: how do you take a running 1.7 site, with plugins loaded into memory and content being served live, and swap the entire library tree underneath it without anything breaking?

The Safe Upgrade Experiment

To handle that, we built a safe upgrade system. The idea was that the upgrade process would prepare a staging area, copy the new code in, run a series of preflight checks, and only commit to the swap if every check passed. Crash detection, restore states, the works. In a controlled environment, on a clean test setup, it worked beautifully.

Then we shipped it to beta users.

What we found was that real-world Grav installations are a lot more varied than any test setup. PHP opcache pinning across the swap, file-locking quirks on certain hosts, code already loaded into memory from the running 1.7 process colliding with the freshly-installed 1.8 vendor tree, plugins that pre-bootstrapped during a request and then ran into mismatches mid-flight: every category of edge case threw a different kind of failure, and a lot of them were specific to the user's host, OS, plugin set, or PHP build. We could fix them one at a time, but as fast as we fixed one, the beta would hit two more.

We tried to backport the safe upgrade machinery to a 1.7.50 release, on the theory that users could have a known-good safe upgrade before the bigger 1.7-to-1.8 jump. The same kinds of issues turned up. We pulled the 1.7.50 releases and rolled the stable 1.7 line back to 1.7.49, where it sat for an uncomfortable stretch while we figured out what to do.

That period was hard. We had a lot of work invested in 1.8, the code was good, and the release kept drifting because the upgrade story wouldn't stabilize.

Lessons Learned

A few things became clear by the end of the 1.8 beta cycle.

The first is that safe upgrade was solving the right problem from the wrong angle. In-place mutation of a live running system is inherently fragile, especially when the change being made is a full vendor stack swap. You can engineer your way around individual edge cases, but the underlying premise (mutating a process and library tree that the OS, the web server, opcache, and the running PHP process all have opinions about) is just not a stable foundation to build on.

The second is that the variety of real-world Grav installations is wider than any test matrix. Grav runs on shared hosts, dedicated boxes, containers, on Apache, Nginx, Caddy, on PHP-FPM, on mod_php, on every flavor of Linux and a non-trivial number of macOS and Windows dev setups. For most things Grav does, that diversity is a strength. For an in-place vendor swap, it's a serious problem.

The third, and the most important one, is that sometimes the honest answer is to find a better approach rather than keep fixing the current one. We could have kept patching safe upgrade. We probably would have eventually shipped a version that worked for most people. But "most people" isn't the bar this project has held itself to for a decade, and the time and energy that would have gone into the next round of patches were better spent rethinking the whole thing.

What's Next

Two things, in short.

First, Grav 1.7.52 is the final stable 1.7 release. It's a small maintenance update with a handful of upgrade-process improvements, and from this point on the 1.7 branch is in security-fix-only mode. If you're running a 1.7 site and you're happy with it, 1.7.52 is a safe long-term resting place. We're not pushing anyone off of it.

Second, the next post in this series is the announcement of what comes after, and it's substantially bigger than a 1.8 ship would have been. Rather than try once more to make an in-place upgrade work, we've spent the last year rethinking what a generational change to Grav should actually look like. A new library stack, yes, but also a brand new admin, a first-party REST API, a first-class AI integration story, and a migration path that doesn't pretend the upgrade is a small thing.

That's the next post. I wanted this one to land first, because the path matters and I think you've earned an honest accounting of it.

If you've been part of the 1.8 beta, hit edge cases, filed reports, or just put up with the longer-than-expected silence on stable 1.x progress, thank you. The lessons that came out of that work are the reason what's coming next is going to be a substantially better release. As always, Discord is the best place to talk through any of this, and the next post in the series is right behind this one.

— Andy


Grav Premium
Turbo-charge your Grav site - from the creators of Grav