diff --git a/daemons/ganeti-rapi b/daemons/ganeti-rapi
index 643fcf91c58e4be264e2e114bd4fde0e342f097c..29469febcecd22dd7f79cac36f69cc5d41a1cb71 100755
--- a/daemons/ganeti-rapi
+++ b/daemons/ganeti-rapi
@@ -31,6 +31,7 @@ import optparse
 import sys
 import os
 import os.path
+import errno
 
 try:
   from pyinotify import pyinotify # pylint: disable-msg=E0611
@@ -103,20 +104,27 @@ class RemoteApiHttpServer(http.auth.HttpServerRequestAuthentication,
     @param filename: Path to file
 
     """
+    logging.info("Reading users file at %s", filename)
     try:
-      contents = utils.ReadFile(filename)
-    except EnvironmentError, err:
-      logging.warning("Error while reading %s: %s", filename, err)
-      return False
+      try:
+        contents = utils.ReadFile(filename)
+      except EnvironmentError, err:
+        self._users = None
+        if err.errno == errno.ENOENT:
+          logging.warning("No users file at %s", filename)
+        else:
+          logging.warning("Error while reading %s: %s", filename, err)
+        return False
 
-    try:
       users = http.auth.ParsePasswordFile(contents)
+
     except Exception, err: # pylint: disable-msg=W0703
       # We don't care about the type of exception
       logging.error("Error while parsing %s: %s", filename, err)
       return False
 
     self._users = users
+
     return True
 
   def _GetRequestContext(self, req):
@@ -229,39 +237,51 @@ class RemoteApiHttpServer(http.auth.HttpServerRequestAuthentication,
     return serializer.DumpJson(result)
 
 
-class FileWatcher:
-  def __init__(self, filename, cb):
+class FileEventHandler(asyncnotifier.FileEventHandlerBase):
+  def __init__(self, wm, path, cb):
     """Initializes this class.
 
-    @type filename: string
-    @param filename: File to watch
+    @param wm: Inotify watch manager
+    @type path: string
+    @param path: File path
     @type cb: callable
     @param cb: Function called on file change
 
     """
-    self._filename = filename
+    asyncnotifier.FileEventHandlerBase.__init__(self, wm)
+
     self._cb = cb
+    self._filename = os.path.basename(path)
 
-    wm = pyinotify.WatchManager()
-    self._handler = asyncnotifier.SingleFileEventHandler(wm, self._OnInotify,
-                                                         filename)
-    asyncnotifier.AsyncNotifier(wm, default_proc_fun=self._handler)
-    self._handler.enable()
+    # Class '...' has no 'IN_...' member, pylint: disable-msg=E1103
+    mask = (pyinotify.EventsCodes.IN_CLOSE_WRITE |
+            pyinotify.EventsCodes.IN_DELETE |
+            pyinotify.EventsCodes.IN_MOVED_FROM |
+            pyinotify.EventsCodes.IN_MOVED_TO)
 
-  def _OnInotify(self, notifier_enabled):
-    """Called upon update of the RAPI users file by pyinotify.
+    self._handle = self.AddWatch(os.path.dirname(path), mask)
 
-    @type notifier_enabled: boolean
-    @param notifier_enabled: whether the notifier is still enabled
+  def process_default(self, event):
+    """Called upon inotify event.
 
     """
-    logging.info("Reloading modified %s", self._filename)
+    if event.name == self._filename:
+      logging.debug("Received inotify event %s", event)
+      self._cb()
+
+
+def SetupFileWatcher(filename, cb):
+  """Configures an inotify watcher for a file.
 
-    self._cb()
+  @type filename: string
+  @param filename: File to watch
+  @type cb: callable
+  @param cb: Function called on file change
 
-    # Renable the watch again if we'd an atomic update of the file (e.g. mv)
-    if not notifier_enabled:
-      self._handler.enable()
+  """
+  wm = pyinotify.WatchManager()
+  handler = FileEventHandler(wm, filename, cb)
+  asyncnotifier.AsyncNotifier(wm, default_proc_fun=handler)
 
 
 def CheckRapi(options, args):
@@ -294,18 +314,19 @@ def PrepRapi(options, _):
                                ssl_verify_peer=False,
                                request_executor_class=JsonErrorRequestExecutor)
 
-  if os.path.exists(constants.RAPI_USERS_FILE):
-    # Setup file watcher (it'll be driven by asyncore)
-    FileWatcher(constants.RAPI_USERS_FILE,
-                compat.partial(server.LoadUsers, constants.RAPI_USERS_FILE))
+  # Setup file watcher (it'll be driven by asyncore)
+  SetupFileWatcher(constants.RAPI_USERS_FILE,
+                   compat.partial(server.LoadUsers, constants.RAPI_USERS_FILE))
 
   server.LoadUsers(constants.RAPI_USERS_FILE)
 
   # pylint: disable-msg=E1101
   # it seems pylint doesn't see the second parent class there
   server.Start()
+
   return (mainloop, server)
 
+
 def ExecRapi(options, args, prep_data): # pylint: disable-msg=W0613
   """Main remote API function, executed with the PID file held.
 
diff --git a/doc/rapi.rst b/doc/rapi.rst
index d3c0dfa9ff1d22eee1374104691bf73b0788bd7e..c25ba6eb881b4e29355efd8b2bcb87f258157b4f 100644
--- a/doc/rapi.rst
+++ b/doc/rapi.rst
@@ -21,10 +21,8 @@ Users and passwords
 -------------------
 
 ``ganeti-rapi`` reads users and passwords from a file (usually
-``/var/lib/ganeti/rapi_users``) on startup. If the file existed when
-``ganeti-rapi`` was started, it'll automatically reload the file upon
-changes. If the users file is newly created, ``ganeti-rapi`` must be
-restarted.
+``/var/lib/ganeti/rapi_users``) on startup. Changes to the file will be
+read automatically.
 
 Each line consists of two or three fields separated by whitespace. The
 first two fields are for username and password. The third field is