A Config Entity's UUID Is Its Real Identity, Not Its Machine Name

Every Drupal config entity has a machine name and a UUID, and it's easy to go a long time treating the machine name as the entity's actual identity, because it's the one you type — id: api_publisher, right there at the top of the YAML file, human-readable and stable. The UUID sits a couple of lines above it, a string nobody reads, that looks like it shouldn't matter. Watching one role's UUID change twice across the same repository's history, while its machine name never moved, is what made clear it's the other way around.

The same role, three UUIDs

sync/user.role.api_publisher.yml defines a role used by this site's publishing pipeline. Across its history in this repo, the file's id field has always read api_publisher. Its uuid field has not been so consistent:

  • First committed with uuid: 8f9bdd2f-f78e-4b3d-b170-a2ca6b2388c5
  • The very next commit, labeled "exported sync from db," rewrote it to uuid: e66f07aa-51e3-487d-b9e0-9828919e4796 — same id, same permissions, different UUID
  • A later commit cleaning up dead theme config rewrote it again, to uuid: a831fa28-40a7-487e-b761-9a68876b5943
  • A subsequent commit that added the scheduler permission left the UUID alone this time — only the permissions list and module dependencies changed

Nothing about the role's purpose or permissions changed between the first and second UUID. What changed was that the file got regenerated from a database export rather than hand-edited. Drupal's config export doesn't derive a UUID from the machine name — it writes out whatever UUID is currently stored against that role's row in the database being exported from. If two different environments each independently end up with a role called api_publisher — one created it directly, another created its own copy separately rather than importing the first one's config — Drupal has no way of knowing they're "the same role" just because the machine name matches. As far as config import is concerned, they're two distinct entities that happen to share an id, and only one of them can be the canonical one at a time. Whichever environment last got exported from is the one whose UUID wins for the file in the repo.

Why the machine name doesn't settle it

This matters practically the moment you try to reconcile config between two environments that have each already created the same-named entity on their own, rather than one having imported the other's export from the start. drush config:import compares incoming YAML against what's already in the target database by UUID, not by machine name. If the incoming file's UUID doesn't match what's already sitting in the local database under api_publisher, Drupal doesn't treat it as "update this role's permissions" — it either refuses the import outright as a conflict, or deletes the local entity and recreates it fresh from the incoming definition, depending on the exact command and situation. Either way, what looks to a human like "the same role, just with different permissions now" is, to Drupal, potentially two unrelated objects that happen to be named the same thing, and the machine name is doing none of the work of telling them apart.

Why the machine name feels like identity anyway

Every other tool you interact with a Drupal role through — the admin UI, drush role:list, permission checks in code, this very YAML file's own id field — refers to it by machine name exclusively. The UUID never surfaces anywhere in normal use; it only becomes visible, and only starts mattering, at the exact moment two environments' understanding of "the same entity" needs to be reconciled through config import. That's a rare enough event, on most sites, that the UUID field can sit there for years looking like ceremony rather than the actual mechanism holding entity identity together across a config export/import boundary.

The actual habit this changes

If a config import behaves like the target already has an entity by that name but rejects the incoming file as some kind of conflict, or silently recreates something you expected to just get updated, the machine name is very unlikely to be where the mismatch lives — it almost certainly matches on both sides, which is exactly why it looks like it should have worked. The UUID is the field worth diffing first. It's not decoration above the field you care about; for config entities, it's the field Drupal cares about, and the human-readable one underneath it is, from the system's point of view, just another attribute that happens to be along for the ride.

Add new comment

Restricted HTML

  • Allowed HTML tags: <a href hreflang> <em> <strong> <cite> <blockquote cite> <code> <ul type> <ol start type> <li> <dl> <dt> <dd> <h2 id> <h3 id> <h4 id> <h5 id> <h6 id>
  • Lines and paragraphs break automatically.
  • Web page addresses and email addresses turn into links automatically.
Please share this article on your favorite website or platform.