Migration from v1/v2
When to Use
Read this if you are moving an existing Group site to a newer major version — v1→v2, v2→v3, or v3→v4.
Decision
| From | To | Path |
|---|---|---|
| Group 1.x | Group 2.x | In-place update via group_update_9200–group_update_9211 (installs flexible_permissions, converts fixed roles to the scope system) — the 1.x site must have run its own updates through 8023 first |
| Group 2.x | Group 3.x | No in-place upgrade — fresh install + migration required (do not use group2to3) |
| Group 3.x | Group 4.x | Normal module update on Drupal 11.2+ — same data model, but requires an explicit @alpha flag |
Version Strategy Summary
Two branches are actionable on Drupal 11.4:
| Branch | Declared core compatibility | Status |
|---|---|---|
| 3.3.x | ^10.3 \|\| ^11 |
Latest stable — 3.3.5 (2025-05-06). The only Group release with a stable tag, and the only one the contrib ecosystem supports. |
| 4.0.x | ^11.2 |
Release branch whose newest tag is 4.0.0-alpha2 (2026-08-21, after 4.0.0-alpha1 on 2026-04-24) — infrastructure rewrite, BC breaks complete, no stable. Requires an @alpha install; not covered by Drupal security advisories. |
Group also still lists 8.x-1. and 2.3. as supported branches upstream (8.x-1.6 declares ^9.5 || ^10, 2.3.2 declares ^10.3 || ^11). Treat both as legacy maintenance lines — start nothing new on them.
v2 and v3 are functionally identical. The only difference is machine names. There is no in-place database upgrade from v2 to v3.
v3 and v4 share the same data model and machine names — unlike v2→v3, upgrading v3→v4 is a normal module update (no data rename), but it requires Drupal 11.2+ and code/config changes. See Upgrading v3 to v4 below.
v1 to v2: Key API Changes
v1→v2 is an in-place update, unlike v2→v3. Group 2.x ships database update hooks — group_update_9200() through group_update_9211() — that convert a 1.x site in place: they install the (then required) flexible_permissions module, swap the group_content / group_content_type entity classes and rework the relationship tables, and rewrite the fixed v1 roles into the scope system. The role conversion is mechanical: {group_type}-anonymous becomes scope: outsider / global_role: anonymous, {group_type}-outsider becomes scope: outsider / global_role: authenticated, {group_type}-member becomes scope: insider / global_role: authenticated, and every user-created role becomes scope: individual. Synchronized roles that carried no permissions and no admin flag are deleted rather than converted.
One prerequisite: group_update_last_removed() at 2.3.2 returns 8023, so the 1.x site must have run its own updates through 8023 before you require 2.x and run drush updb. A site further behind has to catch up on 1.x first. (Verified by reading group.install at tag 2.3.2.)
| v1.x | v2.x / v3.x |
|---|---|
GroupContent entity |
GroupRelationship entity |
GroupContentType |
GroupRelationshipType |
GroupContentStorage |
GroupRelationshipStorage |
GroupContentEnablerInterface |
GroupRelationInterface |
$group->addContent($entity, $plugin_id) |
$group->addRelationship($entity, $plugin_id) |
$group->getContent($plugin_id) |
$group->getRelationships($plugin_id) |
$group->getContentByEntityId($entity) |
$group->getRelationshipsByEntity($entity) |
GroupContent::loadByContentPluginId($id) |
GroupRelationship::loadByPluginId($id) |
GroupContent::loadByEntity($entity) |
GroupRelationship::loadByEntity($entity) |
Annotation @GroupContentEnabler |
Attribute #[GroupRelationType(...)] |
Plugin base: ContentEnablerBase |
Plugin base: GroupRelationBase |
| Fixed roles: anonymous, outsider, member | Scope system: outsider/insider/individual |
DB table: group_content_field_data (v2) |
DB table: group_relationship_field_data (v3) |
Machine name: group_content (v2) |
Machine name: group_relationship (v3) |
v2 to v3: Migration Path
There is no automatic in-place upgrade. The official recommended approach is a fresh install + content migration:
Option A: Recommended (clean migration)
- Set up a clean Drupal 11 install with Group 3.x
- Configure group types, roles, and relationship types in the new install
- Migrate group, membership, and relationship data using Drupal Migrate API or a custom script
- Key: use
GroupRelationshipTypeStorage::getRelationshipTypeId($group_type_id, $plugin_id)to calculate the correct bundle ID in the new system
Option B: Manual database rename (risky, community-documented)
- Rename database table
group_content__group_rolestogroup_relationship__group_roles - In all configuration files: replace
group_contentwithgroup_relationshipandgroup_content_typewithgroup_relationship_type - In config file names: replace
group.content_type.withgroup.relationship_type. - Clear all caches
- Run database updates
- Validate with Drupal status report
Do not use group2to3. Older write-ups (including earlier revisions of this guide) offer the group2to3 contrib module as a third option that automates the Option B steps. As of 2026-08-16 it is marked Unsupported and Obsolete on drupal.org and has never carried a tagged release — only 3.0.x-dev and 3.x-dev, last touched 2024-04-19. Use Option A or Option B.
32-character ID limit gotcha: When renaming configuration IDs, the new IDs may exceed Drupal's 32-character config ID limit, especially if your group type IDs are long. Audit all config IDs before migration.
Custom Code Updates for v2 → v3
Grep for these patterns in your custom code and update:
GroupContent → GroupRelationship
GroupContentType → GroupRelationshipType
group_content → group_relationship (in entity type IDs, service calls, routing)
addContent( → addRelationship(
getContent( → getRelationships(
getContentByEntityId( → getRelationshipsByEntity(
loadByContentPluginId → loadByPluginId
Handler System Migration (v1 to v2/v3)
v1 plugin methods that were removed from GroupRelationBase and moved to handlers:
| Removed from plugin | Now in handler |
|---|---|
checkAccess() |
access_control handler |
getOperations() |
operation_provider handler |
getPermissions() |
permission_provider handler |
postInstall() |
post_install handler |
Upgrading v3 to v4
Because v3 and v4 share the same entity types, tables, and machine names, this is a normal contrib module update — not a data migration. There is no equivalent of the v2→v3 table rename.
Prerequisites
- Upgrade the site to Drupal 11.2 or newer first. Group 4.x will not install on Drupal 11.0/11.1.
- Confirm you accept an alpha. The newest tag on the 4.0.x branch is
4.0.0-alpha2, and alpha releases are not covered by Drupal security advisories. - Move to 4.x with an explicit stability flag, then run database updates:
composer require 'drupal/group:^4.0@alpha' --update-with-dependencies
drush updb
A plain composer update drupal/group will not cross from stable 3.3.5 to an alpha: a default project root ships minimum-stability: stable, and only the per-package @alpha flag on the require line overrides it. You only need to loosen the root (composer config minimum-stability dev plus composer config prefer-stable true) if Group arrives transitively through a recipe, distribution, or site template — stability flags are not transitive. See Pre-Stable Template Deps Require Consumer minimum-stability.
4. Check your contrib modules first. group_content_menu, group_permissions, subgroup, group_flex, and group_action all cap at Group 3.x and will block the require — see Sub-modules and Group Actions (contrib).
5. You may remove drupal/flexible_permissions and drupal/entity from composer.json if no other module needs them — Group 4.x no longer requires either.
Config changes
- Remove
creator_wizardfrom everygroup.type.*config file. - Remove
use_creation_wizardfrom theplugin_configof everygroup.relationship_type.*config file. - Re-export config after the update so exported YAML matches the 4.x schema.
Custom code changes
| 3.x | 4.x |
|---|---|
group.membership_loader service |
Removed — use GroupMembership::loadSingle() / ::loadByGroup() / ::loadByUser() |
loadByGroup($group, 'role_id') / loadByUser($account, 'role_id') |
$roles filter must be an array: loadByGroup($group, ['role_id']) |
Custom flexible_permissions_calculator services |
Re-implement as core AccessPolicyBase subclasses tagged access_policy |
Calls to procedural Group hook functions (e.g. group_entity_access()) |
Gone — Group's hooks are OOP methods in src/Hook/; do not call them directly |
| Code relying on an entity being re-saved when added to a group | Entities are no longer re-saved; only cache tags are invalidated |
Programmatic Group::save() expecting an auto-created creator membership |
Auto creator membership is form-only — call addMember() explicitly |
Common Mistakes
- Attempting a direct v2-to-v3 schema upgrade without a migration plan. The module maintainer explicitly states this is unsafe and there is no upgrade hook.
- Forgetting to update entity references in Views, Panels, and other config that references
group_contentorgroup_content_typemachine names. - Installing Group 4.x on Drupal 11.0/11.1. Group 4.x requires Drupal 11.2+ for the Access Policy API; upgrade core first.
- Expecting
composer update drupal/groupto reach 4.x. It cannot — 4.0.x is alpha-only and a stable-only root will silently keep you on 3.3.5. Require it with'drupal/group:^4.0@alpha'. - Leaving
creator_wizard/use_creation_wizardkeys in config when upgrading to 4.x. The 4.x config schema no longer defines them and config validation will flag them.
See Also
- Core Architecture
- Configuration
- Reference: https://www.drupal.org/docs/extending-drupal/contributed-modules/contributed-module-documentation/group/group-v2v3-guides/upgrading-from-v1-to-v2