Git workflow for managing Drupal 8 configuration

One of the new key features of Drupal 8 is the possibility to deal with configuration in code. Since configuration is now in text files, we want to put it under version control in Git to enjoy the many advantages this brings: comparing configuration states, keeping a history of configuration changes and even moving configuration between sites.

Setup

We will assume that you have a development version of Drupal 8, git and drush available on your system.
You can set up your Drupal git repository in several ways. One of them is outlined in Building a Drupal site with Git on drupal.org. The document is written for Drupal 7, but can easily be adapted for Drupal 8.
Another, probably simpler method is to simply download a Drupal 8 (alpha) release and initialise a new repository with it.

In either case you should copy example.gitignore to .gitignore and adapt it to your needs to prevent settings.php and the files directory from being versioned.

The next step is to make sure that a configuration directory is versionable. By default Drupal 8 will place the staging directory under sites/default/files and it is considered a good practice to not version that location, but an alternative location can easily be specified in settings.php:

<?php$config_directories['staging'] = 'config/staging';?>

It is also possible and even advisable to specify a directory outside of the web root of course. In that case you would put the parent directory of your web root where drupal is under version control and use ../config/staging. We will later see that it is also possible to add more directories and keys to the $config_directories variable.

Because the configuration management of Drupal 8 only works between different instances of the same site, the different instances of the site need to be cloned. Cloning a Drupal 8 site is done the same way as cloning a Drupal 7 site. Just dump the database of the site to clone and import it in the other environment.

Development

After cloning your site you can go ahead and start configuring your site.
Once the part of the configuration you were working on is done the whole configuration of the site needs to be exported.

Next, you need to merge the work of other developers. In some cases it may be enough to simply use git pull, otherwise the configuration has to be merged after it has been committed:

Add all configuration to git and commit it.

Use git pull (or git fetch and git merge) and resolve any conflicts if necessary.

Git can merge changes in text files quite well, but git does not know about Drupal and its yaml format for configuration. It is, therefore, important to verify that the merged configuration makes sense and is valid. In most cases it will probably not be an issue and just work, but it is always better to be vigilant and be on the safe side. So, after merging, you should always run:

local$ drush config-import staging

If the import went smooth you can push the configuration to the remote repository. Otherwise the configuration needs to be fixed first.

Deployment

The simplest case is when the configuration on the production site has not been changed. There is an interesting Configuration Read-only mode module that can enforce this.

If the configuration did not change deploying the new configuration is simply:

remote$ git pullremote$ drush config-import staging

If the configuration changes on the production site, it is best to frequently export the live configuration into a dedicated directory.
Add a new config directory in settings.php:

<?php$config_directories['to_dev'] = 'config/to_dev';?>

remote$ drush config-export to_dev -y

Add, commit and push it to the production branch so that the developers can deal with it and integrate the changes into the configuration which will be deployed next. Exporting the configuration into a dedicated directory rather than the staging directory avoids the danger that merge conflicts happen on the production site.
The deployment to the production site should be kept hassle free, so it should always be safe to pull from git and import the configuration without the risk of a conflict.

Important notes

It is important to firstexport the configuration changes and thenpull changes from collaborators because the exporting action wipes the directory and re-populates it with the active configuration. Since everything is in git, you can recover from such a mistake without much difficulty but why make your life complicated.

Import the configuration beforepushing it to the remote repository. Broken configuration breaks the site, be a nice co-worker.

Git doesn't solve everything! Imagine Alice and Bob start with the same site, it has one content type and among others an "attachment" field. Alice deletes the attachment field, exports the configuration and pushes it to git. In the meantime, Bob creates a new content type and adds the attachment field to it. Bob exports his configuration, merges Alice's configuration changes without a problem (the changes are separate files) and imports the merged configuration. The attentive reader sees where this leads. The commit of Alice deletes the field storage for the attachment field, but Bob added a field instance which depends on the field storage.
The exported configuration now contains a field instance that can't be imported.
At the time of writing, drush will signal a successful import but doesn't actually import it while the UI is more helpful and complains that the attachment field instance was not imported due to the missing field storage.

In order to be able to version the config directory outside of the web root, the web root needs to be a sub directory of the git root.
In the post I mentioned that the git root must be the parent directory of the web root, its the same just formulated differently. I'm sorry if it wasn't clear.

In order to be able to version the config directory outside of the web root, the web root needs to be a sub directory of the git root.
In the post I mentioned that the git root must be the parent directory of the web root, its the same just formulated differently. I'm sorry if it wasn't clear.

