diff --git a/pkg/server/wire.go b/pkg/server/wire.go
index 61a10765e42..74418497d83 100644
--- a/pkg/server/wire.go
+++ b/pkg/server/wire.go
@@ -65,13 +65,13 @@ import (
 	plugindashboardsservice "github.com/grafana/grafana/pkg/services/plugindashboards/service"
 	"github.com/grafana/grafana/pkg/services/pluginsettings"
 	pluginSettings "github.com/grafana/grafana/pkg/services/pluginsettings/service"
+	"github.com/grafana/grafana/pkg/services/preference/prefimpl"
 	"github.com/grafana/grafana/pkg/services/query"
 	"github.com/grafana/grafana/pkg/services/queryhistory"
 	"github.com/grafana/grafana/pkg/services/quota"
 	"github.com/grafana/grafana/pkg/services/rendering"
 	"github.com/grafana/grafana/pkg/services/schemaloader"
 	"github.com/grafana/grafana/pkg/services/search"
-
 	"github.com/grafana/grafana/pkg/services/searchV2"
 	"github.com/grafana/grafana/pkg/services/secrets"
 	secretsDatabase "github.com/grafana/grafana/pkg/services/secrets/database"
@@ -235,6 +235,7 @@ var wireBasicSet = wire.NewSet(
 	wire.Bind(new(alerting.DashAlertExtractor), new(*alerting.DashAlertExtractorService)),
 	comments.ProvideService,
 	guardian.ProvideService,
+	prefimpl.ProvideService,
 	avatar.ProvideAvatarCacheServer,
 	authproxy.ProvideAuthProxy,
 	statscollector.ProvideService,
diff --git a/pkg/services/preference/model.go b/pkg/services/preference/model.go
new file mode 100644
index 00000000000..ae8b5ca665b
--- /dev/null
+++ b/pkg/services/preference/model.go
@@ -0,0 +1,100 @@
+package pref
+
+import (
+	"bytes"
+	"encoding/json"
+	"errors"
+	"time"
+)
+
+var ErrPrefNotFound = errors.New("preference not found")
+
+type Preference struct {
+	ID              int64   `xorm:"pk autoincr 'id'"`
+	OrgID           int64   `xorm:"org_id"`
+	UserID          int64   `xorm:"user_id"`
+	TeamID          int64   `xorm:"team_id"`
+	Teams           []int64 `xorm:"extends"`
+	Version         int
+	HomeDashboardID int64 `xorm:"home_dashboard_id"`
+	Timezone        string
+	WeekStart       string
+	Theme           string
+	Created         time.Time
+	Updated         time.Time
+	JSONData        *PreferenceJSONData `xorm:"json_data"`
+}
+
+type GetPreferenceWithDefaultsQuery struct {
+	Teams  []int64
+	OrgID  int64
+	UserID int64
+}
+
+type GetPreferenceQuery struct {
+	OrgID  int64
+	UserID int64
+	TeamID int64
+}
+
+type SavePreferenceCommand struct {
+	UserID int64
+	OrgID  int64
+	TeamID int64
+
+	HomeDashboardID int64                   `json:"homeDashboardId,omitempty"`
+	Timezone        string                  `json:"timezone,omitempty"`
+	WeekStart       string                  `json:"weekStart,omitempty"`
+	Theme           string                  `json:"theme,omitempty"`
+	Navbar          *NavbarPreference       `json:"navbar,omitempty"`
+	QueryHistory    *QueryHistoryPreference `json:"queryHistory,omitempty"`
+}
+
+type PatchPreferenceCommand struct {
+	UserID int64
+	OrgID  int64
+	TeamID int64
+
+	HomeDashboardID *int64                  `json:"homeDashboardId,omitempty"`
+	Timezone        *string                 `json:"timezone,omitempty"`
+	WeekStart       *string                 `json:"weekStart,omitempty"`
+	Theme           *string                 `json:"theme,omitempty"`
+	Navbar          *NavbarPreference       `json:"navbar,omitempty"`
+	QueryHistory    *QueryHistoryPreference `json:"queryHistory,omitempty"`
+}
+
+type NavLink struct {
+	ID     string `json:"id,omitempty"`
+	Text   string `json:"text,omitempty"`
+	Url    string `json:"url,omitempty"`
+	Target string `json:"target,omitempty"`
+}
+
+type NavbarPreference struct {
+	SavedItems []NavLink `json:"savedItems"`
+}
+
+type PreferenceJSONData struct {
+	Navbar       NavbarPreference       `json:"navbar"`
+	QueryHistory QueryHistoryPreference `json:"queryHistory"`
+}
+
+type QueryHistoryPreference struct {
+	HomeTab string `json:"homeTab"`
+}
+
+func (j *PreferenceJSONData) FromDB(data []byte) error {
+	dec := json.NewDecoder(bytes.NewBuffer(data))
+	dec.UseNumber()
+	return dec.Decode(j)
+}
+
+func (j *PreferenceJSONData) ToDB() ([]byte, error) {
+	if j == nil {
+		return nil, nil
+	}
+
+	return json.Marshal(j)
+}
+
+func (p Preference) TableName() string { return "preferences" }
diff --git a/pkg/services/preference/pref.go b/pkg/services/preference/pref.go
new file mode 100644
index 00000000000..5cf17789a25
--- /dev/null
+++ b/pkg/services/preference/pref.go
@@ -0,0 +1,13 @@
+package pref
+
+import (
+	"context"
+)
+
+type Service interface {
+	GetWithDefaults(context.Context, *GetPreferenceWithDefaultsQuery) (*Preference, error)
+	Get(context.Context, *GetPreferenceQuery) (*Preference, error)
+	Save(context.Context, *SavePreferenceCommand) error
+	Patch(ctx context.Context, cmd *PatchPreferenceCommand) error
+	GetDefaults() *Preference
+}
diff --git a/pkg/services/preference/prefimpl/pref.go b/pkg/services/preference/prefimpl/pref.go
new file mode 100644
index 00000000000..c43fb7d98c5
--- /dev/null
+++ b/pkg/services/preference/prefimpl/pref.go
@@ -0,0 +1,203 @@
+package prefimpl
+
+import (
+	"context"
+	"errors"
+	"time"
+
+	pref "github.com/grafana/grafana/pkg/services/preference"
+	"github.com/grafana/grafana/pkg/services/sqlstore/db"
+	"github.com/grafana/grafana/pkg/setting"
+)
+
+type Service struct {
+	store store
+	cfg   *setting.Cfg
+}
+
+func ProvideService(db db.DB, cfg *setting.Cfg) *Service {
+	return &Service{
+		store: &sqlStore{
+			db: db,
+		},
+		cfg: cfg,
+	}
+}
+
+func (s *Service) GetWithDefaults(ctx context.Context, query *pref.GetPreferenceWithDefaultsQuery) (*pref.Preference, error) {
+	listQuery := &pref.Preference{
+		Teams:  query.Teams,
+		OrgID:  query.OrgID,
+		UserID: query.UserID,
+	}
+	prefs, err := s.store.List(ctx, listQuery)
+	if err != nil {
+		return nil, err
+	}
+
+	res := s.GetDefaults()
+	for _, p := range prefs {
+		if p.Theme != "" {
+			res.Theme = p.Theme
+		}
+		if p.Timezone != "" {
+			res.Timezone = p.Timezone
+		}
+		if p.WeekStart != "" {
+			res.WeekStart = p.WeekStart
+		}
+		if p.HomeDashboardID != 0 {
+			res.HomeDashboardID = p.HomeDashboardID
+		}
+		if p.JSONData != nil {
+			res.JSONData = p.JSONData
+		}
+	}
+
+	return res, err
+}
+
+func (s *Service) Get(ctx context.Context, query *pref.GetPreferenceQuery) (*pref.Preference, error) {
+	getPref := &pref.Preference{
+		OrgID:  query.OrgID,
+		UserID: query.UserID,
+		TeamID: query.TeamID,
+	}
+	prefs, err := s.store.Get(ctx, getPref)
+	if err != nil && !errors.Is(err, pref.ErrPrefNotFound) {
+		return nil, err
+	}
+	return prefs, nil
+}
+
+func (s *Service) Save(ctx context.Context, cmd *pref.SavePreferenceCommand) error {
+	preference, err := s.store.Get(ctx, &pref.Preference{
+		OrgID:  cmd.OrgID,
+		UserID: cmd.UserID,
+		TeamID: cmd.TeamID,
+	})
+	if err != nil {
+		if errors.Is(err, pref.ErrPrefNotFound) {
+			preference := &pref.Preference{
+				UserID:          cmd.UserID,
+				OrgID:           cmd.OrgID,
+				TeamID:          cmd.TeamID,
+				HomeDashboardID: cmd.HomeDashboardID,
+				Timezone:        cmd.Timezone,
+				WeekStart:       cmd.WeekStart,
+				Theme:           cmd.Theme,
+				Created:         time.Now(),
+				Updated:         time.Now(),
+			}
+			_, err = s.store.Insert(ctx, preference)
+			if err != nil {
+				return err
+			}
+		}
+		return err
+	}
+	preference.Timezone = cmd.Timezone
+	preference.WeekStart = cmd.WeekStart
+	preference.Theme = cmd.Theme
+	preference.Updated = time.Now()
+	preference.Version += 1
+	preference.JSONData = &pref.PreferenceJSONData{}
+
+	if cmd.Navbar != nil {
+		preference.JSONData.Navbar = *cmd.Navbar
+	}
+	if cmd.QueryHistory != nil {
+		preference.JSONData.QueryHistory = *cmd.QueryHistory
+	}
+	return s.store.Update(ctx, preference)
+}
+
+func (s *Service) Patch(ctx context.Context, cmd *pref.PatchPreferenceCommand) error {
+	var exists bool
+	preference, err := s.store.Get(ctx, &pref.Preference{
+		OrgID:  cmd.OrgID,
+		UserID: cmd.UserID,
+		TeamID: cmd.TeamID,
+	})
+	if err != nil && !errors.Is(err, pref.ErrPrefNotFound) {
+		return err
+	}
+
+	if errors.Is(err, pref.ErrPrefNotFound) {
+		preference = &pref.Preference{
+			UserID:   cmd.UserID,
+			OrgID:    cmd.OrgID,
+			TeamID:   cmd.TeamID,
+			Created:  time.Now(),
+			JSONData: &pref.PreferenceJSONData{},
+		}
+	} else {
+		exists = true
+	}
+
+	if cmd.Navbar != nil {
+		if preference.JSONData == nil {
+			preference.JSONData = &pref.PreferenceJSONData{}
+		}
+		if cmd.Navbar.SavedItems != nil {
+			preference.JSONData.Navbar.SavedItems = cmd.Navbar.SavedItems
+		}
+	}
+
+	if cmd.QueryHistory != nil {
+		if preference.JSONData == nil {
+			preference.JSONData = &pref.PreferenceJSONData{}
+		}
+		if cmd.QueryHistory.HomeTab != "" {
+			preference.JSONData.QueryHistory.HomeTab = cmd.QueryHistory.HomeTab
+		}
+	}
+
+	if cmd.HomeDashboardID != nil {
+		preference.HomeDashboardID = *cmd.HomeDashboardID
+	}
+
+	if cmd.Timezone != nil {
+		preference.Timezone = *cmd.Timezone
+	}
+
+	if cmd.WeekStart != nil {
+		preference.WeekStart = *cmd.WeekStart
+	}
+
+	if cmd.Theme != nil {
+		preference.Theme = *cmd.Theme
+	}
+
+	preference.Updated = time.Now()
+	preference.Version += 1
+
+	// Wrap this in an if statement to maintain backwards compatibility
+	if cmd.Navbar != nil {
+		if preference.JSONData == nil {
+			preference.JSONData = &pref.PreferenceJSONData{}
+		}
+		if cmd.Navbar.SavedItems != nil {
+			preference.JSONData.Navbar.SavedItems = cmd.Navbar.SavedItems
+		}
+	}
+
+	if exists {
+		err = s.store.Update(ctx, preference)
+	} else {
+		_, err = s.store.Insert(ctx, preference)
+	}
+	return err
+}
+
+func (s *Service) GetDefaults() *pref.Preference {
+	defaults := &pref.Preference{
+		Theme:           s.cfg.DefaultTheme,
+		Timezone:        s.cfg.DateFormats.DefaultTimezone,
+		WeekStart:       s.cfg.DateFormats.DefaultWeekStart,
+		HomeDashboardID: 0,
+		JSONData:        &pref.PreferenceJSONData{},
+	}
+
+	return defaults
+}
diff --git a/pkg/services/preference/prefimpl/pref_test.go b/pkg/services/preference/prefimpl/pref_test.go
new file mode 100644
index 00000000000..b1e439b6830
--- /dev/null
+++ b/pkg/services/preference/prefimpl/pref_test.go
@@ -0,0 +1,446 @@
+package prefimpl
+
+import (
+	"context"
+	"testing"
+
+	"github.com/google/go-cmp/cmp"
+	pref "github.com/grafana/grafana/pkg/services/preference"
+	"github.com/grafana/grafana/pkg/setting"
+	"github.com/stretchr/testify/require"
+)
+
+func TestPreferencesService(t *testing.T) {
+	prefStoreFake := newPreferenceStoreFake()
+	prefService := &Service{
+		store: prefStoreFake,
+	}
+
+	emptyNavbarPreferences := pref.NavbarPreference{}
+	userNavbarPreferences := pref.NavbarPreference{
+		SavedItems: []pref.NavLink{{
+			ID:   "explore",
+			Text: "Explore",
+			Url:  "/explore",
+		}},
+	}
+	orgNavbarPreferences := pref.NavbarPreference{
+		SavedItems: []pref.NavLink{{
+			ID:   "alerting",
+			Text: "Alerting",
+			Url:  "/alerting",
+		}},
+	}
+	team1NavbarPreferences := pref.NavbarPreference{
+		SavedItems: []pref.NavLink{{
+			ID:   "dashboards",
+			Text: "Dashboards",
+			Url:  "/dashboards",
+		}},
+	}
+	team2NavbarPreferences := pref.NavbarPreference{
+		SavedItems: []pref.NavLink{{
+			ID:   "home",
+			Text: "Home",
+			Url:  "/home",
+		}},
+	}
+
+	emptyQueryPreference := pref.QueryHistoryPreference{}
+
+	queryPreference := pref.QueryHistoryPreference{
+		HomeTab: "hometab",
+	}
+
+	queryPreference2 := pref.QueryHistoryPreference{
+		HomeTab: "hometab",
+	}
+
+	emptyPreferencesJsonData := pref.PreferenceJSONData{
+		Navbar: emptyNavbarPreferences,
+	}
+	userPreferencesJsonData := pref.PreferenceJSONData{
+		Navbar:       userNavbarPreferences,
+		QueryHistory: queryPreference,
+	}
+	orgPreferencesJsonData := pref.PreferenceJSONData{
+		Navbar: orgNavbarPreferences,
+	}
+	team2PreferencesJsonData := pref.PreferenceJSONData{
+		Navbar: team2NavbarPreferences,
+	}
+	team1PreferencesJsonData := pref.PreferenceJSONData{
+		Navbar: team1NavbarPreferences,
+	}
+
+	t.Run("GetDefaults should return defaults", func(t *testing.T) {
+		prefService.cfg = setting.NewCfg()
+		prefService.cfg.DefaultTheme = "light"
+		prefService.cfg.DateFormats.DefaultTimezone = "UTC"
+
+		preferences := prefService.GetDefaults()
+		expected := &pref.Preference{
+			Theme:           "light",
+			Timezone:        "UTC",
+			HomeDashboardID: 0,
+			JSONData:        &pref.PreferenceJSONData{},
+		}
+		if diff := cmp.Diff(expected, preferences); diff != "" {
+			t.Fatalf("Result mismatch (-want +got):\n%s", diff)
+		}
+	})
+
+	t.Run("GetDefaults with no saved preferences should return defaults", func(t *testing.T) {
+		prefStoreFake.ExpectedPreference = &pref.Preference{
+			Theme:    "light",
+			Timezone: "UTC",
+		}
+		query := &pref.GetPreferenceWithDefaultsQuery{}
+		preferences, err := prefService.GetWithDefaults(context.Background(), query)
+		require.NoError(t, err)
+		expected := &pref.Preference{
+			Theme:           "light",
+			Timezone:        "UTC",
+			HomeDashboardID: 0,
+			JSONData:        &emptyPreferencesJsonData,
+		}
+		if diff := cmp.Diff(expected, preferences); diff != "" {
+			t.Fatalf("Result mismatch (-want +got):\n%s", diff)
+		}
+	})
+
+	t.Run("GetWithDefaults with saved org and user home dashboard should return user home dashboard", func(t *testing.T) {
+		prefStoreFake.ExpectedPreference = &pref.Preference{}
+		prefStoreFake.ExpectedListPreferences = []*pref.Preference{
+			{
+				OrgID:           1,
+				HomeDashboardID: 1,
+				Theme:           "dark",
+				Timezone:        "UTC",
+			},
+			{
+				OrgID:           1,
+				UserID:          1,
+				HomeDashboardID: 4,
+				Theme:           "light",
+				WeekStart:       "1",
+			},
+		}
+		query := &pref.GetPreferenceWithDefaultsQuery{OrgID: 1, UserID: 1}
+		preferences, err := prefService.GetWithDefaults(context.Background(), query)
+		require.NoError(t, err)
+		expected := &pref.Preference{
+			Theme:           "light",
+			Timezone:        "UTC",
+			WeekStart:       "1",
+			HomeDashboardID: 4,
+			JSONData:        &pref.PreferenceJSONData{},
+		}
+		if diff := cmp.Diff(expected, preferences); diff != "" {
+			t.Fatalf("Result mismatch (-want +got):\n%s", diff)
+		}
+	})
+
+	t.Run("GetWithDefaults with saved org and other user home dashboard should return org home dashboard", func(t *testing.T) {
+		prefStoreFake.ExpectedPreference = &pref.Preference{}
+		prefStoreFake.ExpectedListPreferences = []*pref.Preference{
+			{
+				OrgID:           1,
+				HomeDashboardID: 1,
+				Theme:           "dark",
+				Timezone:        "UTC",
+				WeekStart:       "1",
+			},
+			{
+				OrgID:           1,
+				UserID:          1,
+				HomeDashboardID: 4,
+				Theme:           "light",
+				Timezone:        "browser",
+				WeekStart:       "2",
+			},
+		}
+		prefService.GetDefaults().HomeDashboardID = 1
+		query := &pref.GetPreferenceWithDefaultsQuery{OrgID: 1, UserID: 2}
+		preferences, err := prefService.GetWithDefaults(context.Background(), query)
+		require.NoError(t, err)
+		expected := &pref.Preference{
+			Theme:           "light",
+			Timezone:        "browser",
+			WeekStart:       "2",
+			HomeDashboardID: 4,
+			JSONData:        &pref.PreferenceJSONData{},
+		}
+		if diff := cmp.Diff(expected, preferences); diff != "" {
+			t.Fatalf("Result mismatch (-want +got):\n%s", diff)
+		}
+	})
+
+	t.Run("GetPreferencesWithDefaults with saved org and user json data should return user json data", func(t *testing.T) {
+		prefStoreFake.ExpectedPreference = &pref.Preference{}
+		prefStoreFake.ExpectedListPreferences = []*pref.Preference{
+			{
+				OrgID:    1,
+				JSONData: &orgPreferencesJsonData,
+			},
+			{
+				OrgID:    1,
+				UserID:   1,
+				JSONData: &userPreferencesJsonData,
+			},
+		}
+		query := &pref.GetPreferenceWithDefaultsQuery{OrgID: 1, UserID: 1}
+		preference, err := prefService.GetWithDefaults(context.Background(), query)
+		require.NoError(t, err)
+		require.Equal(t, &pref.Preference{
+			Theme:    "light",
+			JSONData: &userPreferencesJsonData,
+			Timezone: "UTC",
+		}, preference)
+	})
+
+	t.Run("GetWithDefaults with saved org and teams json data should return last team json data", func(t *testing.T) {
+		prefStoreFake.ExpectedPreference = &pref.Preference{}
+		prefStoreFake.ExpectedListPreferences = []*pref.Preference{
+			{
+				OrgID:    1,
+				JSONData: &orgPreferencesJsonData,
+			},
+			{
+				OrgID:    1,
+				TeamID:   2,
+				JSONData: &team1PreferencesJsonData,
+			},
+			{
+				OrgID:    1,
+				TeamID:   3,
+				JSONData: &team2PreferencesJsonData,
+			},
+		}
+		query := &pref.GetPreferenceWithDefaultsQuery{
+			OrgID: 1, Teams: []int64{2, 3},
+		}
+		preference, err := prefService.GetWithDefaults(context.Background(), query)
+		require.NoError(t, err)
+		require.Equal(t, &pref.Preference{
+			Timezone: "UTC",
+			Theme:    "light",
+			JSONData: &team2PreferencesJsonData,
+		}, preference)
+	})
+
+	t.Run("GetWithDefaults with saved org and teams home dashboard should return last team home dashboard", func(t *testing.T) {
+		prefStoreFake.ExpectedPreference = &pref.Preference{
+			Theme:    "dark",
+			Timezone: "UTC",
+		}
+		prefStoreFake.ExpectedListPreferences = []*pref.Preference{
+			{
+				OrgID:           1,
+				HomeDashboardID: 1,
+				Theme:           "light",
+				Timezone:        "browser",
+				WeekStart:       "1",
+			},
+			{
+				OrgID:           1,
+				UserID:          1,
+				HomeDashboardID: 4,
+				Theme:           "light",
+				Timezone:        "browser",
+				WeekStart:       "2",
+			},
+		}
+
+		query := &pref.GetPreferenceWithDefaultsQuery{OrgID: 1, Teams: []int64{2, 3}}
+		preferences, err := prefService.GetWithDefaults(context.Background(), query)
+		require.NoError(t, err)
+		expected := &pref.Preference{
+			Theme:           "light",
+			Timezone:        "browser",
+			WeekStart:       "2",
+			HomeDashboardID: 4,
+			JSONData:        &pref.PreferenceJSONData{},
+		}
+		if diff := cmp.Diff(expected, preferences); diff != "" {
+			t.Fatalf("Result mismatch (-want +got):\n%s", diff)
+		}
+	})
+
+	t.Run("SavePreferences for a user should store correct values", func(t *testing.T) {
+		prefStoreFake.ExpectedPreference = &pref.Preference{
+			ID:              1,
+			OrgID:           1,
+			UserID:          3,
+			TeamID:          6,
+			HomeDashboardID: 5,
+			Timezone:        "browser",
+			WeekStart:       "1",
+			Theme:           "dark",
+		}
+		err := prefService.Save(context.Background(),
+			&pref.SavePreferenceCommand{
+				Theme:           "dark",
+				Timezone:        "browser",
+				HomeDashboardID: 5,
+				WeekStart:       "1"},
+		)
+		require.NoError(t, err)
+	})
+
+	t.Run("SavePreferences for a user should store correct values, when preference not found", func(t *testing.T) {
+		prefStoreFake.ExpectedGetError = pref.ErrPrefNotFound
+		prefStoreFake.ExpectedPreference = &pref.Preference{
+			ID:              1,
+			OrgID:           1,
+			UserID:          3,
+			TeamID:          6,
+			HomeDashboardID: 5,
+			Timezone:        "browser",
+			WeekStart:       "1",
+			Theme:           "dark",
+		}
+		err := prefService.Save(context.Background(),
+			&pref.SavePreferenceCommand{
+				Theme:           "dark",
+				Timezone:        "browser",
+				HomeDashboardID: 5,
+				WeekStart:       "1",
+			},
+		)
+		require.NoError(t, err)
+		prefStoreFake.ExpectedGetError = nil
+	})
+
+	t.Run("SavePreferences for a user should store correct values with nav and query history", func(t *testing.T) {
+		prefStoreFake.ExpectedPreference = &pref.Preference{
+			ID:              1,
+			OrgID:           1,
+			UserID:          3,
+			TeamID:          6,
+			HomeDashboardID: 5,
+			Timezone:        "browser",
+			WeekStart:       "1",
+			Theme:           "dark",
+			JSONData:        &userPreferencesJsonData,
+		}
+		err := prefService.Save(context.Background(),
+			&pref.SavePreferenceCommand{
+				Theme:           "dark",
+				Timezone:        "browser",
+				HomeDashboardID: 5,
+				WeekStart:       "1",
+				Navbar:          &userNavbarPreferences,
+				QueryHistory:    &emptyQueryPreference,
+			},
+		)
+		require.NoError(t, err)
+	})
+
+	t.Run("Get for a user should store correct values", func(t *testing.T) {
+		prefStoreFake.ExpectedPreference = &pref.Preference{
+			HomeDashboardID: 5,
+			Timezone:        "browser",
+			WeekStart:       "1",
+			Theme:           "dark",
+		}
+		preference, err := prefService.Get(context.Background(), &pref.GetPreferenceQuery{})
+		require.NoError(t, err)
+
+		expected := &pref.Preference{
+			ID:              preference.ID,
+			Version:         preference.Version,
+			HomeDashboardID: 5,
+			Timezone:        "browser",
+			WeekStart:       "1",
+			Theme:           "dark",
+			Created:         preference.Created,
+			Updated:         preference.Updated,
+		}
+		if diff := cmp.Diff(expected, preference); diff != "" {
+			t.Fatalf("Result mismatch (-want +got):\n%s", diff)
+		}
+	})
+
+	t.Run("Patch for a user should store correct values", func(t *testing.T) {
+		darkTheme := "dark"
+		prefStoreFake.ExpectedPreference = &pref.Preference{
+			HomeDashboardID: 5,
+			Timezone:        "browser",
+			WeekStart:       "1",
+			Theme:           "dark",
+			JSONData:        &userPreferencesJsonData,
+		}
+		err := prefService.Patch(context.Background(),
+			&pref.PatchPreferenceCommand{
+				Theme:        &darkTheme,
+				Navbar:       &userNavbarPreferences,
+				QueryHistory: &queryPreference2,
+			})
+		require.NoError(t, err)
+	})
+
+	t.Run("Patch for a user should store correct values, without navbar and query history", func(t *testing.T) {
+		darkTheme := "dark"
+		prefStoreFake.ExpectedPreference = &pref.Preference{
+			HomeDashboardID: 5,
+			Timezone:        "browser",
+			WeekStart:       "1",
+			Theme:           "dark",
+		}
+		err := prefService.Patch(context.Background(),
+			&pref.PatchPreferenceCommand{
+				Theme:        &darkTheme,
+				Navbar:       &userNavbarPreferences,
+				QueryHistory: &queryPreference2,
+			})
+		require.NoError(t, err)
+	})
+
+	t.Run("Patch for a user should store correct values, when preference not found", func(t *testing.T) {
+		timezone := "browser"
+		weekStart := "1"
+		homeDashboardID := int64(5)
+		prefStoreFake.ExpectedGetError = pref.ErrPrefNotFound
+		prefStoreFake.ExpectedPreference = nil
+
+		err := prefService.Patch(context.Background(),
+			&pref.PatchPreferenceCommand{
+				HomeDashboardID: &homeDashboardID,
+				Timezone:        &timezone,
+				WeekStart:       &weekStart,
+				Navbar:          &emptyNavbarPreferences,
+				QueryHistory:    &emptyQueryPreference,
+			})
+		require.NoError(t, err)
+		prefStoreFake.ExpectedGetError = nil
+	})
+}
+
+type FakePreferenceStore struct {
+	ExpectedPreference      *pref.Preference
+	ExpectedListPreferences []*pref.Preference
+	ExpectedID              int64
+	ExpectedError           error
+	ExpectedGetError        error
+}
+
+func newPreferenceStoreFake() *FakePreferenceStore {
+	return &FakePreferenceStore{}
+}
+
+func (f *FakePreferenceStore) List(ctx context.Context, query *pref.Preference) ([]*pref.Preference, error) {
+	return f.ExpectedListPreferences, f.ExpectedError
+}
+
+func (f *FakePreferenceStore) Get(ctx context.Context, query *pref.Preference) (*pref.Preference, error) {
+	return f.ExpectedPreference, f.ExpectedGetError
+}
+
+func (f *FakePreferenceStore) Insert(ctx context.Context, cmd *pref.Preference) (int64, error) {
+	return f.ExpectedID, f.ExpectedError
+}
+
+func (f *FakePreferenceStore) Update(ctx context.Context, cmd *pref.Preference) error {
+	return f.ExpectedError
+}
diff --git a/pkg/services/preference/prefimpl/store.go b/pkg/services/preference/prefimpl/store.go
new file mode 100644
index 00000000000..139b723363b
--- /dev/null
+++ b/pkg/services/preference/prefimpl/store.go
@@ -0,0 +1,88 @@
+package prefimpl
+
+import (
+	"context"
+	"strings"
+
+	pref "github.com/grafana/grafana/pkg/services/preference"
+	"github.com/grafana/grafana/pkg/services/sqlstore"
+	"github.com/grafana/grafana/pkg/services/sqlstore/db"
+)
+
+type store interface {
+	Get(context.Context, *pref.Preference) (*pref.Preference, error)
+	List(context.Context, *pref.Preference) ([]*pref.Preference, error)
+	Insert(context.Context, *pref.Preference) (int64, error)
+	Update(context.Context, *pref.Preference) error
+}
+
+type sqlStore struct {
+	db db.DB
+}
+
+func (s *sqlStore) Get(ctx context.Context, query *pref.Preference) (*pref.Preference, error) {
+	var prefs pref.Preference
+	err := s.db.WithDbSession(ctx, func(sess *sqlstore.DBSession) error {
+		exist, err := sess.Where("org_id=? AND user_id=? AND team_id=?", query.OrgID, query.UserID, query.TeamID).Get(&prefs)
+		if err != nil {
+			return err
+		}
+		if !exist {
+			return pref.ErrPrefNotFound
+		}
+		return nil
+	})
+	if err != nil {
+		return nil, err
+	}
+	return &prefs, nil
+}
+
+func (s *sqlStore) List(ctx context.Context, query *pref.Preference) ([]*pref.Preference, error) {
+	prefs := make([]*pref.Preference, 0)
+	params := make([]interface{}, 0)
+	filter := ""
+
+	if len(query.Teams) > 0 {
+		filter = "(org_id=? AND team_id IN (?" + strings.Repeat(",?", len(query.Teams)-1) + ")) OR "
+		params = append(params, query.OrgID)
+		for _, v := range query.Teams {
+			params = append(params, v)
+		}
+	}
+
+	filter += "(org_id=? AND user_id=? AND team_id=0) OR (org_id=? AND team_id=0 AND user_id=0)"
+	params = append(params, query.OrgID)
+	params = append(params, query.UserID)
+	params = append(params, query.OrgID)
+
+	err := s.db.WithDbSession(ctx, func(dbSession *sqlstore.DBSession) error {
+		err := dbSession.Where(filter, params...).
+			OrderBy("user_id ASC, team_id ASC").
+			Find(&prefs)
+
+		if err != nil {
+			return err
+		}
+
+		return nil
+	})
+	return prefs, err
+}
+
+func (s *sqlStore) Update(ctx context.Context, cmd *pref.Preference) error {
+	return s.db.WithTransactionalDbSession(ctx, func(sess *sqlstore.DBSession) error {
+		_, err := sess.ID(cmd.ID).AllCols().Update(cmd)
+		return err
+	})
+}
+
+func (s *sqlStore) Insert(ctx context.Context, cmd *pref.Preference) (int64, error) {
+	var ID int64
+	var err error
+	err = s.db.WithTransactionalDbSession(ctx, func(sess *sqlstore.DBSession) error {
+		ID, err = sess.Insert(cmd)
+		return err
+	})
+	return ID, err
+}
diff --git a/pkg/services/preference/prefimpl/store_test.go b/pkg/services/preference/prefimpl/store_test.go
new file mode 100644
index 00000000000..773b6dea57b
--- /dev/null
+++ b/pkg/services/preference/prefimpl/store_test.go
@@ -0,0 +1,170 @@
+//go:build integration
+// +build integration
+
+package prefimpl
+
+import (
+	"context"
+	"testing"
+	"time"
+
+	"github.com/google/go-cmp/cmp"
+	"github.com/grafana/grafana/pkg/models"
+	pref "github.com/grafana/grafana/pkg/services/preference"
+	"github.com/grafana/grafana/pkg/services/sqlstore"
+	"github.com/stretchr/testify/require"
+)
+
+func TestPreferencesDataAccess(t *testing.T) {
+	ss := sqlstore.InitTestDB(t)
+	prefStore := sqlStore{db: ss}
+	orgNavbarPreferences := pref.NavbarPreference{
+		SavedItems: []pref.NavLink{{
+			ID:   "alerting",
+			Text: "Alerting",
+			Url:  "/alerting",
+		}},
+	}
+
+	t.Run("Get with saved org and user home dashboard returns not found", func(t *testing.T) {
+		query := &pref.Preference{OrgID: 1, UserID: 1, TeamID: 2}
+		prefs, err := prefStore.Get(context.Background(), query)
+		require.EqualError(t, err, pref.ErrPrefNotFound.Error())
+		require.Nil(t, prefs)
+	})
+
+	t.Run("Get with saved org and user home dashboard should return user home dashboard", func(t *testing.T) {
+		_, err := prefStore.Insert(context.Background(),
+			&pref.Preference{
+				OrgID:           1,
+				UserID:          1,
+				HomeDashboardID: 4,
+				TeamID:          2,
+				Created:         time.Now(),
+				Updated:         time.Now(),
+			})
+		require.NoError(t, err)
+
+		query := &pref.Preference{OrgID: 1, UserID: 1, TeamID: 2}
+		prefs, err := prefStore.Get(context.Background(), query)
+		require.NoError(t, err)
+		require.Equal(t, int64(4), prefs.HomeDashboardID)
+	})
+
+	t.Run("List with saved org and user home dashboard should return user home dashboard", func(t *testing.T) {
+		_, err := prefStore.Insert(context.Background(),
+			&pref.Preference{
+				OrgID:           1,
+				UserID:          1,
+				TeamID:          3,
+				HomeDashboardID: 1,
+				Created:         time.Now(),
+				Updated:         time.Now(),
+			})
+		require.NoError(t, err)
+
+		query := &pref.Preference{OrgID: 1, UserID: 1, Teams: []int64{2}}
+		prefs, err := prefStore.List(context.Background(), query)
+		require.NoError(t, err)
+		require.Equal(t, int64(4), prefs[0].HomeDashboardID)
+	})
+
+	t.Run("List with saved org and other user home dashboard should return org home dashboard", func(t *testing.T) {
+		_, err := prefStore.Insert(context.Background(),
+			&pref.Preference{
+				OrgID:           1,
+				UserID:          2,
+				TeamID:          3,
+				HomeDashboardID: 1,
+				Created:         time.Now(),
+				Updated:         time.Now(),
+			})
+		require.NoError(t, err)
+
+		query := &pref.Preference{OrgID: 1, UserID: 1, Teams: []int64{3}}
+		prefs, err := prefStore.List(context.Background(), query)
+		require.NoError(t, err)
+		require.Equal(t, int64(1), prefs[0].HomeDashboardID)
+		require.Equal(t, int64(1), prefs[1].HomeDashboardID)
+	})
+
+	t.Run("List with saved org and teams home dashboard should return last team home dashboard", func(t *testing.T) {
+		query := &pref.Preference{
+			OrgID: 1, Teams: []int64{2, 3},
+		}
+		prefs, err := prefStore.List(context.Background(), query)
+		require.NoError(t, err)
+		require.Equal(t, int64(4), prefs[0].HomeDashboardID)
+		require.Equal(t, int64(1), prefs[1].HomeDashboardID)
+		require.Equal(t, int64(1), prefs[2].HomeDashboardID)
+	})
+
+	t.Run("List with saved org and other teams home dashboard should return org home dashboard", func(t *testing.T) {
+		_, err := prefStore.Insert(context.Background(), &pref.Preference{OrgID: 1, HomeDashboardID: 1, Created: time.Now(), Updated: time.Now()})
+		require.NoError(t, err)
+		_, err = prefStore.Insert(context.Background(), &pref.Preference{OrgID: 1, TeamID: 2, HomeDashboardID: 2, Created: time.Now(), Updated: time.Now()})
+		require.NoError(t, err)
+		_, err = prefStore.Insert(context.Background(), &pref.Preference{OrgID: 1, TeamID: 3, HomeDashboardID: 3, Created: time.Now(), Updated: time.Now()})
+		require.NoError(t, err)
+
+		query := &pref.Preference{OrgID: 1}
+		prefs, err := prefStore.List(context.Background(), query)
+		require.NoError(t, err)
+		require.Equal(t, int64(1), prefs[0].HomeDashboardID)
+	})
+
+	t.Run("Update for a user should only modify a single value", func(t *testing.T) {
+		ss := sqlstore.InitTestDB(t)
+		prefStore := sqlStore{db: ss}
+		id, err := prefStore.Insert(context.Background(), &pref.Preference{
+			UserID:          models.SignedInUser{}.UserId,
+			Theme:           "dark",
+			Timezone:        "browser",
+			HomeDashboardID: 5,
+			WeekStart:       "1",
+			JSONData:        &pref.PreferenceJSONData{Navbar: orgNavbarPreferences},
+			Created:         time.Now(),
+			Updated:         time.Now(),
+		})
+		require.NoError(t, err)
+
+		err = prefStore.Update(context.Background(), &pref.Preference{
+			ID:              id,
+			Theme:           "dark",
+			HomeDashboardID: 5,
+			Timezone:        "browser",
+			WeekStart:       "1",
+			Created:         time.Now(),
+			Updated:         time.Now(),
+			JSONData:        &pref.PreferenceJSONData{},
+		})
+		require.NoError(t, err)
+		query := &pref.Preference{}
+		prefs, err := prefStore.List(context.Background(), query)
+		require.NoError(t, err)
+		expected := &pref.Preference{
+			ID:              prefs[0].ID,
+			Version:         prefs[0].Version,
+			HomeDashboardID: 5,
+			Timezone:        "browser",
+			WeekStart:       "1",
+			Theme:           "dark",
+			JSONData:        prefs[0].JSONData,
+			Created:         prefs[0].Created,
+			Updated:         prefs[0].Updated,
+		}
+		if diff := cmp.Diff(expected, prefs[0]); diff != "" {
+			t.Fatalf("Result mismatch (-want +got):\n%s", diff)
+		}
+	})
+	t.Run("insert preference that does not exist", func(t *testing.T) {
+		_, err := prefStore.Insert(context.Background(),
+			&pref.Preference{
+				UserID:   models.SignedInUser{}.UserId,
+				Created:  time.Now(),
+				Updated:  time.Now(),
+				JSONData: &pref.PreferenceJSONData{},
+			})
+		require.NoError(t, err)
+	})
+}
diff --git a/pkg/services/preference/preftest/fake.go b/pkg/services/preference/preftest/fake.go
new file mode 100644
index 00000000000..f52acea2047
--- /dev/null
+++ b/pkg/services/preference/preftest/fake.go
@@ -0,0 +1,32 @@
+package preftest
+
+import (
+	"context"
+
+	pref "github.com/grafana/grafana/pkg/services/preference"
+)
+
+type FakePreferenceService struct {
+	ExpectedPreference *pref.Preference
+	ExpectedError      error
+}
+
+func NewPreferenceServiceFake() *FakePreferenceService {
+	return &FakePreferenceService{}
+}
+
+func (f *FakePreferenceService) GetWithDefaults(ctx context.Context, query *pref.GetPreferenceWithDefaultsQuery) (*pref.Preference, error) {
+	return f.ExpectedPreference, f.ExpectedError
+}
+
+func (f *FakePreferenceService) Get(ctx context.Context, query *pref.GetPreferenceQuery) (*pref.Preference, error) {
+	return f.ExpectedPreference, f.ExpectedError
+}
+
+func (f *FakePreferenceService) Save(ctx context.Context, cmd *pref.SavePreferenceCommand) error {
+	return f.ExpectedError
+}
+
+func (f *FakePreferenceService) GetDefaults() *pref.Preference {
+	return f.ExpectedPreference
+}