One-click content export, staging and deployment strategy for Drupal 9 websites (with Version Control of Content!)

Tanay Sai
Globant
Published in
7 min readAug 24, 2021

Drupal provides a good way to manage configuration via code. That way, your configuration is version controlled. However, the same is not the case with content in Drupal (at least, out of the box).

Content is saved directly to the database in Drupal. And what that means is that, when you want to preview your development or staging environment with Drupal, then you will need to pull a database copy from your production environment and load it in your development or staging environment.

As such, content is not version controlled. And previewing content in a lower environment before publishing it on production poses a challenge. (You can still put the content in draft state in the production environment, and preview it without publishing. However there are multiple reasons due to which many websites choose to put the content on a staging environment first, before it is sent to the production environment (even in unpublished state).

Some of those reasons that prompt some websites to put the content in lower environments first, before putting the same on production:

Previewing content in draft mode is not always equivalent to ‘previewing’ published content.

Since draft content can be previewed only as an authenticated user with required permissions, it may not look exactly the same as how it would once it is published and when viewed as an anonymous user. This is because, as a logged in user, you would often have blocks, administrative tools like the toolbar on your Drupal page, and the screen real estate may not be the same as what it would be once published.

Workspaces, which is an experimental core module in Drupal 9 now, might solve some of this.

Risk of accidental publication

Some websites would not want to have the content, even in unpublished state, on the production environment. This is especially true when the cost of an accidental publication is very high. (Example: publishing quarterly results of a publicly traded company a few seconds before its intended publication time, could be equivalent to insider trading!)

Ease of Review

When there are many stakeholders from multiple departments involved in the review process, it is easier to have the draft content on a URL/domain that is accessible only via the company’s VPN, without requiring all the stakeholders to login with their Drupal credentials.

And more..

We are not diving deeper into the reasons for why many sites would want to deploy content on a lower environment before it is deployed to production. But the above few reasons give you a gist of why.

Now, for one of the above, or for different reasons, you have chosen a content deployment strategy that would require the content to be published in a lower (development or staging) environment first. This creates the challenge of how to deploy this content from a lower environment to production.

You can’t move your lower environment database to production as it would overwrite any user-generated content from the recent times on the production environment. Further, there may be test content on lower environments that you would not want to move to production.

That is where the need for content staging comes in.

There is no one-size-fits-all content staging solution available for Drupal. Below, we will touch some options available. And my thoughts about each of those solutions, and we will discuss in detail a solution that we think makes a great case.

The below is a small list of solutions we triaged while researching for a project. A detailed list of all possible solutions is available here.

Deploy — Content Staging

Link — https://www.drupal.org/project/deploy

This was the first one that popped up when we were looking for a solution. We did not explore this module further due to the following reasons:

2. Content Sync Module
Link — https://www.drupal.org/project/content_sync

This might actually be a good solution. And comparable to the one that we picked in the end. We did not explore this further based on my first impressions about the module that it

  • Lacks a recent release and maintenance
  • Per project page, seems to lack ability to define content to be selected. Only options looks to be either a single piece of content, or all content

3. Entity Share

Link — https://www.drupal.org/project/entity_share

This module looks to be a real deal! A well maintained project page and documentation. A very recent 8.x/9.x release. Supports most of the field types. Could be a solution that fits most Drupal projects. Again, we skipped it because one of my focus criteria was trying to get the content to be version controlled as well, and not just moving content across environments). Else, this one is a great fit, if you are not very specific about version control of content.

4. Other options

Few other options that caught my attention include CMS Content Sync module that requires a paid SAAS service subscription, Acquia Content Hub (requires subscription), Entity Pilot (requires subscription). Content Synchronizer is yet another option. The setup, import and export process looks fairly complicated from the documentation. And also the export option looks limited to zip file which might not help with version control.

5. Default Content Module

Link — https://www.drupal.org/project/default_content

This is the module we happened to pick for our implementation. The module is actually intended for a one-time push of content that can be packaged into a module, so that the content is created when the module is enabled. Out of the box, with the latest stable version, the module throws an exception when you try to re-enable the module to import updated content that is packaged into the module. (as the content with the same UUID has already been imported in the past).

However, this module can be tweaked further to update existing content on further runs. Using patch #145 from this issue.

Tested the module on a content type that has a good number of varied type of fields, including image and file attachments, paragraphs (nested, with reference to other paragraphs), and it handled the import and export seamlessly.

Workflow

A workflow for using the module could look like this:

  • A custom module to hold content is created and checked into your codebase.
  • Content can be updated on any local or cloud environments
  • Content is packaged into the module using drush commands.

drush dcer node 2 — folder=modules/custom/cm1_install_content/content

2 is the node id of the node we are exporting.
The path indicates the module’s path until the content folder inside it.

  • This creates the required yml files in the folder of the module. Note that the folder for content should be named exactly as “content” as this is what the importer in the module expects.
  • Note that when only one node was exported, the module bundled all the dependencies of the node, like the terms, the paragraphs, referenced nodes, the file attachments, etc,
  • This module can now be checked into your repository.
  • In any other environment, you can do a git pull, and enable the module to get all the content.
  • The content can be updated and re-exported in any environment using the Drush commands, to update the module’s yml files.
  • In another environment where the updated content needs to be imported, you can either enable the module (if first time), or run the below command.

drush dcim cm1_install_content

  • Note that dcim command (and ability to update existing content) is not part of the stable release, and you would either need to fork the module from the issue branch @ https://git.drupalcode.org/issue/default_content-2698425 or wait for the issue to be merged into the stable release of the module.

This solution checks of all boxes that we were looking for in a content staging solution -

  • Allows moving content from one environment to another
  • Allows version control of content
  • Supports most (if not all) field types, including paragraphs to start with
  • Supports translation (Caveat)
  • Supports updation of content and not just initial import (Work in progress, to be added to stable branch soon)
  • Recursively picks all dependency content required

Further, exporting all content one by one by node-ids remains a painful task. The module allows a list of UUIDs to be defined in the module’s info file, so it can automatically pick the ids and export the content each time. Although, requiring a commit every time a node is to be added, is something we are not very comfortable with.

So, we built a Views Bulk Operation Action, that allows you to add the export button to any Drupal view, so you can select the content that you want using the filters on the view, and hit the export button. We published this as a contrib module available to all @ https://www.drupal.org/project/export_action_for_default_content.

Configuring the view:

View showing the custom action:

Updated — Content Management Workflow — with one click export of content

With the above setup, the workflow is now as simple as:

  1. Create/Update content and Export the content from any environment to a module using a view.
  2. Push the module code to the repo
  3. Once code is deployed, just run drush dcim <content module name> command on the environment
  4. And boom!, you have all your content!

--

--