diff --git a/engine/dconf-engine.c b/engine/dconf-engine.c
index 8f5c613..5027cfc 100644
--- a/engine/dconf-engine.c
+++ b/engine/dconf-engine.c
@@ -30,6 +30,8 @@
 #include <unistd.h>
 #include <fcntl.h>
 #include <sys/mman.h>
+#include <glib/gstdio.h>
+#include <sys/vfs.h>
 
 void
 dconf_engine_message_destroy (DConfEngineMessage *dcem)
@@ -84,6 +86,7 @@ struct _DConfEngine
   gchar      *bus_types;
   gchar     **names;
   gint        n_dbs;
+  time_t     *mtimes;
 };
 
 static void
@@ -123,6 +126,7 @@ dconf_engine_setup_user (DConfEngine *engine)
 
       if (engine->shm)
         {
+          GStatBuf buf;
           gchar *filename;
 
           filename = g_build_filename (g_get_user_config_dir (),
@@ -130,6 +134,8 @@ dconf_engine_setup_user (DConfEngine *engine)
                                        engine->names[0],
                                        NULL);
           engine->gvdbs[0] = gvdb_table_new (filename, FALSE, NULL);
+          engine->mtimes[0] = g_stat (filename, &buf) == 0 ? buf.st_mtime : 0;
+
           g_free (filename);
         }
     }
@@ -278,6 +284,12 @@ dconf_engine_new (const gchar *profile)
 {
   DConfEngine *engine;
   gint i;
+  gboolean on_nfs;
+  gchar *filename;
+  GStatBuf buf;
+#if !defined(G_OS_WIN32) || !defined(_MSC_VER)
+  struct statfs fsbuf;
+#endif
 
   engine = g_slice_new (DConfEngine);
   g_mutex_init (&engine->lock);
@@ -315,17 +327,46 @@ dconf_engine_new (const gchar *profile)
   engine->object_paths = g_new (gchar *, engine->n_dbs);
   engine->gvdbs = g_new0 (GvdbTable *, engine->n_dbs);
   engine->lock_tables = g_new0 (GvdbTable *, engine->n_dbs);
+  engine->mtimes = g_new0 (time_t, engine->n_dbs);
   engine->bus_types = g_strdup ("eyyyyyyyyyyyyy");
   engine->state = 0;
 
   for (i = 0; i < engine->n_dbs; i++)
-    if (engine->names[i])
-        engine->object_paths[i] = g_strjoin (NULL,
-                                             "/ca/desrt/dconf/Writer/",
-                                             engine->names[i],
-                                             NULL);
-    else
-      engine->object_paths[i] = NULL;
+    {
+
+      if (engine->names[i])
+        {
+          filename = g_build_filename (g_get_user_config_dir (),
+                                       "dconf",
+                                       engine->names[i],
+                                       NULL);
+
+          on_nfs = TRUE;
+
+#if !defined(G_OS_WIN32) || !defined(_MSC_VER)
+          if (statfs (filename, &fsbuf) == 0 &&
+              fsbuf.f_type != 0x6969)
+            on_nfs = FALSE;
+#endif
+
+          if (g_stat (filename, &buf) == 0 && on_nfs)
+            engine->mtimes[i] = buf.st_mtime;
+          else
+            engine->mtimes[i] = 0;
+
+          g_free (filename);
+
+          engine->object_paths[i] = g_strjoin (NULL,
+                                               "/ca/desrt/dconf/Writer/",
+                                               engine->names[i],
+                                               NULL);
+        }
+      else
+        {
+          engine->mtimes[i] = 0;
+          engine->object_paths[i] = NULL;
+        }
+    }
 
   dconf_engine_refresh_system (engine);
   dconf_engine_setup_user (engine);
@@ -362,6 +403,7 @@ dconf_engine_free (DConfEngine *engine)
   g_free (engine->names);
   g_free (engine->gvdbs);
   g_free (engine->lock_tables);
+  g_free (engine->mtimes);
 
   g_slice_free (DConfEngine, engine);
 }
@@ -714,3 +756,211 @@ dconf_engine_decode_writability_notify (const gchar **path,
 
   return TRUE;
 }