In order to be able to version the config directory outside of the web root, the web root needs to be a sub directory of the git root.
In the post I mentioned that the git root must be the parent directory of the web root, its the same just formulated differently. I'm sorry if it wasn't clear.

Instead of Importing & Exporting the configuration, is it acceptable to simply save the config in a directory that git is tracking (i.e. not in the 'files' directory)? Then you could simply use different branches in Git to manage the differences in configuration?

Instead of Importing & Exporting the configuration, is it acceptable to simply save the config in a directory that git is tracking (i.e. not in the 'files' directory)? Then you could simply use different branches in Git to manage the differences in configuration?

Instead of Importing & Exporting the configuration, is it acceptable to simply save the config in a directory that git is tracking (i.e. not in the 'files' directory)? Then you could simply use different branches in Git to manage the differences in configuration?

If by saving the configuration in a folder you mean having the active store on disk rather than in the database then I would not advise this at all. It is important to realize that configuration changes should always be imported through the API to allow Drupal to update database tables for example.
See the relevant issue on drupal.org: https://www.drupal.org/node/2161591

If you mean as an alternative to the to_dev folder you manage it in different git branches of the same folder, then the answer is yes you may certainly do that. The configuration folder can be anywhere you want it to be if you make sure it is safe from prying eyes. Different branches with the same folder allows for easier merging, but it requires more vigilance when deploying your configuration if you allow configuration changes to happen on the production site. To avoid conflicts you would thus switch to a 'to_dev' branch, export the configuration, switch to the 'from_dev' or 'master' branch, import the configuration you have been working on.

If by saving the configuration in a folder you mean having the active store on disk rather than in the database then I would not advise this at all. It is important to realize that configuration changes should always be imported through the API to allow Drupal to update database tables for example.
See the relevant issue on drupal.org: https://www.drupal.org/node/2161591

If you mean as an alternative to the to_dev folder you manage it in different git branches of the same folder, then the answer is yes you may certainly do that. The configuration folder can be anywhere you want it to be if you make sure it is safe from prying eyes. Different branches with the same folder allows for easier merging, but it requires more vigilance when deploying your configuration if you allow configuration changes to happen on the production site. To avoid conflicts you would thus switch to a 'to_dev' branch, export the configuration, switch to the 'from_dev' or 'master' branch, import the configuration you have been working on.

If by saving the configuration in a folder you mean having the active store on disk rather than in the database then I would not advise this at all. It is important to realize that configuration changes should always be imported through the API to allow Drupal to update database tables for example.
See the relevant issue on drupal.org: https://www.drupal.org/node/2161591

If you mean as an alternative to the to_dev folder you manage it in different git branches of the same folder, then the answer is yes you may certainly do that. The configuration folder can be anywhere you want it to be if you make sure it is safe from prying eyes. Different branches with the same folder allows for easier merging, but it requires more vigilance when deploying your configuration if you allow configuration changes to happen on the production site. To avoid conflicts you would thus switch to a 'to_dev' branch, export the configuration, switch to the 'from_dev' or 'master' branch, import the configuration you have been working on.

Ideally you would not allow editing the configuration on the production site at all and this problem would be solved. However, there might cases in which this is not very practical. In those cases you will want to get the configuration that has been changed to the developers. You could for example export the production configuration into a folder that is outside of the git root of your site and version it independently.

To answer the question about having to import the configuration on all sites: yes you have to, this is one of the takeaways of this blog post. While it is possible to change the storage for the active configuration store to files on the disk, you would never edit those files directly for all the reasons you would find in the issue I posted in the other comment: https://www.drupal.org/node/2161591

Ideally you would not allow editing the configuration on the production site at all and this problem would be solved. However, there might cases in which this is not very practical. In those cases you will want to get the configuration that has been changed to the developers. You could for example export the production configuration into a folder that is outside of the git root of your site and version it independently.

To answer the question about having to import the configuration on all sites: yes you have to, this is one of the takeaways of this blog post. While it is possible to change the storage for the active configuration store to files on the disk, you would never edit those files directly for all the reasons you would find in the issue I posted in the other comment: https://www.drupal.org/node/2161591

Ideally you would not allow editing the configuration on the production site at all and this problem would be solved. However, there might cases in which this is not very practical. In those cases you will want to get the configuration that has been changed to the developers. You could for example export the production configuration into a folder that is outside of the git root of your site and version it independently.

