diff --git a/enterprise/backend/src/metabase_enterprise/serialization/v2/models.clj b/enterprise/backend/src/metabase_enterprise/serialization/v2/models.clj
index 6a79f894f9198156668e4029a61ecee7a8eba35e..f1cc3d93bfba146d59a6777ba24d92782bd8192c 100644
--- a/enterprise/backend/src/metabase_enterprise/serialization/v2/models.clj
+++ b/enterprise/backend/src/metabase_enterprise/serialization/v2/models.clj
@@ -4,6 +4,8 @@
   "The list of models which are exported by serialization. Used for production code and by tests."
   ["Card"
    "Collection"
+   "Dashboard"
+   "DashboardCard"
    "Database"
    "Field"
    "Setting"
diff --git a/enterprise/backend/test/metabase_enterprise/serialization/v2/extract_test.clj b/enterprise/backend/test/metabase_enterprise/serialization/v2/extract_test.clj
index 9b500dd9e9c9bee44bd153acae9429b0bf11f135..e8e1aa460996850949c8e00abe6c59bcc6db4a0f 100644
--- a/enterprise/backend/test/metabase_enterprise/serialization/v2/extract_test.clj
+++ b/enterprise/backend/test/metabase_enterprise/serialization/v2/extract_test.clj
@@ -2,7 +2,7 @@
   (:require [clojure.test :refer :all]
             [metabase-enterprise.serialization.test-util :as ts]
             [metabase-enterprise.serialization.v2.extract :as extract]
-            [metabase.models :refer [Card Collection Database Table User]]
+            [metabase.models :refer [Card Collection Dashboard DashboardCard Database Table User]]
             [metabase.models.serialization.base :as serdes.base]))
 
 (defn- select-one [model-name where]
@@ -75,29 +75,51 @@
 (deftest dashboard-and-cards-test
   (ts/with-empty-h2-app-db
     (ts/with-temp-dpc [Collection [{coll-id    :id
-                                    coll-eid   :entity_id
-                                    coll-slug  :slug}      {:name "Some Collection"}]
-                       User       [{mark-id :id}           {:first_name "Mark"
-                                                            :last_name  "Knopfler"
-                                                            :email      "mark@direstrai.ts"}]
-                       Database   [{db-id      :id}        {:name "My Database"}]
-                       Table      [{no-schema-id :id}      {:name "Schemaless Table" :db_id db-id}]
-                       Table      [{schema-id    :id}      {:name        "Schema'd Table"
-                                                            :db_id       db-id
-                                                            :schema      "PUBLIC"}]
+                                    coll-eid   :entity_id}    {:name "Some Collection"}]
+                       User       [{mark-id :id}              {:first_name "Mark"
+                                                               :last_name  "Knopfler"
+                                                               :email      "mark@direstrai.ts"}]
+                       User       [{dave-id :id}              {:first_name "David"
+                                                               :last_name  "Knopfler"
+                                                               :email      "david@direstrai.ts"}]
+                       Collection [{mark-coll-eid :entity_id} {:name "MK Personal"
+                                                               :personal_owner_id mark-id}]
+                       Collection [{dave-coll-id  :id
+                                    dave-coll-eid :entity_id} {:name "DK Personal"
+                                                               :personal_owner_id dave-id}]
+
+                       Database   [{db-id      :id}           {:name "My Database"}]
+                       Table      [{no-schema-id :id}         {:name "Schemaless Table" :db_id db-id}]
+                       Table      [{schema-id    :id}         {:name        "Schema'd Table"
+                                                               :db_id       db-id
+                                                               :schema      "PUBLIC"}]
                        Card       [{c1-id  :id
-                                    c1-eid :entity_id}     {:name          "Some Question"
-                                                            :database_id   db-id
-                                                            :table_id      no-schema-id
-                                                            :collection_id coll-id
-                                                            :creator_id    mark-id
-                                                            :dataset_query "{\"json\": \"string values\"}"}]
+                                    c1-eid :entity_id}        {:name          "Some Question"
+                                                               :database_id   db-id
+                                                               :table_id      no-schema-id
+                                                               :collection_id coll-id
+                                                               :creator_id    mark-id
+                                                               :dataset_query "{\"json\": \"string values\"}"}]
                        Card       [{c2-id  :id
-                                    c2-eid :entity_id}     {:name          "Second Question"
-                                                            :database_id   db-id
-                                                            :table_id      schema-id
-                                                            :collection_id coll-id
-                                                            :creator_id    mark-id}]]
+                                    c2-eid :entity_id}        {:name          "Second Question"
+                                                               :database_id   db-id
+                                                               :table_id      schema-id
+                                                               :collection_id coll-id
+                                                               :creator_id    mark-id}]
+                       Dashboard  [{dash-id  :id
+                                    dash-eid :entity_id}      {:name          "Shared Dashboard"
+                                                               :collection_id coll-id
+                                                               :creator_id    mark-id
+                                                               :parameters    []}]
+                       Dashboard  [{other-dash-id :id
+                                    other-dash :entity_id}    {:name          "Dave's Dash"
+                                                               :collection_id dave-coll-id
+                                                               :creator_id    mark-id
+                                                               :parameters    []}]
+                       DashboardCard [{dc1-eid :entity_id}    {:card_id      c1-id
+                                                               :dashboard_id dash-id}]
+                       DashboardCard [{dc2-eid :entity_id}    {:card_id      c2-id
+                                                               :dashboard_id other-dash-id}]]
       (testing "table and database are extracted as [db schema table] triples"
         (let [ser (serdes.base/extract-one "Card" {} (select-one "Card" [:= :id c1-id]))]
           (is (= {:serdes/meta   [{:model "Card" :id c1-eid}]
@@ -128,4 +150,36 @@
                       {:model "Schema"     :id "PUBLIC"}
                       {:model "Table"      :id "Schema'd Table"}]
                      [{:model "Collection" :id coll-eid}]}
-                   (set (serdes.base/serdes-dependencies ser))))))))))
+                   (set (serdes.base/serdes-dependencies ser)))))))
+
+      (testing "collection filtering based on :user option"
+        (testing "only unowned collections are returned with no user"
+          (is (= ["Some Collection"]
+                 (->> (serdes.base/extract-all "Collection" {})
+                      (into [])
+                      (map :name)))))
+        (testing "unowned collections and the personal one with a user"
+          (is (= #{coll-eid mark-coll-eid}
+                 (by-model "Collection" (serdes.base/extract-all "Collection" {:user mark-id}))))
+          (is (= #{coll-eid dave-coll-eid}
+                 (by-model "Collection" (serdes.base/extract-all "Collection" {:user dave-id}))))))
+
+      (testing "dashboards are filtered based on :user"
+        (testing "dashboards in unowned collections are always returned"
+          (is (= #{dash-eid}
+                 (by-model "Dashboard" (serdes.base/extract-all "Dashboard" {}))))
+          (is (= #{dash-eid}
+                 (by-model "Dashboard" (serdes.base/extract-all "Dashboard" {:user mark-id})))))
+        (testing "dashboards in personal collections are returned for the :user"
+          (is (= #{dash-eid other-dash}
+                 (by-model "Dashboard" (serdes.base/extract-all "Dashboard" {:user dave-id}))))))
+
+      (testing "dashboard cards are filtered based on :user"
+        (testing "dashboard cards whose dashboards are in unowned collections are always returned"
+          (is (= #{dc1-eid}
+                 (by-model "DashboardCard" (serdes.base/extract-all "DashboardCard" {}))))
+          (is (= #{dc1-eid}
+                 (by-model "DashboardCard" (serdes.base/extract-all "DashboardCard" {:user mark-id})))))
+        (testing "dashboard cards whose dashboards are in personal collections are returned for the :user"
+          (is (= #{dc1-eid dc2-eid}
+                 (by-model "DashboardCard" (serdes.base/extract-all "DashboardCard" {:user dave-id})))))))))
diff --git a/enterprise/backend/test/metabase_enterprise/serialization/v2/yaml_test.clj b/enterprise/backend/test/metabase_enterprise/serialization/v2/yaml_test.clj
index 5079237a6bd884dcfbfea0e5c6589c80331a656c..50791e445832a9062764c67c4495bc4979fb62b5 100644
--- a/enterprise/backend/test/metabase_enterprise/serialization/v2/yaml_test.clj
+++ b/enterprise/backend/test/metabase_enterprise/serialization/v2/yaml_test.clj
@@ -115,7 +115,11 @@
                                                     {:database_id   (keyword (str "db" db))
                                                      :table_id      (keyword (str "t" (+ t (* 10 db))))
                                                      :collection_id (random-key "coll" 100)
-                                                     :creator_id    (random-key "u" 10)})}]]})
+                                                     :creator_id    (random-key "u" 10)})}]]
+                         :dashboard  [[100 {:refs {:collection_id   (random-key "coll" 100)
+                                                   :creator_id      (random-key "u" 10)}}]]
+                         :dashboard-card [[300 {:refs {:card_id      (random-key "c" 100)
+                                                       :dashboard_id (random-key "d" 100)}}]]})
       (let [extraction (into [] (extract/extract-metabase {}))
             entities   (reduce (fn [m entity]
                                  (update m (-> entity :serdes/meta last :model)
@@ -187,6 +191,34 @@
                          (update :updated_at u.date/format))
                      (yaml/from-file (io/file dump-dir "Card" filename))))))
 