+
+typedef struct
+{
+  GvdbTable *old_table;
+  GList     *differs;
+  gchar     *path;
+  gint       level;
+} DConfEqualState;
+
+static void
+table_walk_value (const gchar *name,
+                  gsize        name_len,
+                  GVariant    *value,
+                  gpointer     user_data)
+{
+  DConfEqualState *state = (DConfEqualState *) user_data;
+  GVariant        *old_value;
+  gchar           *path;
+
+  path = g_strconcat (state->path, name, NULL);
+  old_value = gvdb_table_get_value (state->old_table, path);
+
+  if (value &&
+      old_value &&
+      !g_variant_equal (value, old_value))
+    state->differs = g_list_prepend (state->differs, path);
+  else
+    g_free (path);
+
+  if (old_value)
+    g_variant_unref (old_value);
+}
+
+static gboolean
+table_walk_open (const gchar *name,
+                 gsize        name_len,
+                 gpointer     user_data)
+{
+  DConfEqualState *state = (DConfEqualState *) user_data;
+  gchar           *path;
+
+  if (state->level > 0)
+    {
+      path = g_strconcat (state->path, name, NULL);
+      g_free (state->path);
+      state->path = path;
+    }
+
+  state->level++;
+
+  return TRUE;
+}
+
+static void
+table_walk_close (gsize    name_len,
+                  gpointer user_data)
+{
+  DConfEqualState *state = (DConfEqualState *) user_data;
+  gchar           *path;
+
+  if (state->level > 0)
+    {
+      path = g_strndup (state->path, strlen (state->path) - name_len);
+      g_free (state->path);
+      state->path = path;
+    }
+
+  state->level--;
+}
+
+/*
+ * Compares mmapped version of database 'name' with the
+ * version on disc and returns a list of changed values
+ * for given path (relative or absolute).
+ */
+static GList *
+dconf_engine_list_changed (DConfEngine *engine,
+                           const gchar *name,
+                           const gchar *path)
+{
+  GvdbTable *table;
+  GList     *result = NULL;
+  gchar     *filename;
+  gint       i;
+
+  if (name && path)
+    {
+      filename = g_build_filename (g_get_user_config_dir (),
+                                   "dconf",
+                                   name,
+                                   NULL);
+      table = gvdb_table_new (filename, FALSE, NULL);
+      g_free (filename);
+
+      if (table)
+        {
+          for (i = 0; i < engine->n_dbs; i++)
+            {
+              if (g_strcmp0 (engine->names[i], name) == 0)
+                {
+                  if (g_str_has_suffix (path, "/"))
+                    {
+                      DConfEqualState state;
+
+                      state.old_table = engine->gvdbs[i];
+                      state.differs = NULL;
+                      state.path = g_strdup (path);
+                      state.level = 0;
+
+                      gvdb_table_walk (table, path,
+                                       table_walk_open,
+                                       table_walk_value,
+                                       table_walk_close,
+                                       &state);
+
+                      g_free (state.path);
+
+                      result = state.differs;
+                    }
+                  else
+                    {
+                      GVariant *old_value;
+                      GVariant *new_value;
+
+                      old_value = gvdb_table_get_value (engine->gvdbs[i], path);
+                      new_value = gvdb_table_get_value (table, path);
+
+                      if (old_value &&
+                          new_value &&
+                          !g_variant_equal (old_value, new_value))
+                        result = g_list_prepend (result, g_strdup (path));
+
+                      if (old_value)
+                        g_variant_unref (old_value);
+
+                      if (new_value)
+                        g_variant_unref (new_value);
+                    }
+                  break;
+                }
+            }
+        }
+
+      gvdb_table_unref (table);
+    }
+
+  return result;
+}
+
+/*
+ * Return list of tuples containing name of changed database and name of changed key
+ * for the given path.
+ * The new database if read from filesystem, the old from the given engine.
+ */
+GVariant *
+dconf_engine_get_changed (DConfEngine *engine,
+                          const gchar *path)
+{
+  GVariantBuilder  array_builder;
+  GVariant        *result = NULL;
+  gboolean         changed = FALSE;
+  gint             i;
+
+  g_mutex_lock (&engine->lock);
+
+  g_variant_builder_init (&array_builder, G_VARIANT_TYPE ("a(ss)"));
+
+  for (i = 0; i < engine->n_dbs; i++)
+    {
+      GStatBuf  buf;
+      gchar    *filename;
+
+      if (engine->names[i] && engine->mtimes[i] != 0)
+        {
+          filename = g_build_filename (g_get_user_config_dir (),
+                                       "dconf",
+                                       engine->names[i],
+                                       NULL);
+          if (g_stat (filename, &buf) == 0)
+            {
+              if (buf.st_mtime > engine->mtimes[i])
+                {
+                  GList *iter;
+                  GList *changed_paths;
+
+                  changed_paths = dconf_engine_list_changed (engine, engine->names[i], path);
+
+                  for (iter = changed_paths; iter; iter = g_list_next (iter))
+                    {
+                      g_variant_builder_add (&array_builder, "(ss)", (gchar *) engine->names[i], (gchar *) iter->data);
+                      changed = TRUE;
+                    }
+
+                  g_list_free_full (changed_paths, g_free);
+                }
+            }
+
+          g_free (filename);
+        }
+    }
+
+  if (changed)
+    result = g_variant_new ("(a(ss))", &array_builder);
+
+  g_mutex_unlock (&engine->lock);
+
+  return result;
+}
diff --git a/engine/dconf-engine.h b/engine/dconf-engine.h
index 5769cab..e7f4009 100644
--- a/engine/dconf-engine.h
+++ b/engine/dconf-engine.h
@@ -22,8 +22,8 @@
 #ifndef __dconf_engine_h__
 #define __dconf_engine_h__
 