To answer the question about having to import the configuration on all sites: yes you have to, this is one of the takeaways of this blog post. While it is possible to change the storage for the active configuration store to files on the disk, you would never edit those files directly for all the reasons you would find in the issue I posted in the other comment: https://www.drupal.org/node/2161591

I have started editing the d.o. documentation on Building a Drupal site with Git (linked above) for D8, taking inspiration from this article. If anyone more experienced than me, maybe someone involved in giving or attending this training, could check and improve my work, that would be great.

I have started editing the d.o. documentation on Building a Drupal site with Git (linked above) for D8, taking inspiration from this article. If anyone more experienced than me, maybe someone involved in giving or attending this training, could check and improve my work, that would be great.

I have started editing the d.o. documentation on Building a Drupal site with Git (linked above) for D8, taking inspiration from this article. If anyone more experienced than me, maybe someone involved in giving or attending this training, could check and improve my work, that would be great.

Sure, thank you for using these materials as source for the drupal.org documentation and we'll take a look at it and update it with some extra tips we suggested during our training. We're still refining them and possibly we'll publish another blog post based on the training and on the feedback we received; and then we'll update drupal.org too.

Sure, thank you for using these materials as source for the drupal.org documentation and we'll take a look at it and update it with some extra tips we suggested during our training. We're still refining them and possibly we'll publish another blog post based on the training and on the feedback we received; and then we'll update drupal.org too.

Sure, thank you for using these materials as source for the drupal.org documentation and we'll take a look at it and update it with some extra tips we suggested during our training. We're still refining them and possibly we'll publish another blog post based on the training and on the feedback we received; and then we'll update drupal.org too.

The config-export create nice yaml files for all kind of interesting things like content types, fields and views.
Is there a way to pull these yaml files into the config/install directory of a new module, so that when that module is installed is creates the content type/fields/views according to those yaml file?

The config-export create nice yaml files for all kind of interesting things like content types, fields and views.
Is there a way to pull these yaml files into the config/install directory of a new module, so that when that module is installed is creates the content type/fields/views according to those yaml file?

The config-export create nice yaml files for all kind of interesting things like content types, fields and views.
Is there a way to pull these yaml files into the config/install directory of a new module, so that when that module is installed is creates the content type/fields/views according to those yaml file?

There is no mention of the hash suffix in the sites/default/files/config folder filename e.g. config_wNOLcmycPFZCrXJ9wis9dCdSR4lpYILdBsFxSWuK5Hzhcr

Should the config folder be committed like this? Would it not make it difficult to do merges of the commit to other site code bases if one wanted to put the config made here into another site via code?

"Add all configuration to git and commit it." is mentioned but there is no example of what git would outputs after doing the drush config-export staging command. i.e. how does the listing of the changed the files look that git would output when a git status command is issued.

There is no mention of the hash suffix in the sites/default/files/config folder filename e.g. config_wNOLcmycPFZCrXJ9wis9dCdSR4lpYILdBsFxSWuK5Hzhcr

Should the config folder be committed like this? Would it not make it difficult to do merges of the commit to other site code bases if one wanted to put the config made here into another site via code?

"Add all configuration to git and commit it." is mentioned but there is no example of what git would outputs after doing the drush config-export staging command. i.e. how does the listing of the changed the files look that git would output when a git status command is issued.

There is no mention of the hash suffix in the sites/default/files/config folder filename e.g. config_wNOLcmycPFZCrXJ9wis9dCdSR4lpYILdBsFxSWuK5Hzhcr

Should the config folder be committed like this? Would it not make it difficult to do merges of the commit to other site code bases if one wanted to put the config made here into another site via code?

"Add all configuration to git and commit it." is mentioned but there is no example of what git would outputs after doing the drush config-export staging command. i.e. how does the listing of the changed the files look that git would output when a git status command is issued.

You can customize the config folder path and remove the hash if you wish, see the documentation within settings.php to learn how to that and to get some information on why a hash is appended by default (there are security reasons for it and you need to ensure that everything is still properly secured if you remove the hash).

About the git output: you will get a standard git output. In other words, git status and git diff will work exactly as they would work on generic text files: configuration is simply text to git, after all.

You can customize the config folder path and remove the hash if you wish, see the documentation within settings.php to learn how to that and to get some information on why a hash is appended by default (there are security reasons for it and you need to ensure that everything is still properly secured if you remove the hash).

