diff --git a/frontend/src/card/card.controllers.js b/frontend/src/card/card.controllers.js
index 2c9f87d587e93b9636268f751438e6ba22984c4f..e9dea044e01ff409609d6ac74aac99cd74b8a654 100644
--- a/frontend/src/card/card.controllers.js
+++ b/frontend/src/card/card.controllers.js
@@ -409,6 +409,7 @@ CardControllers.controller('CardDetail', [
 
             renderAll();
 
+            let startTime = new Date();
             // make our api call
             Metabase.dataset(dataset_query, function (result) {
                 queryResult = result;
@@ -484,7 +485,7 @@ CardControllers.controller('CardDetail', [
 
             }, function (error) {
                 isRunning = false;
-                queryResult = { error: error };
+                queryResult = { error: error, duration: new Date() - startTime };
 
                 renderAll();
             });
diff --git a/frontend/src/css/query_builder.css b/frontend/src/css/query_builder.css
index 4f5ffc8bf47dbeb54c8b7da23a3e0ba77183d5d3..0cf66effb8ca3a24d7b871c50214e53b1aeb8559 100644
--- a/frontend/src/css/query_builder.css
+++ b/frontend/src/css/query_builder.css
@@ -254,10 +254,45 @@
   background-image: url('/app/img/no_understand.svg');
 }
 
+.QueryError-image--serverError {
+  width: 120px;
+  height: 120px;
+  background-image: url('/app/img/stopwatch.svg');
+}
+
+.QueryError-image--timeout {
+  width: 120px;
+  height: 120px;
+  background-image: url('/app/img/stopwatch.svg');
+}
+
 .QueryError-messageText {
   line-height: 1.4;
 }
 
+.QueryError-adminEmail {
+  position: relative;
+  display: inline-block;
+  border-radius: var(--default-border-radius);
+  border: 1px solid rgb(197, 197, 197);
+  margin-top: var(--margin-2);
+  padding: var(--padding-1) var(--padding-4) var(--padding-1) var(--padding-4);
+}
+
+.QueryError-adminEmail:before {
+  content: "Admin Email";
+  font-size: 10px;
+  text-align: center;
+  text-transform: uppercase;
+  background-color: white;
+  padding-left: var(--padding-1);
+  padding-right: var(--padding-1);
+
+  position: absolute;
+  top: -0.75em;
+  left: 50%;
+  margin-left: -41px; /* ugh */
+}
 
 /* GUI BUILDER */
 
diff --git a/frontend/src/lib/settings.js b/frontend/src/lib/settings.js
index 1d846ebca3505d00f297bc7827f26a8978d6af80..94db204f76917564957d5f797184dc28e001b499 100644
--- a/frontend/src/lib/settings.js
+++ b/frontend/src/lib/settings.js
@@ -19,6 +19,9 @@ const MetabaseSettings = {
     },
 
     // these are all special accessors which provide a lookup of a property plus some additional help
+    adminEmail: function() {
+        return mb_settings.admin_email;
+    },
 
     isEmailConfigured: function() {
         return mb_settings.email_configured;
diff --git a/frontend/src/query_builder/QueryVisualization.jsx b/frontend/src/query_builder/QueryVisualization.jsx
index 814dd67366b3021c6b14f847940102f46e2e2bba..e5d4155ebe5721d74909ab5cf76293ce5f95771d 100644
--- a/frontend/src/query_builder/QueryVisualization.jsx
+++ b/frontend/src/query_builder/QueryVisualization.jsx
@@ -8,6 +8,7 @@ import QueryVisualizationObjectDetailTable from './QueryVisualizationObjectDetai
 import RunButton from './RunButton.jsx';
 import VisualizationSettings from './VisualizationSettings.jsx';
 
+import MetabaseSettings from "metabase/lib/settings";
 import Query from "metabase/lib/query";
 
 import cx from "classnames";
@@ -167,23 +168,39 @@ export default class QueryVisualization extends Component {
                 </div>
             );
         } else {
-            let error = this.props.result.error;
+            let { result } = this.props;
+            let error = result.error;
             if (error) {
                 let message;
-                // transform HTTP errors to plain text error messages, and always show them
                 if (typeof error.status === "number") {
-                    if (error.status === 503) {
-                        error = "I'm sorry, the server timed out while asking your question.";
+                    let adminEmail = MetabaseSettings.adminEmail();
+                    // Assume if the request took more than 15 seconds it was due to a timeout
+                    // Some platforms like Heroku return a 503 for numerous types of errors so we can't use the status code to distinguish between timeouts and other failures.
+                    if (result.duration > 15*1000) {
+                        viz = (
+                            <div className="QueryError flex full align-center">
+                                <div className="QueryError-image QueryError-image--serverError"></div>
+                                <div className="QueryError-message text-centered">
+                                    <h1 className="text-bold">Your question took too long</h1>
+                                    <p className="QueryError-messageText">We didn't get an answer back from your database in time, so we had to stop. You can try again in a minute, or if the problem persists, you can email an admin to let them know.</p>
+                                    {adminEmail && <span className="QueryError-adminEmail"><a className="no-decoration" href={"mailto:"+adminEmail}>{adminEmail}</a></span>}
+                                </div>
+                            </div>
+                        );
                     } else {
-                        error = "I'm sorry, an error occured: " + error.statusText;
+                        viz = (
+                            <div className="QueryError flex full align-center">
+                                <div className="QueryError-image QueryError-image--timeout"></div>
+                                <div className="QueryError-message text-centered">
+                                    <h1 className="text-bold">We're experiencing server issues</h1>
+                                    <p className="QueryError-messageText">Try refreshing the page after waiting a minute or two. If the problem persists we'd recommend you contact an admin.</p>
+                                    {adminEmail && <span className="QueryError-adminEmail"><a className="no-decoration" href={"mailto:"+adminEmail}>{adminEmail}</a></span>}
+                                </div>
+                            </div>
+                        );
                     }
-                    message = error;
-                }
-                // always show errors for native queries
-                if (this.props.card.dataset_query && this.props.card.dataset_query.type === 'native') {
-                    message = error;
-                }
-                if (message) {
+                } else if (this.props.card.dataset_query && this.props.card.dataset_query.type === 'native') {
+                    // always show errors for native queries
                     viz = (
                         <div className="QueryError flex full align-center text-error">
                             <div className="QueryError-iconWrapper">
@@ -194,7 +211,6 @@ export default class QueryVisualization extends Component {
                             <span className="QueryError-message">{message}</span>
                         </div>
                     );
-
                 } else {
                     viz = (
                         <div className="QueryError flex full align-center">
diff --git a/resources/frontend_client/app/img/stopwatch.svg b/resources/frontend_client/app/img/stopwatch.svg
new file mode 100644
index 0000000000000000000000000000000000000000..c5f1b1cf13e50d59f7d63e708a052366efb41cad
--- /dev/null
+++ b/resources/frontend_client/app/img/stopwatch.svg
@@ -0,0 +1,12 @@
+<?xml version="1.0" encoding="UTF-8" standalone="no"?>
+<svg width="108px" height="118px" viewBox="0 0 108 118" version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink">
+    <g stroke="none" stroke-width="1" fill="none" fill-rule="evenodd">
+          <circle stroke="#E3E3E3" stroke-width="3" fill="#FFFFFF" cx="53" cy="65" r="52"></circle>
+          <path d="M54.9857911,57.2483919 C54.9951751,57.1698571 55,57.0899169 55,57.008845 L55,38.991155 C55,37.8896739 54.1045695,37 53,37 C51.8877296,37 51,37.8914705 51,38.991155 L51,57.008845 C51,57.0899255 51.0048519,57.1698584 51.0142819,57.2483732 C47.5565832,58.1314817 45,61.2671799 45,65 C45,69.418278 48.581722,73 53,73 C57.418278,73 61,69.418278 61,65 C61,61.2672062 58.4434528,58.1315259 54.9857911,57.2483919 Z" fill="#E3E3E3"></path>
+          <rect stroke="#E3E3E3" stroke-width="3" fill="#F8F8F8" x="45" y="1" width="16" height="15" rx="6"></rect>
+          <path d="M52,101 L54,101 L54,108 L52,108 L52,101 Z M52,22 L54,22 L54,29 L52,29 L52,22 Z M89,66 L89,64 L95.7338129,64 L95.7338129,66 L89,66 Z M9.52844494,66 L9.52844494,64 L16.2622579,64 L16.2622579,66 L9.52844494,66 Z M78.0473135,92.2999886 L79.4615271,90.8857751 L84.2230519,95.6472999 L82.8088383,97.0615134 L78.0473135,92.2999886 Z M22.0473135,34.2999886 L23.4615271,32.8857751 L28.2230519,37.6472999 L26.8088383,39.0615134 L22.0473135,34.2999886 Z M26.8088383,90.8857751 L28.2230519,92.2999886 L23.4615271,97.0615134 L22.0473135,95.6472999 L26.8088383,90.8857751 Z M82.8088383,32.8857751 L84.2230519,34.2999886 L79.4615271,39.0615134 L78.0473135,37.6472999 L82.8088383,32.8857751 Z" stroke="#E3E3E3" stroke-width="3" fill="#F8F8F8"></path>
+          <path d="M89,7 C91.2273181,4.42229162 94.9764573,4.42395737 97,7 L105,15 C107.579027,17.0283807 107.575629,20.7737606 105,23 L103,25 C100.772682,27.5777084 97.0235427,27.5760426 95,25 L87,17 C84.4209732,14.9716193 84.4243711,11.2262394 87,9 L89,7 L89,7 Z" stroke="#E3E3E3" stroke-width="3" fill="#F8F8F8"></path>
+          <rect stroke="#E3E3E3" stroke-width="3" fill="#F8F8F8" transform="translate(89.038462, 22.726619) rotate(-45.000000) translate(-89.038462, -22.726619) " x="84.8461538" y="17.676259" width="8.38461538" height="10.1007194"></rect>
+          <circle fill="#F8F8F8" cx="53" cy="65" r="5"></circle>
+    </g>
+</svg>
diff --git a/src/metabase/models/setting.clj b/src/metabase/models/setting.clj
index b123d70c970422e49d3b01b4b3ab587f1e8b22b2..5a2a78d5a3d3bafdfd134b47c717183f61d02ac9 100644
--- a/src/metabase/models/setting.clj
+++ b/src/metabase/models/setting.clj
@@ -164,7 +164,8 @@
    :anon_tracking_enabled (let [tracking? (get :anon-tracking-enabled)]
                             (or (nil? tracking?) (= "true" tracking?)))
    :site_name             (get :site-name)
-   :email_configured      (not (s/blank? (get :email-smtp-host)))})
+   :email_configured      (not (s/blank? (get :email-smtp-host)))
+   :admin_email           (sel :one :field ['User :email] (k/where {:is_superuser true}))})
 
 ;; # IMPLEMENTATION