Last updated August 30, 2016. Created on March 4, 2014.
Edited by mathieso, siliconmeadow, therealssj, colan. Log in to edit this page.

This article was originally written based on the Spark distribution.

Installation profiles are like modules

Installation profiles in Drupal 8 have all the functionality of modules, including access to hooks and plugins and, critically, the ability to provide configuration for your site in the form of .yml files.

Picking a machine name

First you need a machine name for your profile. This is a name consisting of lowercase letters and underscores only. From here on, all references to profilename imply the profile machine name.

E.g if your profile is for Acme starter kit valid profile machine names would be

  • acme_starter_kit
  • acme_starter
  • acme_starter_profile
  • acme_kit

the following would be invalid

  • acme-starter-kit
  • acme-kit

This is because profiles are just like modules, and hence can implement hooks, - but acme-kit_form_alter would not constitute a valid PHP function name.

Creating the file structure

Your installation profile will reside in its own profilename directory in the /profiles directory of a Drupal 8 site.

All installation profiles must have a file. They may also have:

  1. profilename.profile.
  2. profilename.install file
  3. config folder
  4. translations folder.

When packaged, your installation profile will also have modules, src, and themes directories as needed.

.info.yml file

The file should look similar to this:

name: Profile Name
type: profile
description: 'Description of your profile.'
core: 8.x

# Optional: Declare your installation profile as a distribution
# This will make the installer auto-select this installation profile.
# The distribution_name property is used in the installer and other places as
# a label for the software being installed.
  name: Distribution Name

# Required modules
  - node
  - history
  - block
  - block_content
  - breakpoint
  - color
  - config
  - comment
  - contextual
  - contact
  - quickedit
  - help
  - image
  - options
  - path
  - taxonomy
  - dblog
  - search
  - shortcut
  - toolbar
  - field_ui
  - file
  - rdf
  - views
  - views_ui
  - editor
  - ckeditor

.install file

 * @file
 * Install, update and uninstall functions for the profilename install profile.

 * Implements hook_install().
 * Perform actions to set up the site for this profile.
 * @see system_install()
function profilename_install() {
  // First, do everything in standard profile.
  include_once DRUPAL_ROOT . '/core/profiles/standard/standard.install';
  // Can add code in here to make nodes, terms, etc.

.profile file

The profilename.profile file has access to almost everything a normal Drupal modulename.module file does because Drupal is fully bootstrapped before almost anything in the profile runs.

 * @file
 * Enables modules and site configuration for a standard site installation.

// Add any custom code here like hook implementations.


Configuration files

Drupal 8 installation profiles can contain configuration files. You can start by taking the configuration directory (config folder) of an installed, configured site and copying it into the config/install folder in your profile.

Once that's in place, there are some other required tasks:

  1. Copy all of the modules and themes listed within core.extension.yml into your profile's info file (using the new info file's format).
  2. Delete core.extension.yml (and possibly some other config files).
  3. Remove all of the UUIDs from your config files so that they don't conflict with those of new sites. This can be done quite easily on the command line like so all on one line:
    find /path/to/PROFILE_NAME/config/install/ -type f -exec sed -i '/^uuid: /d' {} \;

If you just want to grab an existing site's configuration, and don't need to end up with a formal installation profile (for sharing on, for example), you can use the Configuration Installer installation profile to install a new site from the configuration of another site.

Default content

You can also include default content by making default_content a dependency of your installation profile and using it to import JSON-formatted content.

Configuration that needs content to work is possible by putting content (and configuration as needed) in modules you make (which your profile can depend on) that themselves depend on default_content.

Looking for support? Visit the forums, or join #drupal-support in IRC.


Berdir’s picture

st() no longer exists in Drupal 8, just use t().

I recommend using for creating default content in 8.x. But note that happens after hook_install(), so you can't refer to it there. I recommend to put it in a separate module that the install profile depends on.

loophole080’s picture

I'm fiddling with D8 profile building and figured I'd leave some feedback here as the docs are a little on the sparse side! :-) So...

Observation 1:

"All installation profiles must have:

