diff --git a/pyanaconda/bootloader.py b/pyanaconda/bootloader.py
index baac116..1c0fe04 100644
--- a/pyanaconda/bootloader.py
+++ b/pyanaconda/bootloader.py
@@ -1888,12 +1888,12 @@ class SILO(YabootSILOBase):
 
 # anaconda-specific functions
 
-def writeSysconfigKernel(anaconda, default_kernel):
+def writeSysconfigKernel(storage, default_kernel):
     f = open(ROOT_PATH + "/etc/sysconfig/kernel", "w+")
     f.write("# UPDATEDEFAULT specifies if new-kernel-pkg should make\n"
             "# new kernels the default\n")
     # only update the default if we're setting the default to linux (#156678)
-    if anaconda.bootloader.default.device == anaconda.storage.rootDevice:
+    if storage.bootloader.default.device == storage.rootDevice:
         f.write("UPDATEDEFAULT=yes\n")
     else:
         f.write("UPDATEDEFAULT=no\n")
@@ -1903,97 +1903,89 @@ def writeSysconfigKernel(anaconda, default_kernel):
     f.close()
 
 
-def writeBootloader(anaconda):
+def writeBootloader(storage, payload):
     """ Write bootloader configuration to disk.
 
         When we get here, the bootloader will already have a default linux
         image. We only have to add images for the non-default kernels and
         adjust the default to reflect whatever the default variant is.
     """
+    from pyanaconda.progress import progress_report
+    from pyanaconda.errors import *
 
-    # TODO: Verify the bootloader configuration has all it needs.
-    #
-    #       - zipl doesn't need to have a stage1 device set.
-    #       - Isn't it possible for stage1 to be unset on iSeries if not using
-    #         yaboot? If so, presumably they told us not to install any
-    #         bootloader.
-    stage1_device = anaconda.bootloader.stage1_device
-    log.info("bootloader stage1 target device is %s" % stage1_device.name)
-    stage2_device = anaconda.bootloader.stage2_device
-    log.info("bootloader stage2 target device is %s" % stage2_device.name)
-
-    w = None
-    if anaconda.intf:
-        w = anaconda.intf.waitWindow(_("Bootloader"),
-                                     _("Installing bootloader."))
-
-    # get a list of installed kernel packages
-    kernel_versions = anaconda.backend.kernelVersionList()
-    if not kernel_versions:
-        log.warning("no kernel was installed -- bootloader config unchanged")
-        if anaconda.intf:
-            anaconda.intf.messageWindow(_("Warning"),
-                        _("No kernel packages were installed on the system. "
-                          "Bootloader configuration will not be changed."))
-        return
-
-    # The first one is the default kernel. Update the bootloader's default
-    # entry to reflect the details of the default kernel.
-    (version, arch, nick) = kernel_versions.pop(0)
-    default_image = LinuxBootLoaderImage(device=anaconda.storage.rootDevice,
-                                         version=version,
-                                         label=productName,
-                                         short="linux")
-    anaconda.bootloader.add_image(default_image)
-    anaconda.bootloader.default = default_image
-
-    # all the linux images' labels are based on the default image's
-    base_label = default_image.label
-    base_short = default_image.short_label
-
-    # get the name of the default kernel package for use in
-    # /etc/sysconfig/kernel
-    default_kernel = "kernel"
-    if nick != "base":
-        default_kernel += "-%s" % nick
-
-    # now add an image for each of the other kernels
-    used = ["base"]
-    for (version, arch, nick) in kernel_versions:
-        if nick in used:
-            nick += "-%s" % version
-
-        used.append(nick)
-        label = "%s-%s" % (base_label, nick)
-        short = "%s-%s" % (base_short, nick)
-        if anaconda.bootloader.trusted_boot:
-            image = TbootLinuxBootLoaderImage(
-                                         device=anaconda.storage.rootDevice,
-                                         version=version,
-                                         label=label, short=short)
-        else:
-            image = LinuxBootLoaderImage(device=anaconda.storage.rootDevice,
-                                         version=version,
-                                         label=label, short=short)
-        anaconda.bootloader.add_image(image)
-
-    # write out /etc/sysconfig/kernel
-    writeSysconfigKernel(anaconda, default_kernel)
+    with progress_report(_("Installing bootloader.")):
+        # TODO: Verify the bootloader configuration has all it needs.
+        #
+        #       - zipl doesn't need to have a stage1 device set.
+        #       - Isn't it possible for stage1 to be unset on iSeries if not
+        #         using yaboot? If so, presumably they told us not to install
+        #         any bootloader.
+        stage1_device = storage.bootloader.stage1_device
+        log.info("bootloader stage1 target device is %s" % stage1_device.name)
+        stage2_device = storage.bootloader.stage2_device
+        log.info("bootloader stage2 target device is %s" % stage2_device.name)
+
+
+        # get a list of installed kernel packages
+        kernel_versions = payload.kernelVersionList
+        if not kernel_versions:
+            log.warning("no kernel was installed -- bootloader config unchanged")
+            return
 