+          (testing "for dashboards"
+            (is (= 100 (count (dir->file-set (io/file dump-dir "Dashboard")))))
+            (doseq [{:keys [collection_id creator_id entity_id]
+                     :as   dash}                                (get entities "Dashboard")
+                    :let [filename (str entity_id ".yaml")]]
+              (is (= (-> dash
+                         (dissoc :serdes/meta)
+                         (update :created_at u.date/format)
+                         (update :updated_at u.date/format))
+                     (yaml/from-file (io/file dump-dir "Dashboard" filename))))))
+
+          (testing "for dashboard cards"
+            (is (= 300
+                   (reduce + (for [dash (get entities "Dashboard")
+                                   :let [card-dir (io/file dump-dir "Dashboard" (:entity_id dash) "DashboardCard")]]
+                               (if (.exists card-dir)
+                                 (count (dir->file-set card-dir))
+                                 0)))))
+
+            (doseq [{:keys [dashboard_id entity_id]
+                     :as   dashcard}                (get entities "DashboardCard")
+                    :let [filename (str entity_id ".yaml")]]
+              (is (= (-> dashcard
+                         (dissoc :serdes/meta)
+                         (update :created_at u.date/format)
+                         (update :updated_at u.date/format))
+                     (yaml/from-file (io/file dump-dir "Dashboard" dashboard_id "DashboardCard" filename))))))
+
           (testing "for settings"
             (is (= (into {} (for [{:keys [key value]} (get entities "Setting")]
                               [key value]))
diff --git a/src/metabase/models/dashboard.clj b/src/metabase/models/dashboard.clj
index edb521ec9aa54c47520878b8a8859250ad33f4dc..38fd6e2657074883ca62053a9de88e0741302bb5 100644
--- a/src/metabase/models/dashboard.clj
+++ b/src/metabase/models/dashboard.clj
@@ -17,6 +17,7 @@
             [metabase.models.pulse-card :as pulse-card :refer [PulseCard]]
             [metabase.models.revision :as revision]
             [metabase.models.revision.diff :refer [build-sentence]]
+            [metabase.models.serialization.base :as serdes.base]
             [metabase.models.serialization.hash :as serdes.hash]
             [metabase.moderation :as moderation]
             [metabase.public-settings :as public-settings]
@@ -414,3 +415,41 @@
                                {(:parameter_id param) #{(assoc param :dashcard dashcard)}}))]
     (into {} (for [{param-key :id, :as param} (:parameters dashboard)]
                [(u/qualified-name param-key) (assoc param :mappings (get param-key->mappings param-key))]))))
+
+;;; +----------------------------------------------------------------------------------------------------------------+
+;;; |                                               SERIALIZATION                                                    |
+;;; +----------------------------------------------------------------------------------------------------------------+
+(defmethod serdes.base/extract-query "Dashboard" [_ {:keys [user]}]
+  ;; TODO This join over the subset of collections this user can see is shared by a few things - factor it out?
+  (serdes.base/raw-reducible-query
+    "Dashboard"
+    {:select     [:dash.*]
+     :from       [[:report_dashboard :dash]]
+     :left-join  [[:collection :coll] [:= :coll.id :dash.collection_id]]
+     :where      (if user
+                   [:or [:= :coll.personal_owner_id user] [:is :coll.personal_owner_id nil]]
+                   [:is :coll.personal_owner_id nil])}))
+
+;; TODO Maybe nest collections -> dashboards -> dashcards?
+(defmethod serdes.base/extract-one "Dashboard"
+  [_ _ {:keys [collection_id creator_id] :as dash}]
+  (let [email                (db/select-one-field :email 'User :id creator_id)
+        coll                 (db/select-one 'Collection :id collection_id)
+        {collection-eid :id} (serdes.base/infer-self-path "Collection" coll)]
+    (-> (serdes.base/extract-one-basics "Dashboard" dash)
+        (assoc :collection_id collection-eid
+               :creator_id    email))))
+
+(defmethod serdes.base/load-xform "Dashboard"
+  [{email          :creator_id
+    collection-eid :collection_id
+    :as dash}]
+  (let [coll-id   (serdes.base/lookup-by-id 'Collection collection-eid)
+        user-id   (db/select-one-id 'User :email email)]
+    (-> dash
+        (assoc :collection_id coll-id
+               :creator_id    user-id))))
+
+(defmethod serdes.base/serdes-dependencies "Dashboard"
+  [{:keys [collection_id]}]
+  [[{:model "Collection" :id collection_id}]])
diff --git a/src/metabase/models/dashboard_card.clj b/src/metabase/models/dashboard_card.clj
index 9172092214de5481583b9564cb5b86685194c492..5aaa171a07e2821ebce4a61617015d20d7b0873d 100644
--- a/src/metabase/models/dashboard_card.clj
+++ b/src/metabase/models/dashboard_card.clj
@@ -6,6 +6,7 @@
             [metabase.models.dashboard-card-series :refer [DashboardCardSeries]]
             [metabase.models.interface :as mi]
             [metabase.models.pulse-card :refer [PulseCard]]
+            [metabase.models.serialization.base :as serdes.base]
             [metabase.models.serialization.hash :as serdes.hash]
             [metabase.util :as u]
             [metabase.util.schema :as su]
@@ -203,3 +204,38 @@
       (db/delete! PulseCard :dashboard_card_id (:id dashboard-card))
       (db/delete! DashboardCard :id (:id dashboard-card)))
     (events/publish-event! :dashboard-remove-cards {:id id :actor_id user-id :dashcards [dashboard-card]})))
+
+;;; ----------------------------------------------- SERIALIZATION ----------------------------------------------------
+(defmethod serdes.base/extract-query "DashboardCard" [_ {:keys [user]}]
+  ;; TODO This join over the subset of collections this user can see is shared by a few things - factor it out?
+  (serdes.base/raw-reducible-query
+    "DashboardCard"
+    {:select     [:dc.*]
+     :from       [[:report_dashboardcard :dc]]
+     :left-join  [[:report_dashboard :dash] [:= :dash.id :dc.dashboard_id]
+                  [:collection :coll]       [:= :coll.id :dash.collection_id]]
+     :where      (if user
+                   [:or [:= :coll.personal_owner_id user] [:is :coll.personal_owner_id nil]]
+                   [:is :coll.personal_owner_id nil])}))
+
+(defmethod serdes.base/serdes-dependencies "DashboardCard" [{:keys [card_id dashboard_id]}]
+  [[{:model "Dashboard" :id dashboard_id}]
+   [{:model "Card"      :id card_id}]])
+
+(defmethod serdes.base/serdes-generate-path "DashboardCard" [_ dashcard]
+  [(serdes.base/infer-self-path "Dashboard" (db/select-one 'Dashboard :id (:dashboard_id dashcard)))
+   (serdes.base/infer-self-path "DashboardCard" dashcard)])
+
+(defmethod serdes.base/extract-one "DashboardCard"
+  [_ _ {:keys [card_id dashboard_id] :as dashcard}]
+  (let [card (db/select-one 'Card :id card_id)
+        dash (db/select-one 'Dashboard :id dashboard_id)]
+    (-> (serdes.base/extract-one-basics "DashboardCard" dashcard)
+        (assoc :card_id      (or (:entity_id card) (serdes.hash/identity-hash card)))
+        (assoc :dashboard_id (or (:entity_id dash) (serdes.hash/identity-hash dash))))))
+
+(defmethod serdes.base/load-xform "DashboardCard"
+  [{:keys [card_id dashboard_id] :as dashcard}]
+  (-> (serdes.base/load-xform-basics dashcard)
+      (assoc :card_id      (serdes.base/lookup-by-id 'Card card_id))
+      (assoc :dashboard_id (serdes.base/lookup-by-id 'Dashboard dashboard_id))))
diff --git a/test/metabase/test/generate.clj b/test/metabase/test/generate.clj
index 9e2b88c256d3a839ec7715200a43b72b7c577e72..5246c105f5053c359e1288c92cb511195c7ea30e 100644
--- a/test/metabase/test/generate.clj
+++ b/test/metabase/test/generate.clj
@@ -116,7 +116,11 @@
 
 ;; * native-query-snippet
 (s/def ::content ::not-empty-string)
-(s/def ::parameters #{[{:id "a"}]})
+
+(s/def :parameter/id   ::not-empty-string)
+(s/def :parameter/type ::base_type)
+(s/def ::parameter  (s/keys :req-un [:parameter/id :parameter/type]))
+(s/def ::parameters (s/coll-of ::parameter))
 
 ;; * pulse
 (s/def ::row pos-int?)