This is not actually true as of D8 beta 15, all you really need is the info.yml file (the Minimal profile doesn't even have a .profile file!).

Observation 2:

Be aware that whatever you specify in the info.yml file will become fixed dependencies. In other words it is not possible to disable module declared by a profile's info.yml file. (So if you build from Standard profile you will be stuck with Tour, History, etc, which may not be desirable.) I'm looking into how to install modules in the profile without them being requirements.

Observation 3:

Most of the config work can be done using the new config management system (i.e. via .yml settings files - which is way easier than having to figure out a variety of different hooks to apply things like roles, permissions, module settings etc.) although you can also use hooks in the .install file. Standard profile seems to use a mix of both, not sure why but will report back findings here.

Observation 4:

Missing/unknown (or mis-spelled) modules in the info.yml cause a fatal error: "Fatal error: Call to a member function getPath() on a non-object in /[MY_SITES]/[DRUPAL_ROOT]/core/includes/ on line 920".

In other words, missing modules are not handled very elegantly and so be careful to watch for dependencies and typos. (Contrast to D7 profiles, where any missing/unknown modules would be flagged up as missing on the Verify Requirements page of the installer, much more forgiving!)

However, if a module requires another module (e.g. Views UI requires Views) and you have not listed it, the installer will automatically enable the required modules, as per normal.

Observation 5:

Right, this one had me puzzled for a while... While you can enable the devel module and kint submodule (to get debug output) to inspect the Configure Site form, don't bother, it can't handle the form!

Because of the regional settings the form array is RIDICULOUSLY big.... When I finally resorted to print_r() it outputs 138154 lines! Almost all of which is in ['regional_settings']['default_country']['#options'] and ['regional_settings']['date_default_timezone']['#options'].

I will post an abbreviated form output on my blog soon, in the meantime if you want to start messing with the Configure Site form, stick this in your .profile file (and set aside a few hours for scrolling!):

 * @file
 * Enables modules and site configuration for the MY_PROFILE site installation.

use Drupal\Core\Form\FormStateInterface;

 * Implements hook_form_FORM_ID_alter() for install_configure_form().
 * Allows the profile to alter the site configuration form.
function MY_PROFILE_form_install_configure_form_alter(&$form, FormStateInterface $form_state) {
// this print_r() will break the installer, so just use it for development when reloading the configure site form
  echo '
  echo '


Observation 6

We're back on familiar ground tweaking the installer forms as the Form API is essentially the same as in D7 (phew!). The two hooks you may need are MY_THEME_form_install_settings_form_alter() and MY_THEME_form_install_configure_form_alter(). I use my profile for iterative development, so it gets auto-installed repeatedly (using Behat/Selenium) and it's nice to have default values filled in on these forms, I also like to clear up some of the superfluous description text and change some headings.

If you decide you want to preset any of the values of the installer forms using the above alter hooks here's a couple of gotchas:

a) the settings form (aka "Database Configuration") needs to take plain old strings, you should not use t() for these values (the script that builds the settings.php file doesn't like translation)
b) unlike in D7, you can't set the value for password fields, which I assume is a security feature

Observation 7

OK we also still have the hook_install_tasks() and hook_install_tasks_alter() (which should live inside your .install file), which work similarly to in D7. However, we need to build any custom forms using the new D8 Form API Symphony-esque way of doing this (good, quick how-to here: and then use that form's proper class name in the tasks array, this will automagically load the form.

For example, in your .install file:

function MY_PROFILE_install_tasks(&$install_state) {
  // prepare to add extra install task steps for custom configuration
  $tasks = array();
  // add our custom configuration form to the install tasks  
  $tasks['Drupal\MY_PROFILE\Form\CustomConfigForm'] = array(
    'display_name' => t('Custom Config for MY_PROFILE'), 
    'type' => 'form',  

This would load a corresponding form of class "CustomConfigForm" which would live in the file MY_PROFILE/src/Form/CustomConfigForm.php and look something like this:

 * @file
 * Contains \Drupal\MY_PROFILE\Form\CustomConfigForm  
namespace Drupal\MY_PROFILE\Form;
use Drupal\Core\Form\FormBase;
use Drupal\Core\Form\FormStateInterface;
 * Implements custom config form for MY_PROFILE 
class CustomConfigForm extends FormBase {
  public function getFormId() {
    return 'custom_config_form';
  public function buildForm(array $form, FormStateInterface $form_state) {
    // build your form here to do custom stuff
    return $form;
  public function submitForm(array &$form, FormStateInterface $form_state) {
    // custom submit handler code here
mathieso’s picture

Thanks for the detailed info, loophole080. Much appreciated!

Has anything changed since you wrote this?

BTW - you mentioned your blog. Did you end up writing more on D8 installation profiles? Could you share a link?

Final thought. Maybe there could be a short PacktPub (or other publisher) book on D8 profiles? Might not be a big market, though.

jepSter’s picture

Creating a Drupal installation profile from scratch with a couple of advices in the documentation is tricky. There're appearing errors which are pointing to generic getValue() methods and such.

I've copied the standard installation profile, changed the machine name and the method names like for PROFILENAME_install() etc. and worked up from it. Also the minimal profile could be a good starting point. So I recommend to "fork" the profiles from core. They all most community proven at all and the best framework for the task of own profile creation.

dman’s picture

A gotcha:
If you choose to start with:

"// First, do everything in standard profile."

You may need to ensure that *all* your loaded core modules also include the ones that 'standard' started with.

If, for example, you wanted to start your own build *without* using 'comment' module all the time ... then standard_install() will fail, as it attempts to preconfigure a parameter that only applies to 'comment's

It's just as good for you to COPY the setups in standard_install() into your own hook_install() and start pruning things out from there instead of inheriting it.

nevergone’s picture

Remove _core from configuration files? ex.: default_config_hash

useful command:
shopt -s globstar; ls /path/to/PROFILE_NAME/config/install/**/*.yml | xargs gawk -i inplace '/^\S/ {core=0} /^_core/ {core=1} !core'

useful link:

Thanks chx!