-    # set up dracut/fips boot args
-    anaconda.bootloader.set_boot_args(keyboard=anaconda.keyboard,
-                                      storage=anaconda.storage,
-                                      language=anaconda.instLanguage,
-                                      network=anaconda.network)
+        # The first one is the default kernel. Update the bootloader's default
+        # entry to reflect the details of the default kernel.
+        (version, arch, nick) = kernel_versions.pop(0)
+        default_image = LinuxBootLoaderImage(device=storage.rootDevice,
+                                             version=version,
+                                             label=productName,
+                                             short="linux")
+        storage.bootloader.add_image(default_image)
+        storage.bootloader.default = default_image
+
+        # all the linux images' labels are based on the default image's
+        base_label = default_image.label
+        base_short = default_image.short_label
+
+        # get the name of the default kernel package for use in
+        # /etc/sysconfig/kernel
+        default_kernel = "kernel"
+        if nick != "base":
+            default_kernel += "-%s" % nick
+
+        # now add an image for each of the other kernels
+        used = ["base"]
+        for (version, arch, nick) in kernel_versions:
+            if nick in used:
+                nick += "-%s" % version
+
+            used.append(nick)
+            label = "%s-%s" % (base_label, nick)
+            short = "%s-%s" % (base_short, nick)
+            if storage.bootloader.trusted_boot:
+                image = TbootLinuxBootLoaderImage(
+                                             device=storage.rootDevice,
+                                             version=version,
+                                             label=label, short=short)
+            else:
+                image = LinuxBootLoaderImage(device=storage.rootDevice,
+                                             version=version,
+                                             label=label, short=short)
+            storage.bootloader.add_image(image)
+
+        # write out /etc/sysconfig/kernel
+        writeSysconfigKernel(storage, default_kernel)
+
+        # set up dracut/fips boot args
+        # XXX FIXME: do this from elsewhere?
+        #storage.bootloader.set_boot_args(keyboard=anaconda.keyboard,
+        #                                 storage=anaconda.storage,
+        #                                 language=anaconda.instLanguage,
+        #                                 network=anaconda.network)
+        storage.bootloader.set_boot_args(storage=storage)
 
-    try:
-        anaconda.bootloader.write()
-    except BootLoaderError as e:
-        if anaconda.intf:
-            anaconda.intf.messageWindow(_("Warning"),
-                            _("There was an error installing the bootloader.  "
-                              "The system may not be bootable."))
-    finally:
-        if w:
-            w.pop()
+        try:
+            storage.bootloader.write()
+        except BootLoaderError as e:
+            if errorHandler.cb(e) == ERROR_RAISE:
+                raise
 
