diff --git oak-lucene/src/main/java/org/apache/jackrabbit/oak/plugins/index/lucene/IndexNode.java oak-lucene/src/main/java/org/apache/jackrabbit/oak/plugins/index/lucene/IndexNode.java
index 5c3cd6a..d7cf36f 100644
--- oak-lucene/src/main/java/org/apache/jackrabbit/oak/plugins/index/lucene/IndexNode.java
+++ oak-lucene/src/main/java/org/apache/jackrabbit/oak/plugins/index/lucene/IndexNode.java
@@ -24,7 +24,15 @@ import static org.apache.jackrabbit.oak.plugins.index.lucene.LuceneIndexConstant
 
 import java.io.File;
 import java.io.IOException;
+import java.util.List;
+import java.util.concurrent.Executor;
+import java.util.concurrent.FutureTask;
 
+import javax.annotation.Nullable;
+
+import org.apache.commons.io.FileUtils;
+import org.apache.jackrabbit.oak.plugins.index.lucene.util.CopyOnReadDirectory;
+import org.apache.jackrabbit.oak.plugins.index.lucene.util.IndexJanitor;
 import org.apache.jackrabbit.oak.spi.state.NodeState;
 import org.apache.jackrabbit.oak.spi.state.ReadOnlyBuilder;
 import org.apache.lucene.index.DirectoryReader;
