Drupal 8 Theme Header Background Image Upload I'm upgrading the Mayo theme to Drupal 8. For Drupal 7 it worked like this in theme-settings.php with hook_form_system_theme_settings_alter():

  $form['adv_header']['header_bg'] = array(
    '#type' =&gt; 'file',
    '#title' =&gt; t('Upload header background image'),
    '#size' =&gt; 40,
    '#attributes' =&gt; array('enctype' =&gt; 'multipart/form-data'),
    '#description' =&gt; t('If you don\'t jave direct access to the server, use this field to upload your header background image'),
    '#element_validate' =&gt; array('mayo_header_bg_validate'),
  );


element_validate in the above called:

/**
 * Check and save the uploaded header background image
 */
function mayo_header_bg_validate($element, &amp;$form_state) {
  global $base_url;

  $validators = array('file_validate_is_image' =&gt; array());
  $file = file_save_upload('header_bg', $validators, "public://", FILE_EXISTS_REPLACE);

  if ($file) {
    // change file's status from temporary to permanent and update file database
    $file-&gt;status = FILE_STATUS_PERMANENT;
    file_save($file);

    $file_url = file_create_url($file-&gt;uri);
    $file_url = str_ireplace($base_url, '', $file_url);

    // set to form
    $form_state['values']['header_bg_file'] = $file_url;
  }
}


For the Drupal 8 version I have added use Drupal\Core\Form\FormStateInterface; at the top of theme-settings.php and some changes to mayo_header_bg_validate(). So far it looks like this: 

/**
 * Check and save the uploaded header background image
 */
function mayo_header_bg_validate($element, FormStateInterface $form_state) {
  global $base_url;

  $validators = array('file_validate_is_image' =&gt; array());
  $file = file_save_upload('header_bg', $validators, "public://", NULL, FILE_EXISTS_REPLACE);
  if ($file) {
    //change file's status from temporary to permanent and update file database
    $file-&gt;status = FILE_STATUS_PERMANENT;
    //file_save($file);

    $file_url = file_create_url($file-&gt;uri);
    $file_url = str_ireplace($base_url, '', $file_url);

     //set to form
    //$form_state['values']['header_bg_file'] = $file_url;
    $form_state-&gt;setValue(['header_bg_file'], $file_url);

  }
}


It does upload the image to /sites/default/files and put an entry into the file_managed table. file_save() is gone for Drupal 8 and I couldn't find a change log showing how to replace it for Drupal 8. 'header_bg' just shows a value of "/" in mayo.settings.
I suggest you look at ThemeSettingsForm, which is where the code lives for the logo and favicon. Doing something similar there would work.

Note that it doesn't save the file, it just uses the file entity that it gets from the widget and puts it in a permanent place using file_unmanaged_copy().

Make sure to keep the conditional, so that you don't get a fatal error when not providing an image (I assume that's the problem with @dmouse's code example).