diff --git a/app/main.js b/app/main.js
index 64deeb573..4b70973db 100644
--- a/app/main.js
+++ b/app/main.js
@@ -1,9 +1,10 @@
-const {app, BrowserWindow, ipcMain, shell} = require('electron');
+const {app, BrowserWindow, ipcMain} = require('electron');
 const path = require('path');
 const jayson = require('jayson');
 // tree-kill has better cross-platform handling of
 // killing a process.  child-process.kill was unreliable
 const kill = require('tree-kill');
+const child_process = require('child_process');
 
 
 let client = jayson.client.http('http://localhost:5279/lbryapi');
@@ -37,7 +38,7 @@ function launchDaemon() {
     executable = path.join(__dirname, 'dist', 'lbrynet-daemon');
   }
   console.log('Launching daemon: ' + executable)
-  subpy = require('child_process').spawn(executable)
+  subpy = child_process.spawn(executable)
   // Need to handle the data event instead of attaching to
   // process.stdout because the latter doesn't work. I believe on
   // windows it buffers stdout and we don't get any meaningful output
@@ -86,6 +87,31 @@ function launchDaemonIfNotRunning() {
   );
 }
 
+/*
+ * Last resort for killing unresponsive daemon instances.
+ * Looks for any processes called "lbrynet-daemon" and
+ * tries to force kill them.
+ */
+function forceKillAllDaemons() {
+  console.log("Attempting to force kill any running lbrynet-daemon instances...");
+
+  const fgrepOut = child_process.spawnSync('pgrep', ['-x', 'lbrynet-daemon'], {encoding: 'utf8'}).stdout;
+  const daemonPids = fgrepOut.split(/[^\d]+/).filter((pid) => pid);
+  if (!daemonPids) {
+    console.log('No lbrynet-daemon found running.');
+  } else {
+    console.log(`Found ${daemonPids.length} running daemon instances. Attempting to force kill...`);
+
+    for (const pid of daemonPids) {
+      kill(pid, 'SIGKILL', (err) => {
+        if (err) {
+          console.log(`Failed to force kill running daemon with pid ${pid}. Error message: ${err.message}`);
+        }
+      });
+    }
+  }
+}
+
 
 // Quit when all windows are closed.
 app.on('window-all-closed', () => {
@@ -122,10 +148,17 @@ function shutdownDaemon(evenIfNotStartedByApp = false) {
       console.log('Killed lbrynet-daemon process');
     });
   } else if (evenIfNotStartedByApp) {
-    console.log('Killing lbrynet-daemon, even though app did not start it');
-    client.request('daemon_stop', []);
-    // TODO: If the daemon errors or times out when we make this request, find
-    // the process and force quit it.
+    console.log('Stopping lbrynet-daemon, even though app did not start it');
+    client.request('daemon_stop', [], (err, res) => {
+      if (err) {
+        // We could get an error because the daemon is already stopped (good)
+        // or because it's running but not responding properly (bad).
+        // So try to force kill any daemons that are still running.
+
+        console.log('received error when stopping lbrynet-daemon. Error message: {err.message}');
+        forceKillAllDaemons();
+      }
+    });
   } else {
     console.log('Not killing lbrynet-daemon because app did not start it')
   }
@@ -144,7 +177,10 @@ function shutdown() {
 
 function upgrade(event, installerPath) {
   app.on('quit', () => {
-    shell.openItem(installerPath);
+    // shell.openItem doesn't reliably work from the app process, so run xdg-open directly
+    child_process.spawn('xdg-open', [installerPath], {
+      stdio: 'ignore',
+    });
   });
   if (win) {
     win.loadURL(`file://${__dirname}/dist/upgrade.html`);
@@ -160,4 +196,4 @@ function upgrade(event, installerPath) {
 
 ipcMain.on('upgrade', upgrade);
 
-ipcMain.on('shutdown', shutdown);
\ No newline at end of file
+ipcMain.on('shutdown', shutdown);
diff --git a/ui/js/app.js b/ui/js/app.js
index 79ff80873..1c8c08dd7 100644
--- a/ui/js/app.js
+++ b/ui/js/app.js
@@ -27,6 +27,7 @@ const {download} = remote.require('electron-dl');
 const os = require('os');
 const path = require('path');
 const app = require('electron').remote.app;
+const fs = remote.require('fs');
 
 
 var App = React.createClass({
@@ -137,16 +138,14 @@ var App = React.createClass({
     });
   },
   handleUpgradeClicked: function() {
-    // TODO: create a callback for onProgress and have the UI
-    //       show download progress
-    // TODO: calling lbry.stop() ends up displaying the "daemon
-    //       unexpectedly stopped" page. Have a better way of shutting down
-    let dir = app.getPath('temp');
+    // Make a new directory within temp directory so the filename is guaranteed to be available
+    const dir = fs.mkdtempSync(app.getPath('temp') + require('path').sep);
+
     let options = {
       onProgress: (p) => this.setState({downloadProgress: Math.round(p * 100)}),
       directory: dir,
     };
-    download(remote.getCurrentWindow(), this.state.updateUrl, options)
+    download(remote.getCurrentWindow(), this.getUpdateUrl(), options)
       .then(downloadItem => {
         /**
          * TODO: get the download path directly from the download object. It should just be