An approach for keeping content that is added as part of development synchronized with test and production environments is to use the Default Content module to export the content. It's built for the content to be exported to an installation profile's 'content' folder, and then the module, if enabled, automatically brings the content in when the site is installed. Presuming that reinstalling the site would be a drastic step for getting one block's content (aside: we do find it useful to use installation profiles with default content for automated testing and initial development), it's also possible to import the content one item at a time, such as in an update hook, with the below code in your example.install or example.profile:
<?php
/**
* Import a piece of content exported by default content module.
*/
function example_import_default_content($path_to_content_json) {
list($entity_type_id, $filename) = explode('/', $path_to_content_json);
$p = drupal_get_path('profile', 'guts');
$encoded_content = file_get_contents($p . '/content/'. $path_to_content_json);
$serializer = \Drupal::service('serializer');
$content = $serializer->decode($encoded_content, 'hal_json');
global $base_url;
$url = $base_url . base_path();
$content['_links']['type']['href'] = str_replace('http://drupal.org/', $url, $content['_links']['type']['href']);
$contents = $serializer->encode($content, 'hal_json');
$class = 'Drupal\\'. $entity_type_id . '\Entity\\'. str_replace('', '', ucwords(str_replace('_', '', $entity_type_id)));
$entity = $serializer->deserialize($contents, $class, 'hal_json', array('request_method'=> 'POST'));
$entity->enforceIsNew(TRUE);
$entity->save();
}
?>
Export a custom block with an ID of 8:
drush dcer block_content 8
And used in your example.install file:
<?php
/**
* Add the footer block content.
*
* Implements hook_update_N().
*/
function example_update_8001() {
example_import_default_content('block_content/136efd63-021e-42ea-8202-8b97305cc07f.json');
}
?>
See also Drupal 8 default content notes.