@@ -32,10 +40,24 @@ import org.apache.lucene.index.IndexReader;
 import org.apache.lucene.search.IndexSearcher;
 import org.apache.lucene.store.Directory;
 import org.apache.lucene.store.FSDirectory;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
 
 class IndexNode {
-
-    static IndexNode open(String name, NodeState definition)
+    private static final Logger log = LoggerFactory.getLogger(IndexNode.class);
+    /**
+     * Constructs an IndexNode for given definition
+     *
+     * @param name index name
+     * @param definition nodeState capturing index definition
+     * @param indexBaseDir base directory where the Lucene index files needs to be copied
+     *                          If null then lucene index files would not be copied
+     * @param reopen false is the index being opened for first time.
+     */
+    static IndexNode open(String name, NodeState definition,
+                          @Nullable File indexBaseDir,
+                          @Nullable Executor executor,
+                          boolean reopen)
             throws IOException {
         Directory directory = null;
 
@@ -51,6 +73,9 @@ class IndexNode {
 
         if (directory != null) {
             try {
+                if(indexBaseDir != null){
+                    directory = createFSDirectory(name, directory, indexBaseDir, reopen, executor);
+                }
                 IndexNode index = new IndexNode(name, definition, directory);
                 directory = null; // closed in Index.close()
                 return index;
@@ -123,4 +148,30 @@ class IndexNode {
         }
     }
 
+    private static Directory createFSDirectory(String name, Directory sourceDirectory,
+                                               File indexBaseDir,
+                                               boolean reopen,
+                                               Executor executor) throws IOException {
+        if(sourceDirectory instanceof FSDirectory){
+            //Already using a FSDirectory then reuse it
+            return sourceDirectory;
+        }
+
+        File indexDir = new File(indexBaseDir, name);
+        if(!indexDir.exists()){
+            FileUtils.forceMkdir(indexDir);
+        }
+
+        Directory fsdir = FSDirectory.open(indexDir);
+
+        if(!reopen){
+            log.info("Using [{}] for storing local copy of Lucene index files", indexDir.getAbsolutePath());
+            executor.execute(new FutureTask<List<String>>(new IndexJanitor(fsdir, sourceDirectory)));
+        }
+
+        return new CopyOnReadDirectory(sourceDirectory, fsdir, executor);
+    }
+
+
+
 }
diff --git oak-lucene/src/main/java/org/apache/jackrabbit/oak/plugins/index/lucene/IndexTracker.java oak-lucene/src/main/java/org/apache/jackrabbit/oak/plugins/index/lucene/IndexTracker.java
index 698efa8..663ffbb 100644
--- oak-lucene/src/main/java/org/apache/jackrabbit/oak/plugins/index/lucene/IndexTracker.java
+++ oak-lucene/src/main/java/org/apache/jackrabbit/oak/plugins/index/lucene/IndexTracker.java
@@ -25,9 +25,11 @@ import static org.apache.jackrabbit.oak.plugins.index.IndexConstants.TYPE_PROPER
 import static org.apache.jackrabbit.oak.plugins.index.lucene.LuceneIndexConstants.TYPE_LUCENE;
 import static org.apache.jackrabbit.oak.plugins.memory.EmptyNodeState.EMPTY_NODE;
 
+import java.io.File;
 import java.io.IOException;
 import java.util.List;
 import java.util.Map;
+import java.util.concurrent.Executor;
 
 import org.apache.jackrabbit.oak.commons.PathUtils;
 import org.apache.jackrabbit.oak.spi.commit.CompositeEditor;
@@ -50,6 +52,19 @@ class IndexTracker {
 
     private final Map<String, IndexNode> indices = newHashMap();
 
+    private final File localIndexDir;
+
+    private final Executor executor;
+
+    IndexTracker() {
+        this(null, null);
+    }
+
+    IndexTracker(File localIndexDir, Executor executor) {
+        this.localIndexDir = localIndexDir;
+        this.executor = executor;
+    }
+
     synchronized void close() {
         for (Map.Entry<String, IndexNode> entry : indices.entrySet()) {
             try {
@@ -108,7 +123,7 @@ class IndexTracker {
 
         try {
             // TODO: Use DirectoryReader.openIfChanged()
-            index = IndexNode.open(index.getName(), state);
+            index = IndexNode.open(index.getName(), state, localIndexDir, executor, true);
             if (index != null) { // the index might no longer exist
                 indices.put(path, index);
             }
@@ -133,7 +148,7 @@ class IndexTracker {
             for (ChildNodeEntry child : node.getChildNodeEntries()) {
                 node = child.getNodeState();
                 if (TYPE_LUCENE.equals(node.getString(TYPE_PROPERTY_NAME))) {
-                    index = IndexNode.open(child.getName(), node);
+                    index = IndexNode.open(child.getName(), node, localIndexDir, executor, false);
                     if (index != null) {
                         indices.put(path, index);
                         return index;
diff --git oak-lucene/src/main/java/org/apache/jackrabbit/oak/plugins/index/lucene/LuceneIndexProvider.java oak-lucene/src/main/java/org/apache/jackrabbit/oak/plugins/index/lucene/LuceneIndexProvider.java
index d05840f..2aae44e 100644
--- oak-lucene/src/main/java/org/apache/jackrabbit/oak/plugins/index/lucene/LuceneIndexProvider.java
+++ oak-lucene/src/main/java/org/apache/jackrabbit/oak/plugins/index/lucene/LuceneIndexProvider.java
@@ -16,11 +16,14 @@
  */
 package org.apache.jackrabbit.oak.plugins.index.lucene;
 
+import java.io.File;
 import java.util.List;
+import java.util.concurrent.Executor;
 
 import javax.annotation.Nonnull;
 
 import org.apache.felix.scr.annotations.Component;
+import org.apache.felix.scr.annotations.ConfigurationPolicy;
 import org.apache.felix.scr.annotations.Deactivate;
 import org.apache.felix.scr.annotations.Service;
 import org.apache.jackrabbit.oak.plugins.index.aggregate.NodeAggregator;
@@ -38,11 +41,11 @@ import com.google.common.collect.ImmutableList;
  * 
  * @see LuceneIndex
  */
-@Component
+@Component()
 @Service({ QueryIndexProvider.class, Observer.class })
 public class LuceneIndexProvider implements QueryIndexProvider, Observer {
 
-    protected final IndexTracker tracker = new IndexTracker();
+    protected final IndexTracker tracker;
 
     /**
      * TODO how to inject this in an OSGi friendly way?
@@ -51,6 +54,14 @@ public class LuceneIndexProvider implements QueryIndexProvider, Observer {
 
     protected NodeAggregator aggregator = null;
 
+    public LuceneIndexProvider(){
+        tracker = new IndexTracker();
+    }
+
+    public LuceneIndexProvider(File indexDir, Executor executor){
+        tracker = new IndexTracker(indexDir, executor);
+    }
+
     @Deactivate
     public void close() {
         tracker.close();
diff --git oak-lucene/src/main/java/org/apache/jackrabbit/oak/plugins/index/lucene/util/CopyOnReadDirectory.java oak-lucene/src/main/java/org/apache/jackrabbit/oak/plugins/index/lucene/util/CopyOnReadDirectory.java
new file mode 100644
index 0000000..a3883cd
--- /dev/null
+++ oak-lucene/src/main/java/org/apache/jackrabbit/oak/plugins/index/lucene/util/CopyOnReadDirectory.java
@@ -0,0 +1,290 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+package org.apache.jackrabbit.oak.plugins.index.lucene.util;
+
+import java.io.IOException;
+import java.util.Collection;
+import java.util.Collections;
+import java.util.List;
+import java.util.Set;
+import java.util.concurrent.Callable;
+import java.util.concurrent.ConcurrentMap;
+import java.util.concurrent.Executor;
+import java.util.concurrent.atomic.AtomicInteger;
+
+import javax.annotation.CheckForNull;
+
+import com.google.common.collect.ComparisonChain;
+import com.google.common.collect.ImmutableSet;
+import com.google.common.collect.Lists;
+import com.google.common.collect.Ordering;
+import com.google.common.util.concurrent.ListenableFutureTask;
+import org.apache.lucene.store.BaseDirectory;
+import org.apache.lucene.store.Directory;
+import org.apache.lucene.store.IOContext;
+import org.apache.lucene.store.IndexInput;
+import org.apache.lucene.store.IndexOutput;
+
+import static com.google.common.collect.Maps.newConcurrentMap;
+
+/**
+ * Directory implementation which lazily copies the index files from a
+ * remote directory in background.
+ */
+public class CopyOnReadDirectory extends BaseDirectory {
+    private static final Set<String> REMOTE_ONLY = ImmutableSet.of("segments.gen");
+
+    private static final AtomicInteger COUNTER = new AtomicInteger(0);
+
+    private final Directory remote;
+    private final Directory local;
+    private final Executor executor;
+
+    private final ConcurrentMap<String, FileReference> files = newConcurrentMap();
+
+    public CopyOnReadDirectory(Directory remote, Directory local, Executor executor) throws IOException {
+        this.remote = remote;
+        this.local = local;
+        this.executor = executor;
+    }
+
+    @Override
+    public String[] listAll() throws IOException {
+        return remote.listAll();
+    }
+
+    @Override
+    public boolean fileExists(String name) throws IOException {
+        return remote.fileExists(name);
+    }
+
+    @Override
+    public void deleteFile(String name) throws IOException {
+        throw new UnsupportedOperationException("Cannot delete in a ReadOnly directory");
+    }
+
+    @Override
+    public long fileLength(String name) throws IOException {
+        return remote.fileLength(name);
+    }
+
+    @Override
+    public IndexOutput createOutput(String name, IOContext context) throws IOException {
+        throw new UnsupportedOperationException("Cannot write in a ReadOnly directory");
+    }
+
+    @Override
+    public void sync(Collection<String> names) throws IOException {
+        remote.sync(names);
+    }
+
+    @Override
+    public IndexInput openInput(String name, IOContext context) throws IOException {
+        if(REMOTE_ONLY.contains(name)){
+            return remote.openInput(name, context);
+        }
+
+        if(localCopyReady(name)){
+            return files.get(name).openLocalInput(context);
+        }
+
+        FileReference toPut = new FileReference(name);
+        FileReference old = files.putIfAbsent(name, toPut);
+        if(old == null){
+            copy(toPut);
+        }
+
+        //If immediate executor is used the result would be ready right away
+        if(toPut.isLocalValid()){
+            return toPut.openLocalInput(context);
+        }
+
+        return remote.openInput(name, context);
+    }
+
+    private void copy(FileReference ref) {
+        executor.execute(ListenableFutureTask.create(new FileRefCallable(ref)));
+    }
+
+    private boolean localCopyReady(String name) {
+        FileReference ref = files.get(name);
+        if(ref == null){
+            return false;
+        }
+        return ref.isLocalValid();
+    }
+
+    @Override
+    public void close() throws IOException {
+        remote.close();
+    }
+
+    @CheckForNull
+    private VersionedFileName getFile(String name) throws IOException {
+        List<VersionedFileName> versions = Lists.newArrayList();
+        for(String versionedName : local.listAll()){
+            VersionedFileName vfn = new VersionedFileName(versionedName);
+            if(vfn.name.equals(name)){
+                versions.add(vfn);
+            }
+        }
+
+        if(versions.isEmpty()){
+            return null;
+        }
+
+        Collections.sort(versions);
+
+        //Get latest version
+        return versions.get(0);
+    }
+
+    private class FileRefCallable implements Callable<Boolean> {
+        private final FileReference reference;
+
+        private FileRefCallable(FileReference reference) {
+            this.reference = reference;
+        }
+
+        @Override
+        public Boolean call() throws Exception {
+            String name = reference.name;
+            VersionedFileName localFile = getFile(name);
+            final long remoteLength = remote.fileLength(name);
+            final long sourceLength = localFile != null ? localFile.length(local) : -1;
+
+            //Check if local copy is consistent with remote
+            //valid if size is same
+            if(sourceLength == remoteLength){
+                reference.markValid(localFile);
+                return false;
+            }
+
+            //Create a new unique file and copy the content to that
+            //This ensures even if there are readers bound to old OakDirectory
+            //new files do not interfere with that
+            int version = localFile != null ? localFile.version : 0;
+            version += COUNTER.incrementAndGet();
+            VersionedFileName newFile =
+                    new VersionedFileName(name, version);
+            remote.copy(local, name, newFile.versionedName(), IOContext.READ);
+            reference.markValid(newFile);
+
+            return true;
+        }
+    }
+
+    private class FileReference {
+        final String name;
+        private volatile VersionedFileName file;
+
+        private FileReference(String name) {
+            this.name = name;
+        }
+
+        boolean isLocalValid(){
+            return file != null;
+        }
+
+        IndexInput openLocalInput( IOContext context) throws IOException {
+            return local.openInput(file.versionedName(), context);
+        }
+
+        void markValid(VersionedFileName file){
+            this.file = file;
+        }
+    }
+
+    /**
+     * Files are named in format <version>-<actual name>. This ensures
+     * that two files with same name can be sorted and one with highest version
+     * is latest
+     */
+    static class VersionedFileName implements Comparable<VersionedFileName>{
+        static final String SEP = "-";
+        final String name;
+        final int version;
+
+        VersionedFileName(String name, int version) {
+            this.name = name;
+            this.version = version;
+        }
+
+        VersionedFileName(String versionedName) {
+            int indexOfSep = versionedName.indexOf(SEP);
+            if(indexOfSep != -1){
+                this.name = versionedName.substring(indexOfSep+SEP.length());
+                this.version = Integer.valueOf(versionedName.substring(0, indexOfSep));
+            }else{
+                this.name = versionedName;
+                this.version = 0;
+            }
+        }
+
+        String versionedName(){
+            if(hasVersionInfo()){
+                return String.valueOf(version) + SEP + name;
+            } else{
+                return name;
+            }
+        }
+
+        long length(Directory dir) throws IOException {
+            return dir.fileLength(versionedName());
+        }
+
+        boolean hasVersionInfo() {
+            return version != 0;
+        }
+
+        @Override
+        public int compareTo(VersionedFileName o) {
+            return ComparisonChain.start()
+                    .compare(name, o.name)
+                    .compare(version, o.version, Ordering.natural().reverse())
+                    .result();
+        }
+
+        @Override
+        public String toString() {
+            return versionedName();
+        }
+
+        @Override
+        public boolean equals(Object o) {
+            if (this == o) return true;
+            if (o == null || getClass() != o.getClass()) return false;
+
+            VersionedFileName that = (VersionedFileName) o;
+
+            if (version != that.version) return false;
+            if (!name.equals(that.name)) return false;
+
+            return true;
+        }
+
+        @Override
+        public int hashCode() {
+            int result = name.hashCode();
+            result = 31 * result + version;
+            return result;
+        }
+    }
+}
diff --git oak-lucene/src/main/java/org/apache/jackrabbit/oak/plugins/index/lucene/util/IndexJanitor.java oak-lucene/src/main/java/org/apache/jackrabbit/oak/plugins/index/lucene/util/IndexJanitor.java
new file mode 100644
index 0000000..1d575b8
--- /dev/null
+++ oak-lucene/src/main/java/org/apache/jackrabbit/oak/plugins/index/lucene/util/IndexJanitor.java
@@ -0,0 +1,103 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+package org.apache.jackrabbit.oak.plugins.index.lucene.util;
+
+import java.io.IOException;
+import java.util.Collections;
+import java.util.List;
+import java.util.Map;
+import java.util.Set;
+import java.util.SortedSet;
+import java.util.concurrent.Callable;
+
+import com.google.common.collect.ImmutableSet;
+import com.google.common.collect.Lists;
+import com.google.common.collect.Maps;
+import com.google.common.collect.Sets;
+import org.apache.lucene.store.Directory;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import static org.apache.jackrabbit.oak.plugins.index.lucene.util.CopyOnReadDirectory.VersionedFileName;
+
+/**
+ * Cleans up the local lucene index directory. By deleting files which are deleted in
+ * remote directory and also older version of index files. This would
+ * be mostly called in the startup
+ */
+public class IndexJanitor implements Callable<List<String>> {
+    private final Logger log = LoggerFactory.getLogger(getClass());
+    private final String[] localFilesSnapshot;
+    private final Directory local;
+    private final String[] remoteFiles;
+
+    public IndexJanitor(Directory local, Directory remote) throws IOException {
+        this.local = local;
+        this.localFilesSnapshot = local.listAll();
+        this.remoteFiles = remote.listAll();
+    }
+
+    @Override
+    public List<String> call() throws Exception {
+        Map<String, SortedSet<VersionedFileName>> files = Maps.newHashMap();
+
+        for(String name : localFilesSnapshot){
+            VersionedFileName vfn = new VersionedFileName(name);
+
+            SortedSet<VersionedFileName> versionedFiles = files.get(vfn.name);
+            if(versionedFiles == null){
+                versionedFiles = Sets.newTreeSet(Collections.reverseOrder());
+                files.put(vfn.name, versionedFiles);
+            }
+
+            versionedFiles.add(vfn);
+        }
+
+        //Files present in dest but not present in source have to be deleted
+        Set<String> filesToBeDeleted = Sets.difference(
+                files.keySet(),
+                ImmutableSet.copyOf(remoteFiles)
+        );
+
+        List<VersionedFileName> deletedFiles = Lists.newArrayList();
+
+        for(String name : filesToBeDeleted){
+            deletedFiles.addAll(files.remove(name));
+        }
+
+        for(SortedSet<VersionedFileName> versionedFiles : files.values()){
+            deletedFiles.addAll(versionedFiles.headSet(versionedFiles.last()));
+        }
+
+        List<String> deletedFileNames = Lists.newArrayListWithCapacity(deletedFiles.size());
+
+        for(VersionedFileName vfn : deletedFiles){
+            local.deleteFile(vfn.versionedName());
+            deletedFileNames.add(vfn.versionedName());
+        }
+
+        if(!deletedFileNames.isEmpty()) {
+            log.debug("Following files have been removed from Lucene " +
+                    "index directory [{}]", deletedFileNames);
+        }
+
+        return deletedFileNames;
+    }
+}
diff --git oak-lucene/src/test/java/org/apache/jackrabbit/oak/plugins/index/lucene/LuceneIndexTest.java oak-lucene/src/test/java/org/apache/jackrabbit/oak/plugins/index/lucene/LuceneIndexTest.java
index d170f43..4f2b662 100644
--- oak-lucene/src/test/java/org/apache/jackrabbit/oak/plugins/index/lucene/LuceneIndexTest.java
+++ oak-lucene/src/test/java/org/apache/jackrabbit/oak/plugins/index/lucene/LuceneIndexTest.java
@@ -16,7 +16,11 @@
  */
 package org.apache.jackrabbit.oak.plugins.index.lucene;
 
+import java.io.File;
 import java.util.List;
+import java.util.concurrent.Executor;
+import java.util.concurrent.ExecutorService;
+import java.util.concurrent.Executors;
 
 import static com.google.common.collect.ImmutableList.copyOf;
 import static com.google.common.collect.Iterators.transform;
@@ -32,6 +36,7 @@ import static org.apache.jackrabbit.oak.plugins.nodetype.NodeTypeConstants.JCR_N
 import static org.apache.jackrabbit.oak.plugins.nodetype.write.InitialContent.INITIAL_CONTENT;
 
 import com.google.common.base.Function;
+import org.apache.commons.io.FileUtils;
 import org.apache.jackrabbit.oak.api.Type;
 import org.apache.jackrabbit.oak.plugins.index.IndexUpdateProvider;
 import org.apache.jackrabbit.oak.query.QueryEngineSettings;
@@ -48,6 +53,10 @@ import org.apache.jackrabbit.oak.spi.query.QueryIndex;
 import org.apache.jackrabbit.oak.spi.state.NodeBuilder;
 import org.apache.jackrabbit.oak.spi.state.NodeState;
 import org.apache.lucene.analysis.Analyzer;
+import org.apache.lucene.index.DirectoryReader;
+import org.apache.lucene.search.IndexSearcher;
+import org.apache.lucene.store.FSDirectory;
+import org.junit.After;
 import org.junit.Test;
 
 import com.google.common.collect.ImmutableList;
@@ -65,6 +74,13 @@ public class LuceneIndexTest {
 
     private NodeBuilder builder = root.builder();
 
+    private ExecutorService executor = Executors.newSingleThreadExecutor();
+
+    @After
+    public void tearDown(){
+        executor.shutdown();
+    }
+
     @Test
     public void testLucene() throws Exception {
         NodeBuilder index = builder.child(INDEX_DEFINITIONS_NAME);
@@ -100,7 +116,7 @@ public class LuceneIndexTest {
         builder.setProperty("foo", "bar");
 
         for(int i = 0; i < LuceneIndex.LUCENE_QUERY_BATCH_SIZE; i++){
-            builder.child("parent").child("child"+i).setProperty("foo", "bar");
+            builder.child("parent").child("child" + i).setProperty("foo", "bar");
         }
 
         NodeState after = builder.getNodeState();
@@ -125,6 +141,52 @@ public class LuceneIndexTest {
     }
 
     @Test
+    public void testLuceneWithFSDirectory() throws Exception {
+        NodeBuilder index = builder.child(INDEX_DEFINITIONS_NAME);
+        newLuceneIndexDefinition(index, "lucene",
+                ImmutableSet.of(TYPENAME_STRING));
+
+        NodeState before = builder.getNodeState();
+        builder.setProperty("foo", "bar");
+
+        NodeState after = builder.getNodeState();
+
+        NodeState indexed = HOOK.processCommit(before, after, CommitInfo.EMPTY);
+
+        File indexDir = new File("target", "lucene-"+System.currentTimeMillis());
+
+        IndexTracker tracker = new IndexTracker(indexDir, executor);
+        tracker.update(indexed);
+
+        QueryIndex queryIndex = new LuceneIndex(tracker, analyzer, null);
+        FilterImpl filter = createFilter(NT_BASE);
+        filter.restrictProperty("foo", Operator.EQUAL,
+                PropertyValues.newString("bar"));
+        Cursor cursor = queryIndex.query(filter, indexed);
+
+        assertTrue(cursor.hasNext());
+        assertEquals("/", cursor.next().getPath());
+        assertFalse(cursor.hasNext());
+
+        IndexSearcher searcher = tracker.getIndexNode("/").acquireSearcher();
+
+        tracker.getIndexNode("/").releaseSearcher();
+
+        builder = after.builder();
+        builder.child("parent").setProperty("foo", "bar");
+        indexed = HOOK.processCommit(after, builder.getNodeState(), CommitInfo.EMPTY);
+        cursor = queryIndex.query(filter, indexed);
+
+        tracker.update(indexed);
+
+        List<IndexRow> result = ImmutableList.copyOf(cursor);
+        assertEquals(2, result.size());
+
+        FileUtils.deleteQuietly(indexDir);
+        tracker.close();
+    }
+
+    @Test
     public void testLucene2() throws Exception {
         NodeBuilder index = builder.child(INDEX_DEFINITIONS_NAME);
         newLuceneIndexDefinition(index, "lucene",
diff --git oak-run/src/main/java/org/apache/jackrabbit/oak/benchmark/BenchmarkRunner.java oak-run/src/main/java/org/apache/jackrabbit/oak/benchmark/BenchmarkRunner.java
index 51fe103..ed07bcd 100644
--- oak-run/src/main/java/org/apache/jackrabbit/oak/benchmark/BenchmarkRunner.java
+++ oak-run/src/main/java/org/apache/jackrabbit/oak/benchmark/BenchmarkRunner.java
@@ -220,7 +220,9 @@ public class BenchmarkRunner {
             new FullTextSearchTest(
                     wikipedia.value(options),
                     flatStructure.value(options),
-                    report.value(options), withStorage.value(options))
+                    report.value(options), 
+                    withStorage.value(options),
+                    base.value(options))
         };
 
         Set<String> argset = Sets.newHashSet(options.nonOptionArguments());
diff --git oak-run/src/main/java/org/apache/jackrabbit/oak/benchmark/FullTextSearchTest.java oak-run/src/main/java/org/apache/jackrabbit/oak/benchmark/FullTextSearchTest.java
index e0c336c..459a4bd 100644
--- oak-run/src/main/java/org/apache/jackrabbit/oak/benchmark/FullTextSearchTest.java
+++ oak-run/src/main/java/org/apache/jackrabbit/oak/benchmark/FullTextSearchTest.java
@@ -25,6 +25,8 @@ import java.io.File;
 import java.util.List;
 import java.util.Random;
 import java.util.Set;
+import java.util.concurrent.ExecutorService;
+import java.util.concurrent.Executors;
 import java.util.regex.Matcher;
 import java.util.regex.Pattern;
 
@@ -36,6 +38,8 @@ import javax.jcr.query.QueryManager;
 import javax.jcr.query.QueryResult;
 import javax.jcr.query.RowIterator;
 
+import com.google.common.util.concurrent.MoreExecutors;
+import org.apache.commons.io.FileUtils;
 import org.apache.jackrabbit.oak.benchmark.wikipedia.WikipediaImport;
 import org.apache.jackrabbit.oak.fixture.JcrCustomizer;
 import org.apache.jackrabbit.oak.fixture.OakRepositoryFixture;
@@ -68,6 +72,8 @@ public class FullTextSearchTest extends AbstractTest<FullTextSearchTest.TestCont
 
     private int maxRowsToFetch = Integer.getInteger("maxRowsToFetch",100);
 
+    private boolean useFSDirectory = !Boolean.getBoolean("useFSDirectory");
+
     private TestContext defaultContext;
 
     /**
@@ -75,7 +81,11 @@ public class FullTextSearchTest extends AbstractTest<FullTextSearchTest.TestCont
      */
     private Boolean storageEnabled;
 
-    public FullTextSearchTest(File dump, boolean flat, boolean doReport, Boolean storageEnabled) {
+    private File indexDir;
+
+    private ExecutorService executor;
+
+    public FullTextSearchTest(File dump, boolean flat, boolean doReport, Boolean storageEnabled, File base) {
         this.importer = new WikipediaImport(dump, flat, doReport) {
             @Override
             protected void pageAdded(String title, String text) {
@@ -97,6 +107,7 @@ public class FullTextSearchTest extends AbstractTest<FullTextSearchTest.TestCont
             }
         };
         this.storageEnabled = storageEnabled;
+            indexDir = useFSDirectory ? new File(base, "lucene-"+System.currentTimeMillis()) : null;
     }
 
     @Override
@@ -112,6 +123,13 @@ public class FullTextSearchTest extends AbstractTest<FullTextSearchTest.TestCont
     }
 
     @Override
+    protected void afterSuite() throws Exception {
+        if(executor != null){
+            executor.shutdownNow();
+        }
+    }
+
+    @Override
     protected TestContext prepareThreadExecutionContext() {
         return new TestContext();
     }
@@ -160,7 +178,20 @@ public class FullTextSearchTest extends AbstractTest<FullTextSearchTest.TestCont
             return ((OakRepositoryFixture) fixture).setUpCluster(1, new JcrCustomizer() {
                 @Override
                 public Jcr customize(Jcr jcr) {
-                    LuceneIndexProvider provider = new LuceneIndexProvider();
+                    LuceneIndexProvider provider;
+                    if(useFSDirectory){
+                        //executor = Executors.newCachedThreadPool();
+                        //Using a same thread executor for copying the index as otherwise
+                        //due to small test duration the files do not get copied in time
+                        //and hence do not gte benefit of local index files
+                        //In prod this cost would be amortized
+                        executor = MoreExecutors.sameThreadExecutor();
+                        FileUtils.deleteQuietly(indexDir);
+                        provider = new LuceneIndexProvider(indexDir, executor);
+                    }else{
+                        provider = new LuceneIndexProvider();
+                    }
+
                     jcr.with((QueryIndexProvider) provider)
                        .with((Observer) provider)
                        .with(new LuceneIndexEditorProvider())
