diff --git a/client/hosting_client.module b/client/hosting_client.module
index 6261029..c8339ea 100644
--- a/client/hosting_client.module
+++ b/client/hosting_client.module
@@ -580,21 +580,6 @@ function hosting_client_load($nodes) {
 }
 
 /**
- * Implements hook_node_load().
- */
-function hosting_client_node_load($arg) {
-  if (!is_numeric($arg)) {
-    return FALSE;
-  }
-  if ($node = node_load_multiple(array($arg))) {
-    if ($node->type == 'client') {
-      return $node;
-    }
-  }
-  return FALSE;
-}
-
-/**
  * Return a list of users for a given client.
  *
  * @param int|object $node
diff --git a/hosting.api.php b/hosting.api.php
index 97008f4..31fc154 100644
--- a/hosting.api.php
+++ b/hosting.api.php
@@ -357,7 +357,7 @@ function hosting_QUEUE_TYPE_queue($count = 5) {
   global $provision_errors;
 
   drush_log(dt("Running tasks queue"));
-  $tasks = _hosting_get_new_tasks($count);
+  $tasks = hosting_get_new_tasks($count);
   foreach ($tasks as $task) {
     drush_invoke_process('@self', "hosting-task", array($task->nid), array(), array('fork' => TRUE));
   }
diff --git a/package/hosting_package.instance.inc b/package/hosting_package.instance.inc
index 977528a..90a6d12 100644
--- a/package/hosting_package.instance.inc
+++ b/package/hosting_package.instance.inc
@@ -410,3 +410,13 @@ function _hosting_package_temporary_table($ref) {
   return $names[$ref];
 }
 
+/**
+ * Helper to determine if a particular site has a module installed.
+ */
+function _hosting_package_site_has_module($node, $module) {
+  $package = hosting_package_instance_load(array('rid' => $node->nid, 'p.short_name' => $module));
+  if (!is_object($package)) {
+    return FALSE;
+  }
+  return $package->status;
+}
diff --git a/package/hosting_package.module b/package/hosting_package.module
index b035f08..15b1a1b 100644
--- a/package/hosting_package.module
+++ b/package/hosting_package.module
@@ -40,21 +40,6 @@ function hosting_package_node_info() {
 }
 
 /**
- * @todo document this function
- */
-function hosting_package_node_load($arg) {
- if (!is_numeric($arg)) {
-    return FALSE;
-  }
-  if ($node = node_load($arg)) {
-    if (in_array($node->type, array('site', 'platform'))) {
-      return $node;
-    }
-  }
-  return FALSE;
-}
-
-/**
  * Implements hook_permission().
  */
 function hosting_package_permission() {
diff --git a/queued/hosting_queued.drush.inc b/queued/hosting_queued.drush.inc
index 983c45f..08246da 100644
--- a/queued/hosting_queued.drush.inc
+++ b/queued/hosting_queued.drush.inc
@@ -62,7 +62,7 @@ function drush_hosting_queued() {
   variable_set('hosting_queued_process_started', REQUEST_TIME);
 
   watchdog('hosting_queued', 'Started Hosting queue daemon, waiting for new tasks');
-
+  drush_log('Started hosting queue daemon. Waiting for new tasks.', 'ok');
   while (TRUE) {
     try {
       // Should we terminate.
@@ -72,8 +72,13 @@ function drush_hosting_queued() {
       }
 
 
-    // Get some tasks to run
-    if ($tasks = @_hosting_get_new_tasks()) {
+    // Get some tasks to run.
+    if ($tasks = @hosting_get_new_tasks()) {
+
+      drush_log(dt("Found %count tasks in queue. Running...", array(
+        '%count' => count($tasks),
+      )), "notice");
+
       if (lock_acquire('hosting_queue_tasks_running', HOSTING_QUEUE_LOCK_TIMEOUT)) {
         drush_log('Acquired lock on task queue.');
         foreach ($tasks as $task) {
@@ -85,10 +90,8 @@ function drush_hosting_queued() {
           drush_log(dt('Found task to execute. Pausing before execution.'));
           sleep(1);
 
-          watchdog('hosting_queued', 'Running task @nid.', array('@nid' => $task->nid));
-          // Execute the task in the backend
-          drush_invoke_process('@self', 'hosting-task', array($task->nid), array('strict' => FALSE), array('interactive' => TRUE));
-          drush_log(dt('Finished executing task.'));
+          // Execute the task.
+          hosting_task_execute($task, array('interactive' => TRUE));
 
           // Delay for a configurable amount of time.
           $delay = variable_get('hosting_queued_post_task_delay', 0);
diff --git a/resume.hosting.inc b/resume.hosting.inc
index 32fcfb4..32ec519 100644
--- a/resume.hosting.inc
+++ b/resume.hosting.inc
@@ -2,7 +2,7 @@
 
 /**
  * @file
- *   Drush include for the Hosting module's hosting resume command.
+ * Drush include for the Hosting module's hosting resume command.
  */
 
 include_once 'hosting.inc';
@@ -19,7 +19,7 @@ function drush_hosting_resume() {
 
   $node = hosting_context_load(d()->name);
 
-  // fix the old platform first
+  // Fix the old platform first.
   $platform_id = db_query('SELECT nid FROM {hosting_platform} WHERE publish_path = :publish_path', array(':publish_path' => d($old_platform)->root))->fetchField();
   if ($platform_id) {
     hosting_context_register($platform_id, ltrim($old_platform, '@'));
@@ -28,11 +28,11 @@ function drush_hosting_resume() {
     drush_log(dt('Old platform not found in path %path, not setting context %context', array('%path' => d($old_platform)->root, '%context' => ltrim($old_platform, '@'))), 'warning');
   }
 
-  // if the new platform doesn't exist, create it.
+  // If the new platform doesn't exist, create it.
   $platform_root = d()->root;
   if (!($platform_id = db_query('SELECT nid FROM {hosting_platform} WHERE publish_path = :publish_path', array(':publish_path' => $platform_root))->fetchField())) {
     drush_log(dt('Platform not found for path %path, adding platform node', array('%path' => $platform_root)));
-    // inherit settings from current platform node
+    // Inherit settings from current platform node.
     $platform = node_load($node->platform);
     $platform->type = 'platform';
     unset($platform->nid);
diff --git a/server/hosting_server.module b/server/hosting_server.module
index 9259e8e..7314748 100644
--- a/server/hosting_server.module
+++ b/server/hosting_server.module
@@ -177,7 +177,7 @@ function hosting_server_node_access($node, $op, $account) {
   }
 
   $type = is_string($node) ? $node : $node->type;
-  if ($type != 'server' ) {
+  if ($type != 'server') {
     return NODE_ACCESS_IGNORE;
   }
 
@@ -775,7 +775,7 @@ function hosting_server_entity_property_info() {
     'verified' => array(
       'label' => t('Last verification time'),
       'description' => t('The date and time of the last verification of the server.'),
-      'type' =>  'date',
+      'type' => 'date',
     ),
     'server_status' => array(
       'label' => t('Server status'),
diff --git a/site/hosting_site.drush.inc b/site/hosting_site.drush.inc
index 88dc891..1faa0d5 100644
--- a/site/hosting_site.drush.inc
+++ b/site/hosting_site.drush.inc
@@ -24,6 +24,12 @@ function hosting_hosting_site_context_options(&$task) {
     $task->options['client_email'] = $user->mail;
   }
   $task->context_options['client_name'] = $client->uname;
+
+  // Set "install_method" property, only if there is one present on the site node.
+  // "profile" is used by default, we don't have to set it here.
+  if (isset($task->ref->install_method) && !empty($task->ref->install_method)) {
+    $task->context_options['install_method'] = $task->ref->install_method;
+  }
 }
 
 /**
diff --git a/site/hosting_site.form.inc b/site/hosting_site.form.inc
index 763d328..c2ef047 100644
--- a/site/hosting_site.form.inc
+++ b/site/hosting_site.form.inc
@@ -102,7 +102,7 @@ function hosting_site_available_options($node, $platform = NULL) {
 
     // Trim down the list of profiles to those that are available and the user has access to
     // XXX This hack (next 22 lines) hides profiles that can't be accessed
-    // Eventually we should lighten up the content of this callback
+    // Eventually we should lighten up the content of this callback.
     $result = db_query("SELECT l.nid
                         FROM {hosting_package_instance} i
                         JOIN {hosting_package} p
@@ -135,7 +135,7 @@ function hosting_site_available_options($node, $platform = NULL) {
       }
       // Open access if no platform access has been set.
       // @todo move this into _hosting_get_allowed_platforms
-      // @todo confirm this logic
+      // @todo confirm this logic.
       elseif (!$unrestricted = db_query("SELECT cid
                                          FROM {hosting_platform_client_access}
                                          WHERE pid = :pid LIMIT 1",
@@ -218,18 +218,18 @@ function hosting_site_form($node, &$form_state) {
   $editable = ((!isset($node->client) || !isset($node->nid)) || user_access('administer sites')) && hosting_feature('client');
   $add_client_text = '';
   if (user_access('administer clients') || user_access('create client')) {
-    $add_client_text = t(' Click !here to add a new client.', array('!here' => l(t('here'), 'node/add/client', array('attributes' => array('target' => '_blank')))));
+    $add_client_text = t('Click !here to add a new client.', array('!here' => l(t('here'), 'node/add/client', array('attributes' => array('target' => '_blank')))));
   }
   _hosting_site_field($form, $node, 'client', array(
     '#type' => 'textfield',
     '#required' => TRUE,
     '#title' => t('Client'),
     '#default_value' => _hosting_client_site_default($node),
-    '#description' => t('The client to whom this site belongs.') . $add_client_text,
+    '#description' => t('The client to whom this site belongs.') . ' ' . $add_client_text,
     '#autocomplete_path' => 'hosting_client/autocomplete/client',
   ), 'filter_xss', $editable);
 
-  // Install profiles
+  // Install profiles.
   $profiles = hosting_get_profiles();
   foreach ($profiles as $id => $name) {
     $profile = hosting_package_instance_load(array('p.nid' => $id));
@@ -264,9 +264,10 @@ function hosting_site_form($node, &$form_state) {
     $selected_profile = hosting_package_instance_load(array('p.nid' => hosting_get_default_profile()))->nid;
   }
 
-  // Load platforms for the selected profile
+  // Load platforms for the selected profile.
   $platforms = hosting_get_profile_platforms($selected_profile);
 
+  $node->profile = $selected_profile;
   $available_options = hosting_site_available_options($node);
   $available_platforms = array_flip($available_options['platform']);
   $platforms = array_intersect_key($platforms, $available_platforms);
@@ -288,7 +289,7 @@ function hosting_site_form($node, &$form_state) {
     natcasesort($form['profile']['#options']);
     reset($form['profile']['#options']);
 
-    // Set page title
+    // Set page title.
     drupal_set_title(t('Create site on platform @name', array('@name' => $platforms[$default_platform])));
   }
   else {
@@ -354,10 +355,17 @@ function hosting_site_form($node, &$form_state) {
   return $form;
 }
 
+
+/**
+ * Ajax callback for the site profile field.
+ */
 function hosting_site_platform_callback($form, &$form_state) {
   return $form['platform'];
 }
 
+/**
+ * Ajax callback for the site platform field.
+ */
 function hosting_site_language_callback($form, &$form_state) {
   return $form['site_language'];
 }
diff --git a/task/hosting_task.api.php b/task/hosting_task.api.php
index fa34820..4233d13 100644
--- a/task/hosting_task.api.php
+++ b/task/hosting_task.api.php
@@ -103,6 +103,24 @@ function hosting_task_TASK_TYPE_form_validate($form, &$form_state) {
 
 }
 
+/**
+ * Alter the query that selects the next tasks to run.
+ *
+ * You may use this hook to prioritise one type of task over another, or to
+ * prefer one client over another etc.
+ *
+ * @see hosting_get_new_tasks
+ *
+ * @param QueryAlterableInterface $query
+ *   The structured query that will select the next tasks to run.
+ */
+function hook_query_hosting_get_new_tasks_alter(QueryAlterableInterface $query) {
+  // Change the sort ordering so that newer tasks are preferred to older ones.
+  $order_by = &$query->getOrderBy();
+  $order_by['n.changed'] = 'DESC';
+  $order_by['n.nid'] = 'DESC';
+}
+
 
 /**
  * @} End of "addtogroup hooks".
diff --git a/task/hosting_task.module b/task/hosting_task.module
index 598ae49..82b1116 100644
--- a/task/hosting_task.module
+++ b/task/hosting_task.module
@@ -723,10 +723,60 @@ function hosting_task_set_title(&$node) {
 function hosting_tasks_queue($count = 20) {
   global $provision_errors;
 
-  drush_log(dt("Running tasks queue"));
-  $tasks = _hosting_get_new_tasks($count);
+  $tasks = hosting_get_new_tasks($count);
+
+  if (count($tasks)) {
+    drush_log(dt("Found @count tasks (max @max) in queue. Running...", array(
+      '@count' => count($tasks),
+      '@max' => $count,
+    )), "notice");
+  }
+  else {
+    drush_log(dt("Found no tasks in queue. Not running."), "notice");
+  }
+
   foreach ($tasks as $task) {
-    drush_invoke_process('@self', "hosting-task", array($task->nid), array('strict' => FALSE), array('fork' => TRUE));
+    hosting_task_execute($task, array('fork' => TRUE));
+  }
+}
+
+/**
+ * Executes a task while logging to watchdog and drush.
+ *
+ * @param $task
+ *   A fully loaded task node.
+ */
+function hosting_task_execute($task, $backend_options = array()) {
+  // Log in watchdog and drush.
+  watchdog('hosting_task', 'Starting task "@title" [node/@nid].', array(
+    '@title' => $task->title,
+    '@nid' => $task->nid,
+  ), WATCHDOG_NOTICE, url("node/$task->nid"));
+  drush_log(dt('Starting task "@title" [node/@nid].', array(
+    '@title' => $task->title,
+    '@nid' => $task->nid,
+  )), 'ok');
+
+  // Execute in it's own process.
+  drush_invoke_process('@self', "hosting-task", array($task->nid), array('strict' => FALSE), $backend_options);
+
+  // Log a message, depending on forked process or not.
+  // If forked, the process may not have completed yet, so we should change the message.
+  if ($backend_options['fork']) {
+    drush_log(dt('Launched task "@title" in a forked process. [node/@nid]', array(
+      '@title' => $task->title,
+      '@nid' => $task->nid,
+    )), 'ok');
+  }
+  // If not forked, load and display the task status.
+  else {
+    $task = node_load($task->nid, NULL, TRUE);
+    drush_log(dt('Finished task "@title" with status "@status" in @duration [node/@nid].', array(
+      '@title' => $task->title,
+      '@nid' => $task->nid,
+      '@status' => _hosting_parse_error_code($task->task_status),
+      '@duration' => format_interval($task->delta, 1),
+    )), 'ok');
   }
 }
 
@@ -1323,35 +1373,46 @@ function hosting_get_tasks($filter_by = NULL, $filter_value = NULL, $count = 5,
 }
 
 /**
- * Retrieve a list of outstanding tasks.
+ * Retrieve a list of outstanding, queued, tasks.
  *
  * @param int $limit
- *   The amount of items to return.
+ *   The maximum number of tasks to return.
  *
  * @return array
  *   An associative array containing task nodes, indexed by node id.
  */
-function _hosting_get_new_tasks($limit = 20) {
+function hosting_get_new_tasks($limit = 20) {
   $return = array();
-  $result = db_query_range("SELECT t.nid
-    FROM {hosting_task} t
-    INNER JOIN {node} n
-    ON t.vid = n.vid
-    WHERE t.task_status = :task_status
-    GROUP BY t.rid
-    ORDER BY n.changed, n.nid ASC",
-    0,
-    $limit,
-    array(
-      ':task_status' => HOSTING_TASK_QUEUED,
-    ));
-  foreach ($result as $node) {
+  $query = db_select('hosting_task', 't');
+  $query->innerJoin('node', 'n', 't.vid = n.vid');
+  $query
+    ->fields('t', array('nid'))
+    ->condition('t.task_status', HOSTING_TASK_QUEUED)
+    ->orderBy('n.changed')
+    ->orderBy('n.nid')
+    ->groupBy('t.rid')
+    ->range(0, $limit)
+    ->addTag('hosting_get_new_tasks');
+
+  foreach ($query->execute() as $node) {
     $return[$node->nid] = node_load($node->nid);
   }
   return $return;
 }
 
 /**
+ * Retrieve a list of outstanding, queued, tasks.
+ *
+ * @deprecated First deprecated in Hosting 3.9 because this function was made
+ * part of the public API, use hosting_get_new_tasks() instead.
+ *
+ * @see hosting_get_new_tasks
+ */
+function _hosting_get_new_tasks($limit = 20) {
+  return hosting_get_new_tasks($limit);
+}
+
+/**
  * @name Error status definitions
  * @{
  * Bitmask values used to generate the error code to return.