-#include <dconf-readtype.h>
-#include <dconf-resetlist.h>
+#include "dconf-readtype.h"
+#include "dconf-resetlist.h"
 #include <glib.h>
 
 typedef struct _DConfEngine DConfEngine;
@@ -144,4 +144,7 @@ gboolean                dconf_engine_decode_writability_notify          (const g
                                                                          const gchar             *iface,
                                                                          const gchar             *method,
                                                                          GVariant                *body);
+G_GNUC_INTERNAL
+GVariant *              dconf_engine_get_changed                        (DConfEngine             *engine,
+                                                                         const gchar             *path);
 #endif /* __dconf_engine_h__ */
diff --git a/service/Makefile.am b/service/Makefile.am
index 1c611fc..9086e72 100644
--- a/service/Makefile.am
+++ b/service/Makefile.am
@@ -6,6 +6,7 @@ dbusservice_DATA = ca.desrt.dconf.service
 
 dconf_service_LDADD = $(gio_LIBS)
 dconf_service_SOURCES = \
+	../engine/dconf-engine.c	\
 	../gvdb/gvdb-builder.c		\
 	../gvdb/gvdb-reader.c		\
 	../common/dconf-shmdir.c	\
diff --git a/service/service.c b/service/service.c
index b2ab1cf..de6815c 100644
--- a/service/service.c
+++ b/service/service.c
@@ -28,6 +28,12 @@
 #include "dconf-interfaces.h"
 #include "dconf-writer.h"
 #include "dconf-state.h"
+#include "../engine/dconf-engine.h"
+
+#define DB_FILE_CHANGE_TIMEOUT 60000
+
+static guint signal_id = 0;
+static guint timeout_id = 0;
 
 static void
 emit_notify_signal (GDBusConnection  *connection,
@@ -386,6 +392,106 @@ subtree_introspect (GDBusConnection *connection,
     }
 }
 
