ECA's reported growth shows how far configuration-based automation has moved into Drupal site-building practice. The milestone also raises the stakes for a project that many sites now rely on for workflow decisions, integrations, and operational logic.
We've published an updated product strategy for Drupal CMS. Version 2.0 replaces the original Drupal Starshot strategy from August 2024, and it reflects where we are after nearly two years of building.
The updated strategy largely documents what we're already doing, and why, and makes some important clarifications.
Developers delivering for marketers
The initial strategy framed content creators and marketers as the primary target audience. That made sense as a signal about our ambitions: Drupal already has a reputation for being developer-friendly, so we wanted to emphasize the focus on end users.
In practice, though, it created some confusion. Marketers are the end users of the sites built with Drupal CMS, but they're not the ones installing it, configuring it, or (in most cases) choosing it. That decision usually belongs to agencies and professional developers.
So the updated strategy is clearer: Content creators and marketers remain the target person for the product as end users, and the primary audience for the builder experience is agencies and professional developers. We can only reach marketers if developers can succeed with Drupal.
Rather than representing a change in what we're focused on, this now more accurately captures it.
What's actually changed
The strategic frame has shifted to "making agencies and developers successful faster." The end goal of delivering great experiences for content teams is still central to the strategy, but we are explicit about doing that through agencies and developers.
A few other notable updates:
AI is now framed as infrastructure, rather than a feature. The original strategy positioned AI as one of several ways to win. Version 2.0 is more direct: every workflow in Drupal CMS should be operable by an AI agent. The goal is to be able to ship new AI-enabled workflows in days, not months.
Integrated hosting providers are now explicitly part of the strategy. These platforms are becoming real distribution channels for Drupal CMS, and the strategy names them as a priority. Making Drupal CMS excellent to provision and host is a prerequisite for those partnerships.
Vibe coding platforms are now named as a positioning opportunity. We're not competing with tools like Lovable or Bolt for prototyping. But we are positioning Drupal CMS as where those projects land when they need real content governance, multi-contributor workflows, and long-term maintainability.
The timeline has changed. Version 1.0 set a target of June 2027. Version 2.0 extends that to June 2028, acknowledging that the scope has grown and the strategy is more comprehensive.
What hasn't changed
We're still aiming to expand in the mid-market, with projects with total budgets in the $30,000-$120,000 USD range, and we're still explicitly not competing with entry-level website builders. We are also calling out that we will continue to maintain our leadership in the enterprise market.
And, of course, the differentiators against proprietary CMS solutions are the same: open source, no vendor lock-in, digital sovereignty.
Drupal 11.4.0 is the latest feature release in the Drupal 11 branch, bringing developer-focused enhancements, performance improvements, and a smoother upgrade experience. As a minor release, it introduces new capabilities while maintaining backward compatibility for public APIs, making it a recommended upgrade for production websites. Drupal 11.4.x will receive security support until June 2027…
Drupal work now extends beyond implementation alone. Four Asheville sessions show where accessibility, AI, components, and local tooling shape delivery.
Recent Drupal developments point to a clearer direction for the project's AI and developer-experience work. The immediate story is not only that Drupal is adding AI features.
Core performance, upgrade tooling, workflow orchestration, and AI governance are beginning to converge around a more controlled operating layer for modern web teams. That makes maintainability, auditability, and human review central to Drupal's AI direction, not secondary safeguards.
Drupal 11.4.0, published on 1 July 2026, gives that direction a stronger core foundation. The release reduces database queries compared with Drupal 11.3, speeds up recipe-based site installation, improves translation file handling, and adds Brotli compression for aggregated CSS and JavaScript when ext-brotli is available. It also introduces an experimental extensible native command-line interface through ./vendor/bin/dr, improves password hashing with support for argon2id, and adds display-management changes intended to support tools such as Drupal Canvas.
The AI layer is moving in the same direction. Drupal AI 1.4.0 adds developer-focused drush generate commands for providers, automators, guardrails, operation types, API explorers, function calls, and related extension points. The release also introduces chat normalisation, Views Bulk Operations integration for AI Automators, failover foundations, and streaming guardrails.
Those additions support AI workflows that need clearer execution paths, safer handling, and extension points that contributed modules can build on. They also match the distinction now forming around Drupal's Inside AI and Outside AI work. Inside AI covers cases where a person uses Drupal and Drupal uses AI to assist, while Outside AI covers cases where a person uses an external agent and the agent uses Drupal.
In that model, Drupal's value is not just page rendering. It is the governed system of record for content structure, permissions, validation, moderation, revisions, and publishing workflows. Dries Buytaert's recent writing on agentic workflows frames the same challenge around setup, connection, context, governed action, validation, recovery, and launch.
The same question appears in Drupal's orchestration work. Recent discussions around ECA, FlowDrop, Maestro, and Drupal core focus on whether automation tools can share vocabulary and data-handoff contracts while keeping their different execution models. Randy Kolenko's recent Nextide post adds the durable-state side of that discussion, positioning Maestro around long-running workflows, human approval steps, and audit trails that persist beyond a single request or cache cycle.
Upgrade tooling is also becoming part of the maintainability story. As of Rector 2.5, Composer-based sets support Drupal, allowing Rector to inspect composer.json, detect installed Drupal and dependency versions, and apply relevant refactoring sets without manually listing each Drupal version in rector.php. For site owners and maintainers, that reduces configuration work as Drupal 11.4, Drupal 12, and later releases move through the upgrade path.
The broader open-source context came through at UN Open Source Week 2026, held from 22 to 26 June 2026 at United Nations Headquarters in New York. The official programme focused on open source, artificial intelligence, Digital Public Infrastructure, Open Source Program Offices, sustainable public infrastructure, and digital cooperation. Matthew Saunders' Drupal.org reflection connected those discussions to AI harnesses, orchestration, constraints, auditability, verification, and human-in-the-loop workflows.
For Drupal, the practical implication is clear. AI adoption depends less on isolated prompts and more on trusted systems that can govern what agents do, record what happened, and keep human responsibility visible.
We're announcing DDEV v1.25.3: faster ddev start and ddev stop, built-in Docker Compose, stable Podman and Docker Rootless support, MariaDB 12.3 LTS support, Node.js improvements, XDG_CONFIG_HOME changes, and more.
This release represents 131 PRs from the entire DDEV community: your suggestions, bug reports, code, and financial support made it possible.
Table of Contents
Faster ddev start, ddev stop, and ddev restart
ddev start in v1.25.3 (bottom) is faster than in v1.25.2 (top), including a faster warm start:
ddev stop in v1.25.3 (bottom) is significantly faster than in v1.25.2 (top), and the same improvement also applies to ddev poweroff and ddev delete, since all three share the same code path:
ddev restart in v1.25.3 (bottom) is significantly faster than in v1.25.2 (top), since it stops and starts a project and benefits from both improvements:
Post-healthcheck tasks now run concurrently instead of one after another, reducing overall ddev start time, thanks to @jonesrussell.
A bug in the web server startup script also added a ~10-second delay to ddev stop. That delay is now gone.
We benchmarked ddev start from a stopped state on both macOS and Linux, and v1.25.3 is faster on both. Numbers vary by machine, but you can reproduce it with scripts/compare-start-perf.sh:
git clone https://github.com/ddev/ddev ddev-upstream
cd ddev-upstream
bash scripts/compare-start-perf.sh v1.25.2 v1.25.3
On macOS, v1.25.3 is about 28% faster than v1.25.2 (benchmarked by @rfay):
Summary (ddev start from stopped state)
-------------------------------------------------------------------
A (v1.25.2): median=11.03s trimmed-mean=10.49s
B (v1.25.3): median=7.91s trimmed-mean=7.84s
B is FASTER than A by 3.12s (-28.3%) on median
On Linux, it's about 21% faster (benchmarked by @stasadev):
Summary (ddev start from stopped state)
-------------------------------------------------------------------
A (v1.25.2): median=18.03s trimmed-mean=18.25s
B (v1.25.3): median=14.18s trimmed-mean=14.96s
B is FASTER than A by 3.85s (-21.4%) on median
New Docker Compose Library
DDEV now uses the Docker Compose SDK directly instead of shelling out to a separate docker-compose binary. The $HOME/.ddev/bin/docker-compose binary DDEV used to download and manage can be removed. This switch was made possible by the Docker Compose maintainers, who exposed the SDK as a reusable library in Compose v5.0.0. Thank you very much!
Driving Compose through the SDK is also what gives you the cleaner output and live per-step timer in the GIFs above: DDEV now controls how progress is displayed instead of passing through whatever the external binary printed.
This is the same underlying change that added the optional ddev config global --docker-buildx-version setting in this release. See Docker Buildx Requirement in DDEV for the full background on Buildx and the Compose SDK switch.
MariaDB 12.3 LTS Support
DDEV now supports MariaDB 12.3, the latest LTS release. For new projects, set it with:
ddev config --database=mariadb:12.3
To migrate an existing project's database, use:
ddev utility migrate-database mariadb:12.3
Podman and Docker Rootless Are No Longer Experimental
Both Podman rootless and Docker rootless are now stable. We introduced this support as experimental in v1.25.0. See Podman and Docker Rootless in DDEV for the background, trade-offs, and the work behind it. Setup instructions:
The correct Node.js version is now used during the build phase of ddev start. Previously the build phase always used DDEV's default version, which could cause problems when a project specified a different one (see ddev-pnpm#14).
If you install global npm packages in post-start hooks, move them to extra Dockerfiles instead, since those now run against the correct Node.js version.
nodejs_version is now preserved in .ddev/config.yaml even when it matches DDEV's default (previously it was removed in that case).
Setting nodejs_version: "" in .ddev/config.yaml always uses the default Node.js version bundled with DDEV, currently Node.js 24.
You can install additional Node.js versions with ddev exec n install <version> inside the web container. This used to be a reason to use nvm, which was moved to the ddev-nvm add-on in v1.25.0; with n built-in, you no longer need nvm for it.
N_PREFIX moved from /usr/local to /usr/local/n.
See the updated nodejs_version documentation for more details.
XDG_CONFIG_HOME Is No Longer Respected, but DDEV_XDG_CONFIG_HOME Is Available
We received several reports of DDEV recreating $HOME/.ddev repeatedly:
Warning: multiple global DDEV configurations found, /home/stas/.config/ddev is used, /home/stas/.ddev is not used, delete one of them to avoid confusion
IDEs such as PhpStorm don't always see XDG_CONFIG_HOME from the terminal, so DDEV fell back to and recreated $HOME/.ddev repeatedly. See the upstream issue IJPL-1055 for details.
To avoid this problem, DDEV now reads its own environment variable, DDEV_XDG_CONFIG_HOME, and no longer respects XDG_CONFIG_HOME. If you had set XDG_CONFIG_HOME to something other than its default of $HOME/.config, set DDEV_XDG_CONFIG_HOME to that same value instead.
Support for using $HOME/.config/ddev as the global configuration directory on Linux is unchanged.
Everything Else
This release includes many more features and bugfixes. See the full release notes for the complete list.
From the entire team, thanks for using, promoting, contributing, and supporting DDEV!
If you have questions, reach out in any of the support channels.
It's often useful to let the machines do the work, and output something that's dynamically computed on an entity. By that I mean something that can't be hardcoded as a fixed value for all entities of a particular type, but that varies for each entity, in a way that allows it to be generated in code rather than laboriously entered into each entity form by humans.
For example, you might want a backlink for an entity reference, or a link to a view that has an argument for the entity's ID, or something that depends on field values on the entity.
There are several ways in Drupal of putting something dynamic on the entity's display output. You can of course add something to the build array yourself, in either the entity's view handler or hook_entity_view(). The extra fields system lets you then declare your additional build array item with hook_entity_extra_field_info() which allows it to be rearranged among normal fields in the field admin UI.
This is okay, but the extra fields system is Drupal 5-era stuff. Your piece of build array is just that, some render stuff; it can't participate in any data structures and nothing else will recognise it and work with it.
A better approach is a computed field. This involves a little more boilerplate code than the extra fields technique, but there are several benefits.
The first is that you are defining a field value, not a render array, and you get access to all the field formatters that apply to your type of data. So for example, if your computed data is a URL, you get all of the link field formatters at your disposal, in core and contrib.
The second is that anything that works with fields will be aware of your computed field. So you can add it to a view as a field (though not a sort or filter of course, since it has nothing in the database). You can add it to a SearchAPI index (and there, you actually can filter on it, because SearchAPI will index the computed value into its backend).
The code
Here's what you need to do. You need two things:
A FieldItemList class.
A declaration of your field in an entity class or field info hook.
Unlike declaring code fields, you don't need to declare a field storage: that's because a computed field doesn't store anything!
1. The FieldItemList class
Create a subclass of \Drupal\Core\Field\FieldItemList that uses \Drupal\Core\TypedData\ComputedItemListTrait. In this class, all you need to do is implement computeValue() to return your data.
<?php
// The namespace doesn't matter, but I like to put it under \Field.
namespace Drupal\my_module\Field;
use Drupal\Core\Field\FieldItemList;
use Drupal\Core\TypedData\ComputedItemListTrait;
use Drupal\Core\Url;
/**
* Field item list class for my computed field.
*/
class MyFieldFieldItemList extends FieldItemList {
use ComputedItemListTrait;
/**
* {@inheritdoc}
*/
protected function computeValue() {
// You have access to the complete entity, so you can use other field
// values.
$entity = $this->parent->getValue();
// Create a field item for your data. You can create more than one for a
// multi-valued field.
$this->list[] = $this->createItem(0, [
'value' => 'cake',
]);
}
}
?>
For most field types, the key to use in the item array is 'value', but some more specialised fields use something else. You can find this by looking in the field item class for the field type. For example, in \Drupal\link\Plugin\Field\FieldType\LinkItem::propertyDefinitions() you can see that for a link field, you need these array keys:
For the field definition, there are two things to consider:
Is it on an entity type you control, or somebody else's?
Do you want a base field or a bundle field?
If it's your own entity type, you define the field in the entity class, in either the baseFieldDefinitions() or bundleFieldDefinitions() method. If it's an existing entity type, you need to use hook_entity_base_field_info() or hook_entity_bundle_field_info().
In all cases, the code is broadly similar. For a base field, it looks like this:
/**
* Implements hook_entity_base_field_info().
*/
#[Hook('entity_base_field_info')]
function entityBaseFieldInfo(EntityTypeInterface $entity_type) {
if ($entity_type->id() == 'node') {
$fields = [];
$fields['my_computed_field'] = BaseFieldDefinition::create('link')
->setLabel($this->t('My computed field'))
->setDescription($this->t('My field is amazing.'))
// This declares it as a computed field.
->setComputed(TRUE)
// This is the class you created earlier, which provides the values.
->setClass(MyFieldFieldItemList::class)
// Optional default view display options, which can be overriden in the admin UI.
->setDisplayOptions('view', [
'label' => 'above',
'type' => 'link',
'weight' => '0',
]);
return $fields;
}
}
For a bundle field, you need the Drupal\entity\BundleFieldDefinition class from Entity module, and a few extra things need to be explicitly set on the definition because the field system doesn't handle them for you:
$fields['my_computed_field'] = BundleFieldDefinition::create('link')
->setName('my_computed_field')
->setTargetEntityTypeId($entity_type_id)
->setTargetBundle($bundle)
// Rest of the definition as above.
If you want to do it all with less boilerplate, or your computed field is something that's reusable across different entity types, consider the Computed Field module as an alternative to the code examples above. Instead of a Field class, the computational code does in a plugin, which the module then makes available in an admin UI where you can create and edit computed fields alongside the usual stored fields.
And if your computed field is purely a render array rather than data, the Computed Field module also provides a computed_render_array field type for that, with an accompanying field formatter.
Do you need help with data structures, and their integration with Views or SearchAPI? I'm available for hire - contact me!
Canvas Internals - JSON data types in differentes databases: It works on my machine!
Image
Drupal has been working to add a JSON data type since 2023, but that has not landed yet. Drupal Canvas jumps ahead of that in its inputs for a component tree item with
'inputs' => [
'description' => 'The input for this component instance in the component tree.',
'type' => 'json',
'pgsql_type' => 'jsonb',
'mysql_type' => 'json',
'sqlite_type' => 'json',
'not null' => FALSE,
],
Recently some of our tests started failing for MySQL and Postgres on CI, but passed in SQLite and MariaDB, which is what most of us use locally.
The problem was that the sorting of the keys of that field was not deterministic, and we used assertSame in our tests to see if operations added/removed the inputs as expected when components evolved.
To make lookups more efficient, MySQL also sorts the keys of a JSON object. You should be aware that the result of this ordering is subject to change and not guaranteed to be consistent across releases.
For PostgreSQL, the engine offers two different data types: json and jsonb, with the second being the option we (and core) opted for because of its efficiency. But that's key, as the docs explain:
In general, most applications should prefer to store JSON data as jsonb, unless there are quite specialized needs, such as legacy assumptions about ordering of object keys.
That's exactly what our problem was.
For MariaDB, the JSON type is just an alias. See their docs:
JSON is an alias for LONGTEXT COLLATE utf8mb4_bin introduced for compatibility reasons with MySQL's JSON data type. MariaDB implements this as a LONGTEXT rather, as the JSON data type contradicts the SQL:2016 standard, and MariaDB's benchmarks indicate that performance is at least equivalent.
And the last one, SQLite, has support for a jsonb format since 3.45, but the work in progress for introducing this in Core uses json, which, like MariaDB, is ordinary text and sorting of the keys is respected.
How did we fix this?
The actual sorting of the inputs in the database is, as of today, irrelevant to us. So we ended up with:
Our own assertSameInputs, which sorts the keys before comparison. assertEqualsCanonicalizing is not an option, as that sorts by value.
Our own PHPStan rule, which is not 100% accurate but detects most usages of assertSame with these inputs, and suggests using assertSameInputs instead.
Translating Drupal Canvas
This is just one of the many show-stoppers that we faced while working on the much-anticipated symmetric translation support for Drupal Canvas. If you want to test this experimental feature, check the release notes in Canvas 1.7.0, but please only on test sites for now!
Shibin Das explains why visual workflow tools need visible execution, audit trails, runtime ownership, and clear boundaries between deterministic workflows and AI-assisted decision-making.
Every Drupal developer eventually hits this wall: a client or editor requests that a standard text field (like a headline or subheadline) be expanded from 255 to 512 characters. You change the value in the field configuration YAML or try to update it programmatically via the Entity API, only to be hit with a fatal exception.
Randy Kolenko, partner and senior architect at Nextide, recently joined the Drupal Orchestration Initiative with Jurgen Haas, Shibin Das and Dries Buytaert. The Orchestration Initiative is still in its infancy, however, the discussions and (dis)agreements will continue until the vision of what Orchestration means to Drupal is sharpened.
Instead of manually adding dozens of configuration sets and keeping your list up to date as you upgrade to new Drupal releases, you can enable the new feature in your rector.php file. Rector will automatically inspect your composer.json, detect your exact installed versions of Drupal and its dependencies, and run the relevant refactoring sets. This means as you upgrade to newer versions of Drupal in the future, Rector will dynamically adapt and apply the correct upgrade rules without any manual config updates.
Two lines of configuration does the work: withSetProviders registers the Drupal rules, and withComposerBased(drupal: true) tells Rector to select them based on what's actually installed. No version numbers need to be in your config.
Rector reads the installed drupal/core version and loads every set up to and including that minor. A site on 11.4 loads the 11.0 → 11.4 rules. A site on 11.2 loads 11.0 → 11.2. When you upgrade core, the set selection moves with you. You don't need to change rector.php again.
That's the same mechanism Rector already uses for Symfony, Doctrine, Twig, and PHPUnit. Drupal is now a first-class citizen.
But the config was never the hardest part. The hard part was coverage. Automatic version selection is only worth anything if the rules behind it are good and preferably complete. That changed a lot when the Project Update Bot was refreshed for Drupal 12 readiness, pushing automated deprecation coverage past 80%. The bot and drupal-rector draw from the same well. Better coverage there is what made shipping this as the default setup defensible. The fact we also run all these rules against almost 10.000 contrib modules makes for some very good testing.
Even less work maintaing your Drupal site
Drupal rules will also appear on getrector.com/find-rule at a later date. That's the searchable catalogue of every rule Rector ships. Having Drupal in it means a maintainer can look up exactly which transformation handles a given deprecation, the same way they can for any other framework today.
Drupal 12 readiness isn't a one-time push, every new minor brings deprecations, and we will keep on adding any missing coverage. Because your setup selects rules by installed version, the rules you get tomorrow are the rules for the core you're running tomorrow. No migration step. You upgrade core, you run Rector, you're up-to-date.
Add it to a project this week. Point it at your custom modules, run vendor/bin/rector process, and open an issue when something doesn't transform the way it should. It's a beta because we want exactly that. Two lines of config, the correct rules for your version, automatically.
Also posted on Rector's blog, big thanks to the author of Rector, Tomas Votruba for the collaboration on making this happen.
Digital sovereignty often sounds abstract but, in practice, it comes down to technical decisions: where data is stored, who controls the platform, how systems are maintained over time, and how much privacy, transparency, and independence is built in from the start.
These choices directly affect how digital services are designed and delivered. That is why digital sovereignty is a key theme at DrupalCon Rotterdam 2026. The event's Digital Sovereignty & Open Web track connects platform strategy with architecture, governance, accessibility, regulation, and the long-term future of open digital ecosystems.
Image
Photo by Matthew Saunders
This is not only a policy discussion, it is also a practical one. Privacy-first architecture, public code, digital identity, accessibility, open-source infrastructure, and responsible AI all shape how organisations think about control and trust today. In that context, digital sovereignty is no longer a side topic, it's becoming part of how teams approach procurement, hosting, compliance, and long-term platform resilience.
That is what makes this conversation especially relevant in Rotterdam. Developers can connect values to implementation, digital leaders can look at governance and long-term control and public sector teams, accessibility advocates, and open-source contributors can all bring important perspectives to the same discussion.
Drupal has long been part of the open web story. At DrupalCon Rotterdam, digital sovereignty becomes a practical question: how do we build systems that remain open, secure, adaptable, and worthy of trust.
- Article by Daniela Moreira.
🎟️ Join Us at DrupalCon Rotterdam 2026
Continue the conversation at DrupalCon Rotterdam 2026, where the Digital Sovereignty & Open Web track explores the technologies, strategies, and decisions shaping open digital ecosystems.
Release timing is often treated as calendar work, but the 11.4.0 delay exposed a dependency problem beneath the schedule. The fix changes how selected Composer dependencies can move when security updates land.
Many Drupal sites are fast when launched, but become heavier over the years. The cause is usually not Drupal itself, but how the platform is maintained, extended and filled with content over time. Slowness rarely comes from one change A Drupal site does not usually become slow overnight. It is usually a gradual process: more content is added; more images and files are uploaded; new modules are installed; small custom features are built; analytics, marketing and chat scripts are added; cache or server configuration falls behind; old solutions remain in place next to new ones. Each individual change may seem small. Together, they can make the site slower, harder to manage and more expensive to develop further. Content growth has a bigger impact than expected A large organisation's Drupal…