diff --git a/frontend/src/metabase/admin/settings/components/SettingsEditor.jsx b/frontend/src/metabase/admin/settings/components/SettingsEditor.jsx
index 94cbe970fabe9b2960e6dbe4840235b78078f22b..63e9d64f7eb47222339e28ca6e7e15dc1334683d 100644
--- a/frontend/src/metabase/admin/settings/components/SettingsEditor.jsx
+++ b/frontend/src/metabase/admin/settings/components/SettingsEditor.jsx
@@ -7,6 +7,7 @@ import SettingsHeader from "./SettingsHeader.jsx";
 import SettingsSetting from "./SettingsSetting.jsx";
 import SettingsEmailForm from "./SettingsEmailForm.jsx";
 import SettingsSlackForm from "./SettingsSlackForm.jsx";
+import SettingsSetupList from "./SettingsSetupList.jsx";
 import SettingsUpdatesForm from "./SettingsUpdatesForm.jsx";
 
 import _ from "underscore";
@@ -76,6 +77,13 @@ export default class SettingsEditor extends Component {
                     />
                 </div>
             );
+        } else if (section.name === "Setup") {
+            return (
+                <div className="px2">
+                    <SettingsSetupList
+                        ref="settingsForm" />
+                </div>
+            );
         } else if (section.name === "Slack") {
             return (
                 <div className="px2">
diff --git a/frontend/src/metabase/admin/settings/components/SettingsSetupList.jsx b/frontend/src/metabase/admin/settings/components/SettingsSetupList.jsx
new file mode 100644
index 0000000000000000000000000000000000000000..084aad64bec3d50bac8e5da43b302003e3522b8f
--- /dev/null
+++ b/frontend/src/metabase/admin/settings/components/SettingsSetupList.jsx
@@ -0,0 +1,100 @@
+import React, { Component, PropTypes } from "react";
+import Icon from "metabase/components/Icon.jsx";
+import LoadingAndErrorWrapper from "metabase/components/LoadingAndErrorWrapper.jsx";
+
+const TaskList = ({tasks}) =>
+  <ol>
+    { tasks.map((task, index) => <li className="mb2" key={index}><Task {...task} /></li>)}
+  </ol>
+
+const TaskSectionHeader = ({name}) =>
+  <h4 className="text-grey-4 text-bold text-uppercase pb2">{name}</h4>
+
+const TaskSection = ({name, tasks}) =>
+  <div className="mb4">
+    <TaskSectionHeader name={name} />
+    <TaskList tasks={tasks} />
+  </div>
+
+const TaskTitle = ({title, titleClassName}) =>
+  <h3 className={titleClassName}>{title}</h3>
+
+const TaskDescription = ({description}) => <p className="m0 mt1">{description}</p>
+
+const CompletionBadge = ({completed}) =>
+    <div className="mr2 flex align-center justify-center flex-no-shrink" style={{
+        borderWidth: 1,
+        borderStyle: 'solid',
+        borderColor: completed ? '#9CC177' : '#DCE9EA',
+        backgroundColor: completed ? '#9CC177' : '#fff',
+        width: 32,
+        height: 32,
+        borderRadius: 99
+      }}>
+      { completed && <Icon name="check" color={'#fff'} />}
+    </div>
+
+
+export const Task = ({title, description, completed, link}) =>
+  <a className="bordered border-brand-hover rounded transition-border flex align-center p2 no-decoration" href={link}>
+    <CompletionBadge completed={completed} />
+    <div>
+      <TaskTitle title={title} titleClassName={
+          completed ? 'text-success': 'text-brand'
+        } />
+      { !completed ? <TaskDescription description={description} /> : null }
+    </div>
+  </a>
+
+export default class SettingsSetupList extends Component {
+    constructor(props, context) {
+        super(props, context);
+        this.state = {
+            tasks: null,
+            error: null
+        };
+    }
+
+    async componentWillMount() {
+        let response = await fetch("/api/setup/admin_checklist", { credentials: 'same-origin' });
+        if (response.status !== 200) {
+            this.setState({ error: await response.json() })
+        } else {
+            this.setState({ tasks: await response.json() });
+        }
+    }
+
+    render() {
+        let tasks, nextTask;
+        if (this.state.tasks) {
+            tasks = this.state.tasks.map(section => ({
+                ...section,
+                tasks: section.tasks.filter(task => {
+                    if (task.is_next_step) {
+                        nextTask = task;
+                    }
+                    return !task.is_next_step;
+                })
+            }))
+        }
+
+        return (
+            <div className="px2">
+              <h2>Getting set up</h2>
+              <p className="mt1">A few things you can do to get the most out of Metabase.</p>
+              <LoadingAndErrorWrapper loading={!this.state.tasks} error={this.state.error} >
+              { () =>
+                  <div style={{maxWidth: 468}}>
+                      <TaskSection name="Recommended next step" tasks={[nextTask]} />
+                      {
+                        tasks.map((section, index) =>
+                          <TaskSection {...section} key={index} />
+                        )
+                      }
+                  </div>
+              }
+              </LoadingAndErrorWrapper>
+            </div>
+        );
+    }
+}
diff --git a/frontend/src/metabase/admin/settings/settings.controllers.js b/frontend/src/metabase/admin/settings/settings.controllers.js
index ea84bdb27de13fdad48f191130928bfa572b4b24..915a64090bc1913632a9e8e71019339249d423f2 100644
--- a/frontend/src/metabase/admin/settings/settings.controllers.js
+++ b/frontend/src/metabase/admin/settings/settings.controllers.js
@@ -9,6 +9,10 @@ import MetabaseSettings from 'metabase/lib/settings';
 var SettingsAdminControllers = angular.module('metabase.admin.settings.controllers', ['metabase.services']);
 
 const SECTIONS = [
+    {
+        name: "Setup",
+        settings: []
+    },
     {
         name: "General",
         settings: [
diff --git a/frontend/src/metabase/css/core/transitions.css b/frontend/src/metabase/css/core/transitions.css
index db0267df26be8456ff6774810d31c43331e5d55d..77c7f40f648e8d13be14f7c9790cf86d57bfbe26 100644
--- a/frontend/src/metabase/css/core/transitions.css
+++ b/frontend/src/metabase/css/core/transitions.css
@@ -10,3 +10,7 @@
 .transition-all {
   transition: all .2s linear;
 }
+
+.transition-border {
+  transition: border .3s linear;
+}
diff --git a/frontend/src/metabase/home/components/Homepage.jsx b/frontend/src/metabase/home/components/Homepage.jsx
index ee97832659ba236d3d358cc1dc591d10d74aa173..0535dc7fb129cbb9cb80eb546b37ca54b9b1b37e 100644
--- a/frontend/src/metabase/home/components/Homepage.jsx
+++ b/frontend/src/metabase/home/components/Homepage.jsx
@@ -7,6 +7,7 @@ import Activity from "./Activity.jsx";
 import RecentViews from "./RecentViews.jsx";
 import Smile from './Smile.jsx';
 import NewUserOnboardingModal from './NewUserOnboardingModal.jsx';
+import NextStep from "./NextStep.jsx";
 
 export default class Homepage extends Component {
 
@@ -70,6 +71,7 @@ export default class Homepage extends Component {
                         </div>
                     </div>
                     <div className="Layout-sidebar flex-no-shrink">
+                      <NextStep />
                       <RecentViews {...this.props} />
                     </div>
                 </div>
diff --git a/frontend/src/metabase/home/components/NextStep.jsx b/frontend/src/metabase/home/components/NextStep.jsx
new file mode 100644
index 0000000000000000000000000000000000000000..8b3ca201f6a86b25e9111d23077a82777ddc512c
--- /dev/null
+++ b/frontend/src/metabase/home/components/NextStep.jsx
@@ -0,0 +1,43 @@
+import React, { Component, PropTypes } from "react";
+
+import SidebarSection from "./SidebarSection.jsx";
+
+export default class NextStep extends Component {
+    constructor(props, context) {
+        super(props, context);
+        this.state = {
+            next: null
+        };
+    }
+
+    async componentWillMount() {
+        let response = await fetch("/api/setup/admin_checklist", { credentials: 'same-origin' });
+        if (response.status === 200) {
+            let sections = await response.json();
+            for (let section of sections) {
+                for (let task of section.tasks) {
+                    if (task.is_next_step) {
+                        this.setState({ next: task });
+                        break;
+                    }
+                }
+            }
+        }
+    }
+
+    render() {
+        const { next } = this.state;
+        if (next) {
+            return (
+                <SidebarSection title="Setup Tip" icon="clock" extra={<a className="text-brand no-decoration" href="/admin/settings">View all</a>}>
+                    <a className="block p3 no-decoration" href={next.link}>
+                        <h4 className="text-brand text-bold">{next.title}</h4>
+                        <p className="m0 mt1">{next.description}</p>
+                    </a>
+                </SidebarSection>
+            )
+        } else {
+            return <span className="hide" />
+        }
+    }
+}
diff --git a/frontend/src/metabase/home/components/RecentViews.jsx b/frontend/src/metabase/home/components/RecentViews.jsx
index 4c2346da4e100844ff447832614afe89681e316c..c323e378cf95f73c7e2d36d4dc635d4bce27d085 100644
--- a/frontend/src/metabase/home/components/RecentViews.jsx
+++ b/frontend/src/metabase/home/components/RecentViews.jsx
@@ -1,6 +1,7 @@
 import React, { Component, PropTypes } from "react";
 
 import Icon from "metabase/components/Icon.jsx";
+import SidebarSection from "./SidebarSection.jsx";
 import Urls from "metabase/lib/urls";
 
 export default class RecentViews extends Component {
@@ -43,29 +44,23 @@ export default class RecentViews extends Component {
         let { recentViews } = this.props;
 
         return (
-            <div className="p2">
-                <div className="text-dark-grey clearfix pt2 pb2">
-                    <Icon className="float-left" name={'clock'} width={18} height={18}></Icon>
-                    <span className="pl1 Sidebar-header">Recently Viewed</span>
-                </div>
-                <div className="rounded bg-white" style={{border: '1px solid #E5E5E5'}}>
-                    {recentViews.length > 0 ?
-                        <ul className="p2">
-                            {recentViews.map((item, index) =>
-                                <li key={index} className="py1 ml1 flex align-center clearfix">
-                                    {this.renderIllustration(item)}
-                                    <a data-metabase-event={"Recent Views;"+item.model+";"+item.cnt} className="ml1 flex-full link" href={Urls.modelToUrl(item.model, item.model_id)}>{item.model_object.name}</a>
-                                </li>
-                            )}
-                        </ul>
-                    :
-                        <div className="flex flex-column layout-centered text-normal text-grey-2">
-                            <span className="QuestionCircle mt4">!</span>
-                            <p className="p3 text-centered text-grey-4" style={{ "maxWidth": "100%" }}>You haven't looked at any Dashboards or Questions recently</p>
-                        </div>
-                    }
-                </div>
-            </div>
+            <SidebarSection title="Recently Viewed" icon="clock">
+                {recentViews.length > 0 ?
+                    <ul className="p2">
+                        {recentViews.map((item, index) =>
+                            <li key={index} className="py1 ml1 flex align-center clearfix">
+                                {this.renderIllustration(item)}
+                                <a data-metabase-event={"Recent Views;"+item.model+";"+item.cnt} className="ml1 flex-full link" href={Urls.modelToUrl(item.model, item.model_id)}>{item.model_object.name}</a>
+                            </li>
+                        )}
+                    </ul>
+                :
+                    <div className="flex flex-column layout-centered text-normal text-grey-2">
+                        <span className="QuestionCircle mt4">!</span>
+                        <p className="p3 text-centered text-grey-4" style={{ "maxWidth": "100%" }}>You haven't looked at any Dashboards or Questions recently</p>
+                    </div>
+                }
+            </SidebarSection>
         );
     }
 }
diff --git a/frontend/src/metabase/home/components/SidebarSection.jsx b/frontend/src/metabase/home/components/SidebarSection.jsx
new file mode 100644
index 0000000000000000000000000000000000000000..c8358c8e39ba9ee8ff2c12b226e59e6189db0674
--- /dev/null
+++ b/frontend/src/metabase/home/components/SidebarSection.jsx
@@ -0,0 +1,17 @@
+import React, { Component, PropTypes } from "react";
+
+import Icon from "metabase/components/Icon.jsx";
+
+const SidebarSection = ({ title, icon, extra, children }) =>
+    <div className="px2 pt1">
+        <div className="text-dark-grey clearfix pt2 pb2">
+            <Icon className="float-left" name={icon} width={18} height={18}></Icon>
+            <span className="pl1 Sidebar-header">{title}</span>
+            { extra && <span className="float-right">{extra}</span>}
+        </div>
+        <div className="rounded bg-white" style={{border: '1px solid #E5E5E5'}}>
+            { children }
+        </div>
+    </div>
+
+export default SidebarSection;
diff --git a/resources/log4j.properties b/resources/log4j.properties
index e6d168cbff584a30e28e57663fe782235f6b905d..4bdb765b46d724bb49b18d2a21da944fa5f345ba 100644
--- a/resources/log4j.properties
+++ b/resources/log4j.properties
@@ -20,5 +20,6 @@ log4j.appender.metabase=metabase.logger.Appender
 log4j.logger.metabase=INFO
 log4j.logger.metabase.db=DEBUG
 log4j.logger.metabase.driver=DEBUG
+log4j.logger.metabase.middleware=DEBUG
 log4j.logger.metabase.query-processor=DEBUG
 log4j.logger.metabase.sync-database=DEBUG
diff --git a/src/metabase/api/setup.clj b/src/metabase/api/setup.clj
index 9ed6f9fdc158f29fb91f5041277d507303e58d14..3bde8714ac1635cecba6ef305b45cb0120576881 100644
--- a/src/metabase/api/setup.clj
+++ b/src/metabase/api/setup.clj
@@ -1,10 +1,12 @@
 (ns metabase.api.setup
-  (:require [compojure.core :refer [defroutes POST]]
+  (:require [compojure.core :refer [defroutes GET POST]]
             (metabase.api [common :refer :all]
                           [database :refer [annotation:DBEngine]])
             (metabase [db :as db]
                       [driver :as driver]
+                      [email :as email]
                       [events :as events])
+            [metabase.integrations.slack :as slack]
             (metabase.models [database :refer [Database]]
                              [session :refer [Session]]
                              [setting :as setting]
@@ -86,4 +88,108 @@
       (catch Throwable e
         (response-invalid :general (.getMessage e))))))
 
+
+;;; Admin Checklist
+
+(defn- admin-checklist-values []
+  (let [has-dbs?           (db/exists? Database)
+        has-dashboards?    (db/exists? 'Dashboard)
+        has-pulses?        (db/exists? 'Pulse)
+        has-labels?        (db/exists? 'Label)
+        has-hidden-tables? (db/exists? 'Table, :visibility_type [:not= nil])
+        has-metrics?       (db/exists? 'Metric)
+        has-segments?      (db/exists? 'Segment)
+        num-tables         (db/select-one-count 'Table)
+        num-cards          (db/select-one-count 'Card)
+        num-users          (db/select-one-count 'User)]
+    [{:title       "Add a database"
+      :group       "Get connected"
+      :description "TODO - Write something good here"
+      :link        "/admin/databases/create"
+      :completed   has-dbs?
+      :triggered   :always}
+     {:title       "Set up email"
+      :group       "Get connected"
+      :description "Add email credentials so you can more easily invite team members and get updates via Pulses."
+      :link        "/admin/settings/?section=Email"
+      :completed   (email/email-configured?)
+      :triggered   :always}
+     {:title       "Set Slack credentials"
+      :group       "Get connected"
+      :description "Does your team use Slack?  If so, you can send automated updates via pulses and ask questions with Metabot."
+      :link        "/admin/settings/?section=Slack"
+      :completed   (slack/slack-configured?)
+      :triggered   :always}
+     {:title       "Invite team members"
+      :group       "Get connected"
+      :description "Share answers and data with the rest of your team."
+      :link        "/admin/people/"
+      :completed   (>= num-users 1)
+      :triggered   (or has-dashboards?
+                       has-pulses?
+                       (>= num-cards 5))}
+     {:title       "Hide irrelevant tables"
+      :group       "Curate your data"
+      :description "If your data contains technical or irrelevant info you can hide it."
+      :link        "/admin/datamodel/database"
+      :completed   has-hidden-tables?
+      :triggered   (>= num-tables 20)}
+     {:title       "Organize questions"
+      :group       "Curate your data"
+      :description "Have a lot of saved questions in Metabase? Create labels to help manage them and add context."
+      :link        "/questions/all"
+      :completed   (not has-labels?)
+      :triggered   (>= num-cards 30)}
+     {:title       "Create metrics"
+      :group       "Curate your data"
+      :description "Define canonical metrics to make it easier for the rest of your team to get the right answers."
+      :link        "/admin/datamodel/database"
+      :completed   has-metrics?
+      :triggered   (>= num-cards 30)}
+     {:title       "Create segments"
+      :group       "Curate your data"
+      :description "Keep everyone on the same page by creating canonnical sets of filters anyone can use while asking questions."
+      :link        "/admin/datamodel/database"
+      :completed   has-segments?
+      :triggered   (>= num-cards 30)}
+     {:title       "Create a getting started guide"
+      :group       "Curate your data"
+      :description "Have a lot of data in Metabase? A getting started guide can help your team find their way around."
+      :completed   false                               ; TODO - how do we determine this?
+      :triggered   (and (>= num-cards 10)
+                        (>= num-users 5))}]))
+
+(defn- add-next-step-info
+  "Add `is_next_step` key to all the STEPS from `admin-checklist`.
+  The next step is the *first* step where `:triggered` is `true` and `:completed` is `false`."
+  [steps]
+  (loop [acc [], found-next-step? false, [step & more] steps]
+    (if-not step
+      acc
+      (let [is-next-step? (boolean (and (not found-next-step?)
+                                        (:triggered step)
+                                        (not (:completed step))))
+            step          (-> (assoc step :is_next_step is-next-step?)
+                              (update :triggered boolean))]
+        (recur (conj acc step)
+               (or found-next-step? is-next-step?)
+               more)))))
+
+(defn- partition-steps-into-groups
+  "Partition the admin checklist steps into a sequence of groups."
+  [steps]
+  (for [[{group-name :group}, :as tasks] (partition-by :group steps)]
+    {:name  group-name
+     :tasks tasks}))
+
+(defn- admin-checklist []
+  (partition-steps-into-groups (add-next-step-info (admin-checklist-values))))
+
+(defendpoint GET "/admin_checklist"
+  "Return various \"admin checklist\" steps and whether they've been completed. You must be a superuser to see this!"
+  []
+  (check-superuser)
+  (admin-checklist))
+
+
 (define-routes)