About the git output: you will get a standard git output. In other words, git status and git diff will work exactly as they would work on generic text files: configuration is simply text to git, after all.

You can customize the config folder path and remove the hash if you wish, see the documentation within settings.php to learn how to that and to get some information on why a hash is appended by default (there are security reasons for it and you need to ensure that everything is still properly secured if you remove the hash).

About the git output: you will get a standard git output. In other words, git status and git diff will work exactly as they would work on generic text files: configuration is simply text to git, after all.

I have an issu with the drush config-import command. I have two differents environments defined as Drupal 8 - multiiste item. sites/local.mysite.com and sites/staging.mysite.com When I execute the config export command in first environment, everything is fine. When I try to import the config through the import command, I have an error message.

Drupal\Core\Config\ConfigImporterException: There were errors validating the config synchronization. in Drupal\Core\Config\ConfigImporter->validate() (line 730 of [error] /Users/fabrer/Workspace/mvb/application/core/lib/Drupal/Core/Config/ConfigImporter.php). The import failed due for the following reasons: [error] Entities exist of type <em class="placeholder">Shortcut link</em> and <em class="placeholder"></em> <em class="placeholder">Default</em>. These entities need to be deleted before importing.

I have an issu with the drush config-import command. I have two differents environments defined as Drupal 8 - multiiste item. sites/local.mysite.com and sites/staging.mysite.com When I execute the config export command in first environment, everything is fine. When I try to import the config through the import command, I have an error message.

Drupal\Core\Config\ConfigImporterException: There were errors validating the config synchronization. in Drupal\Core\Config\ConfigImporter->validate() (line 730 of [error] /Users/fabrer/Workspace/mvb/application/core/lib/Drupal/Core/Config/ConfigImporter.php). The import failed due for the following reasons: [error] Entities exist of type <em class="placeholder">Shortcut link</em> and <em class="placeholder"></em> <em class="placeholder">Default</em>. These entities need to be deleted before importing.

I have an issu with the drush config-import command. I have two differents environments defined as Drupal 8 - multiiste item. sites/local.mysite.com and sites/staging.mysite.com When I execute the config export command in first environment, everything is fine. When I try to import the config through the import command, I have an error message.

Drupal\Core\Config\ConfigImporterException: There were errors validating the config synchronization. in Drupal\Core\Config\ConfigImporter->validate() (line 730 of [error] /Users/fabrer/Workspace/mvb/application/core/lib/Drupal/Core/Config/ConfigImporter.php). The import failed due for the following reasons: [error] Entities exist of type <em class="placeholder">Shortcut link</em> and <em class="placeholder"></em> <em class="placeholder">Default</em>. These entities need to be deleted before importing.

Hi, this blog post is only about the proper usage of git to store and transfer the configuration. The error you are getting is at a lower level (the level of Configuration Management itself) and http://drupal.org is the appropriate place for these support requests.

Hi, this blog post is only about the proper usage of git to store and transfer the configuration. The error you are getting is at a lower level (the level of Configuration Management itself) and http://drupal.org is the appropriate place for these support requests.

Hi, this blog post is only about the proper usage of git to store and transfer the configuration. The error you are getting is at a lower level (the level of Configuration Management itself) and http://drupal.org is the appropriate place for these support requests.

Hello Greg
Glad to hear that our blog post has inspired you! However, your approach is missing a key ingredient importing config through the api. When importing configuration happens through the api drupal takes care of two very important things: Drupal adapts the database schema (it creates the database tables for new fields etc) and it validates the configuration before importing. So your workflow only works for a single-developer team. If you want to skip the conscious "drush config-export" you can use https://www.drupal.org/project/config_tools

Hello Greg
Glad to hear that our blog post has inspired you! However, your approach is missing a key ingredient importing config through the api. When importing configuration happens through the api drupal takes care of two very important things: Drupal adapts the database schema (it creates the database tables for new fields etc) and it validates the configuration before importing. So your workflow only works for a single-developer team. If you want to skip the conscious "drush config-export" you can use https://www.drupal.org/project/config_tools

Hello Greg
Glad to hear that our blog post has inspired you! However, your approach is missing a key ingredient importing config through the api. When importing configuration happens through the api drupal takes care of two very important things: Drupal adapts the database schema (it creates the database tables for new fields etc) and it validates the configuration before importing. So your workflow only works for a single-developer team. If you want to skip the conscious "drush config-export" you can use https://www.drupal.org/project/config_tools