+typedef struct
+{
+  GDBusConnection *connection;
+  DConfEngine     *engine;
+  DConfState      *state;
+} DConfCheckData;
+
+static gboolean
+check_file_change (gpointer user_data)
+{
+  DConfCheckData *check_data = (DConfCheckData *) user_data;
+  GVariant       *changed_keys = dconf_engine_get_changed (check_data->engine, "/");
+  gint            i;
+
+  if (changed_keys && g_variant_is_of_type (changed_keys, g_variant_type_new ("(a(ss))")))
+    {
+      GVariant *items = g_variant_get_child_value (changed_keys, 0);
+
+      if (items && g_variant_is_of_type (items, g_variant_type_new ("a(ss)")))
+        {
+          for (i = 0; i < g_variant_n_children (items); i++)
+            {
+              GVariant *item = g_variant_get_child_value (items, i);
+
+              if (item && g_variant_is_of_type (item, g_variant_type_new ("(ss)")))
+                {
+                  const gchar *changed_database = g_variant_get_string (g_variant_get_child_value (item, 0), NULL);
+                  const gchar *changed_key = g_variant_get_string (g_variant_get_child_value (item, 1), NULL);
+                  GVariant    *none;
+                  gchar       *path;
+                  gchar       *shm;
+                  gchar        one = 1;
+                  gint         fd;
+
+                  shm = g_build_filename (check_data->state->shm_dir, changed_database, NULL);
+
+                  /* Mark changed database as dirty */
+                  fd = open (shm, O_WRONLY);
+                  if (fd >= 0)
+                    {
+                      write (fd, &one, sizeof one);
+                      close (fd);
+                      unlink (shm);
+                    }
+                  else if (errno != ENOENT)
+                    unlink (shm);
+
+                  none = g_variant_new_array (G_VARIANT_TYPE_STRING, NULL, 0);
+                  path = g_strjoin (NULL, "/ca/desrt/dconf/Writer/",
+                                    changed_database, NULL);
+
+                  g_dbus_connection_emit_signal (check_data->connection, NULL, path,
+                                                 "ca.desrt.dconf.Writer", "Notify",
+                                                 g_variant_new ("(s@ass)",
+                                                                changed_key, none, ""),
+                                                 NULL);
+                  g_free (path);
+                  g_variant_unref (none);
+                  g_free (shm);
+                }
+            }
+        }
+
+      /* Refresh local copy of engine after the change */
+      dconf_engine_free (check_data->engine);
+      check_data->engine = dconf_engine_new (NULL);
+    }
+
+  return TRUE;
+}
+
+static void
+writer_callback (GDBusConnection *connection,
+                 const gchar     *sender_name,
+                 const gchar     *object_path,
+                 const gchar     *interface_name,
+                 const gchar     *signal_name,
+                 GVariant        *parameters,
+                 gpointer         user_data)
+{
+  DConfCheckData *check_data = (DConfCheckData *) user_data;
+
+  if (check_data->engine)
+    {
+      dconf_engine_free (check_data->engine);
+      check_data->engine = dconf_engine_new (NULL);
+    }
+}
+
+static void
+check_data_free (gpointer user_data)
+{
+  DConfCheckData *check_data = (DConfCheckData *) user_data;
+
+  if (check_data->engine)
+    dconf_engine_free (check_data->engine);
+
+  g_free (check_data);
+}
+
 static void
 bus_acquired (GDBusConnection *connection,
               const gchar     *name,
@@ -396,12 +502,24 @@ bus_acquired (GDBusConnection *connection,
   };
   DConfState *state = user_data;
   GDBusSubtreeFlags flags;
+  DConfCheckData   *check_data;
 
   dconf_state_set_id (state, g_dbus_connection_get_unique_name (connection));
 
   flags = G_DBUS_SUBTREE_FLAGS_DISPATCH_TO_UNENUMERATED_NODES;
   g_dbus_connection_register_subtree (connection, "/ca/desrt/dconf/Writer",
                                       &vtable, flags, state, NULL, NULL);
+
+  check_data = g_new0 (DConfCheckData, 1);
+  check_data->engine = dconf_engine_new (NULL);
+  check_data->connection = connection;
+  check_data->state = state;
+
+  signal_id = g_dbus_connection_signal_subscribe (check_data->connection, NULL,
+                                                  "ca.desrt.dconf.Writer",
+                                                  NULL, NULL, NULL, G_DBUS_SIGNAL_FLAGS_NONE,
+                                                  writer_callback, check_data, check_data_free);
+  timeout_id = g_timeout_add (DB_FILE_CHANGE_TIMEOUT, check_file_change, check_data);
 }
 
 static void
@@ -417,6 +535,19 @@ name_lost (GDBusConnection *connection,
            gpointer         user_data)
 {
   fprintf (stderr, "unable to acquire name: '%s'\n", name);
+
+  if (timeout_id > 0)
+    {
+      g_source_remove (timeout_id);
+      timeout_id = 0;
+    }
+
+  if (signal_id > 0)
+    {
+      g_dbus_connection_signal_unsubscribe (connection, signal_id);
+      signal_id = 0;
+    }
+
   exit (1);
 }
 
@@ -424,6 +555,28 @@ static gboolean
 exit_service (gpointer data)
 {
   DConfState *state = data;
+  GDBusConnection *connection;
+
+  if (timeout_id > 0)
+    {
+      g_source_remove (timeout_id);
+      timeout_id = 0;
+    }
+
+  if (signal_id > 0)
+    {
+      if (state->is_session)
+        connection = g_bus_get_sync (G_BUS_TYPE_SESSION, FALSE, NULL);
+      else
+        connection = g_bus_get_sync (G_BUS_TYPE_SYSTEM, FALSE, NULL);
+
+      if (connection)
+        {
+          g_dbus_connection_signal_unsubscribe (connection, signal_id);
+          g_object_unref (connection);
+        }
+      signal_id = 0;
+    }
 
   g_main_loop_quit (state->main_loop);
 