diff --git a/pyanaconda/install.py b/pyanaconda/install.py
index 11dcb47..3c9f626 100644
--- a/pyanaconda/install.py
+++ b/pyanaconda/install.py
@@ -22,6 +22,7 @@
 
 from pyanaconda.errors import errorHandler
 from pyanaconda.storage import turnOnFilesystems
+from pyanaconda.bootloader import writeBootloader
 
 def doInstall(storage, payload, ksdata, instClass):
     """Perform an installation.  This method takes the ksdata as prepared by
@@ -41,15 +42,32 @@ def doInstall(storage, payload, ksdata, instClass):
     steps = len(storage.devicetree.findActions(type="create", object="format")) + \
             len(storage.devicetree.findActions(type="resize", object="format")) + \
             len(storage.devicetree.findActions(type="migrate", object="format"))
-    steps += 2  # package install setup, package install
+    steps += 3  # package install setup, package install, bootloader
     progress.send_init(steps)
 
     # Do partitioning.
     turnOnFilesystems(storage, errorHandler)
+    storage.write()
+
+    # select packages for storage, bootloader
+    map(payload.selectPackage, storage.platform.packages)
+    map(payload.selectPackage, storage.bootloader.packages)
+    for device in storage.fsset.devices:
+        # this takes care of device and filesystem packages
+        map(payload.selectPackage, device.packages)
 
     # Do packaging.
     payload.preInstall()
     payload.install()
     payload.postInstall()
 
+    # Install bootloader.
+    writeBootloader(storage, payload)
+
+    # set default systemd target
+    default_target = ROOT_PATH + '/etc/systemd/system/default.target'
+    if os.path.islink(default_target):
+        os.unlink(default_target)
+    os.symlink('/usr/lib/systemd/system/multi-user.target', default_target)
+
     progress.send_complete()
diff --git a/pyanaconda/packaging/yumpayload.py b/pyanaconda/packaging/yumpayload.py
index e625e31..64b743d 100644
--- a/pyanaconda/packaging/yumpayload.py
+++ b/pyanaconda/packaging/yumpayload.py
@@ -744,6 +746,49 @@ reposdir=%s
 
         return self._space_required
 
+    @property
+    def kernelVersionList(self):
+        import rpm
+
+        def kernel_tag(hdr):
+            """ Return the kernel variant or None if not a kernel package. """
+            tag = None
+            if hdr['name'] == "kernel":
+                tag = "base"
+            elif hdr['name'].startswith("kernel-"):
+                # strip off leading "kernel-"
+                tag = hdr['name'][7:]
+
+            return tag
+
+        iutil.resetRpmDb()
+        ts = rpm.TransactionSet(ROOT_PATH)
+
+        versions = []
+        mi = ts.dbMatch('provides', 'kernel')
+        for h in mi:
+            arch = h['arch']
+
+            tag = kernel_tag(h)
+            if not tag:
+                log.warning("failed to find tag for kernel %s" % h['nevra'])
+                continue
+
+            version = "%s-%s.%s" % (h['version'], h['release'], arch)
+            if tag != "base":
+                version += ".%s" % tag
+
+            entry = (version, arch, tag)
+
+            # rpm really shouldn't return the same kernel more than once... but
+            # sometimes it does (#467822)
+            if entry in versions:
+                continue
+
+            versions.append(entry)
+
+        return versions
+
     ###
     ### METHODS FOR INSTALLING THE PAYLOAD
     ###
diff --git a/pyanaconda/storage/__init__.py b/pyanaconda/storage/__init__.py
index 8ea582e..e22e090 100644
--- a/pyanaconda/storage/__init__.py
+++ b/pyanaconda/storage/__init__.py
@@ -1250,6 +1250,8 @@ class Storage(object):
             shelf[key] = [d.dict for d in self.devices]
 
     def write(self):
+        if not os.path.isdir("%s/etc" % ROOT_PATH):
+            os.mkdir("%s/etc" % ROOT_PATH)
         self.fsset.write()
         self.makeMtab()
         self.iscsi.write(self)