I am running into some problems though. I tried changing my configuration directories during a clean install. I'm trying to change them to a location outside of the root, like you suggested. However, I keep on getting an error message and I'm unable to proceed with the Drupal install.

Am I suppose to change them after I have already installed Drupal with the default settings?

I am running into some problems though. I tried changing my configuration directories during a clean install. I'm trying to change them to a location outside of the root, like you suggested. However, I keep on getting an error message and I'm unable to proceed with the Drupal install.

Am I suppose to change them after I have already installed Drupal with the default settings?

I am running into some problems though. I tried changing my configuration directories during a clean install. I'm trying to change them to a location outside of the root, like you suggested. However, I keep on getting an error message and I'm unable to proceed with the Drupal install.

Am I suppose to change them after I have already installed Drupal with the default settings?

Great article thanks. A lot has happened since you wrote the article. In an effort to simplify thing the 'staging' directory has been depricated, see https://www.drupal.org/node/2487588 .

So now instead it seems that best practice is to set the name to sync directory $config_directories['sync'] = 'config/sync'; since the word staging can be confusing, since staging sites are used very frequently. This makes sense IMHO.

In a lot of cases there is only one set of configuration. Per environment exceptions can be set using overrides in settings.php, instead.

Great article thanks. A lot has happened since you wrote the article. In an effort to simplify thing the 'staging' directory has been depricated, see https://www.drupal.org/node/2487588 .

So now instead it seems that best practice is to set the name to sync directory $config_directories['sync'] = 'config/sync'; since the word staging can be confusing, since staging sites are used very frequently. This makes sense IMHO.

In a lot of cases there is only one set of configuration. Per environment exceptions can be set using overrides in settings.php, instead.

Great article thanks. A lot has happened since you wrote the article. In an effort to simplify thing the 'staging' directory has been depricated, see https://www.drupal.org/node/2487588 .

So now instead it seems that best practice is to set the name to sync directory $config_directories['sync'] = 'config/sync'; since the word staging can be confusing, since staging sites are used very frequently. This makes sense IMHO.

In a lot of cases there is only one set of configuration. Per environment exceptions can be set using overrides in settings.php, instead.

I'm using Red Hat/Centos/Fedora based systems.
I use /srv/web_site_name as base directory for every website
+ /srv/web_site_name/www directory, as drupal DocumentRoot
+ /srv/web_site_name/config, this for config export/import should contain sync dir
Hope it helps

I'm using Red Hat/Centos/Fedora based systems.
I use /srv/web_site_name as base directory for every website
+ /srv/web_site_name/www directory, as drupal DocumentRoot
+ /srv/web_site_name/config, this for config export/import should contain sync dir
Hope it helps

I'm using Red Hat/Centos/Fedora based systems.
I use /srv/web_site_name as base directory for every website
+ /srv/web_site_name/www directory, as drupal DocumentRoot
+ /srv/web_site_name/config, this for config export/import should contain sync dir
Hope it helps

I haven't really got my head around using uuid numbers within config and why they even exist. At the moment they "just get in the way" by causing error messages.

For example, this error:
"[error] The import failed due for the following reasons:
Site UUID in source storage does not match the target storage."

- which *can* occur (depending on one's setup), when you run the following commands in your steps above
remote$ git pull
remote$ drush config-import staging

There are steps in the following link that *can* be used to overcome the above error (however, at this stage, they are not working for me, but hope they can be of use to someone else:https://justdrupal.com/drupal-8-reinstall/

I haven't really got my head around using uuid numbers within config and why they even exist. At the moment they "just get in the way" by causing error messages.

For example, this error:
"[error] The import failed due for the following reasons:
Site UUID in source storage does not match the target storage."

- which *can* occur (depending on one's setup), when you run the following commands in your steps above
remote$ git pull
remote$ drush config-import staging

There are steps in the following link that *can* be used to overcome the above error (however, at this stage, they are not working for me, but hope they can be of use to someone else:https://justdrupal.com/drupal-8-reinstall/

I haven't really got my head around using uuid numbers within config and why they even exist. At the moment they "just get in the way" by causing error messages.

For example, this error:
"[error] The import failed due for the following reasons:
Site UUID in source storage does not match the target storage."

- which *can* occur (depending on one's setup), when you run the following commands in your steps above
remote$ git pull
remote$ drush config-import staging

There are steps in the following link that *can* be used to overcome the above error (however, at this stage, they are not working for me, but hope they can be of use to someone else:https://justdrupal.com/drupal-8-reinstall/