Notes from the work

Making a Drupal site rebuildable from an empty database

Ask the concierge Contact

Short answer: a Drupal site is rebuildable when every part of it has one home you can name. Code and configuration live in git. The content the site's pages depend on lives in git too, as seed files. Editors' everyday content and uploaded files live in a backup that someone has actually restored. With that in place, four commands turn an empty database into the site. We proved it on our own site, tecnocratica.com.co, on 30 September 2026 and again on 2 October: a build from the repository matched production field for field, in both languages. Here is how it works, what that proof shows and what it doesn't, and what we would check on yours.

What does "rebuild from an empty database" mean?

It means nobody's memory is part of the install. On our site the sequence is:

  1. drush site:install --existing-config: install Drupal from the configuration saved in the repository.
  2. drush deploy: Drush's standard deploy. It runs the database updates, imports the configuration, rebuilds the caches and runs any deploy hooks, in that order.
  3. drush tecnocratica:seed: create the content the site depends on, from files in the repository.
  4. drush tec:release-sync: bring the projects' release notes from GitHub, where they are published.

If the result is the production site, the site belongs to its repository, not to the person who built it. If it isn't, the difference is exactly what would be lost the day that person leaves.

Why split a site into four streams?

A Drupal site doesn't have one source of truth. It has four, and each moves in its own direction:

  • Code (PHP, templates, dependencies) lives in git. It travels one way: from a developer, through CI, to production.
  • Configuration (content types, fields, views, settings) is exported to files in git and travels the same way.
  • Content (pages, media, translations) is written by editors on production. It travels down only, when a local copy is filled from production. It never travels up as a database push.
  • Files (uploads) stay on production too, and travel down only.

Why it matters to you: most lost work starts with copying a whole database between environments. With the streams apart, a deploy changes code, configuration and the content the code owns, and leaves editors' work alone.

What is "config as code"?

Drupal can export its configuration as YAML files and import it into another environment. That is Drupal's own configuration management. On our site, config/sync holds 784 such files (as of 27 October 2026), and every deploy imports them. A changed field is exported, committed and applied by the deploy; nobody clicks it into production.

Two rules keep this honest:

  • Before a deploy that adds or removes a module, we fill a local copy from production, run drush deploy on the new code, and require drush config:status to report no differences. That rehearsal catches the imports that would otherwise fail only on production.
  • A rebuilt site is not a byte-for-byte copy. The front page and the hero images point at database ids, and a fresh database hands out its own. The seeder fixes those references by name, so on a rebuilt site the configuration check lists those few items as different, by design. On production it lists nothing.

Where does the content go?

The content that the templates and the brand depend on goes into seed files. That means the front page, the menus, the redirects, the landing pages and their sections, every project and testimonial, every Spanish translation, and the artwork. A script writes the seeds from the live site. It walks each content type's fields, so a field added tomorrow is exported and seeded without changing the script. Nothing in a seed carries a database id: a page is found by its type and title, an image by its name.

Each value has an owner:

  • The code owns the media, menus, redirects and front page. The seeder restores them whenever they differ.
  • An editor owns the copy once it exists. The seeder fills it while it is empty and never overwrites it. A rewrite made on production comes back to the repository the next time the seeds are pulled.

The seeder runs on every deploy, and a second run changes nothing. Some things never go into a seed, on purpose: what visitors told the site (contact leads and the concierge's conversations) and photographs of real people. Those stay in the database and its backups.

Why do we never write update hooks?

Drupal lets a module ship an update hook: code that runs once per site and is then marked done forever. We don't use it in any module we write, for three reasons:

  • It cannot repair. If a term or a menu item it created is deleted by accident, it never comes back, because the hook has already run.
  • It goes stale. It covers what existed on the day it ran.
  • Every correction needs another hook. Our own site had collected eleven deploy hooks before they were replaced by seeders on 2 October 2026.

The rule is enforced, not advised. Our public starter records it as decision D25, and its test gate fails on any update, post-update or deploy hook, so a site cannot build an image that carries one. Drupal core's and contributed modules' own updates still run on every deploy: that is the first step of drush deploy.

How do you prove it?

You build the site from nothing and compare it with production, entity by entity. Reading the seed files proves nothing. Our audit script does it on a local copy and never writes to production:

  1. Fill a local copy from production and export the seeds from it.
  2. Run the seeder on that copy. Nothing may change.
  3. Drop the database, install from the saved configuration, deploy, and seed twice (the second run must change nothing). Then compare the result with production. References are compared by what they point at, and images by a hash of their content.

The first run, on 30 September 2026, failed. The hand-written seeders of the time had passed every check anyone had thought to run, and the build did not even finish installing. The run showed what had lived only in production:

  • the front page;
  • the landing pages' sections;
  • every Spanish translation outside the projects;
  • the testimonials' copy;
  • ten pieces of artwork that existed only in production's files folder;
  • the menus and the redirects.

All of it went into the seeds. On 2 October 2026 the audit was re-run on a fresh copy of production, and the build from the repository equalled production field for field.

What does it not prove?

  • It is not a backup. A rebuild reproduces the structure and the content the site depends on. On a site where editors publish every day, their articles come back from a database backup, and only a restore proves that backup works. Our restores are drilled separately (first full drill: 18 September 2026).
  • It is dated. It proves the repository matched production on 2 October 2026. A site changes every week, so the check is repeated, not framed.
  • It leaves out what should stay out. Leads, conversations and staff accounts are not in git. Neither are the secrets (the database password, the hash salt). Those come back from their own protected copies.
  • It is per site. This is the record for tecnocratica.com.co. We don't claim it for a site until the audit has passed there.

What would we check on your site?

An audit, or the intake that makes a site maintainable, starts with the same questions. We ask them of your repository and of a copy of your site, never of production:

  • Is the configuration in git, and does drush config:status on a copy of production show no differences?
  • Can someone other than the usual person deploy, with one command, from CI?
  • How many update and deploy hooks does your custom code carry, and what do they create?
  • Where do the front page, the menus, the landing sections and the translations come from? If the answer is "the database", a rebuild would lose them.
  • Which files exist only on the server?
  • Has anyone restored your backup? Into what, and when?
  • Where are the secrets written down, and who holds them?

Then we try the rebuild on a copy and write down every difference. That list is the audit's first finding, and it is yours whether or not you hire us for the work. More on how we work and what we offer.

To find out whether your site would survive an empty database, write to us or ask the concierge on the home page. An hour of the founder's time is USD 75.

Sources

Read 28 October 2026.

Drafted with Claude from our own records; checked and approved by Carlos Santana. How we use AI

Want to talk this project through?

Ask the concierge about this project — what it is, how it was built, or get put in touch.