diff --git a/mod/box/CMakeLists.txt b/mod/box/CMakeLists.txt
index 259ef3e4107b3c01460538c25cf5d1b2e5c9e5d8..790e960cafb5c5e5851d27cd1f86961f4aa0e916 100644
--- a/mod/box/CMakeLists.txt
+++ b/mod/box/CMakeLists.txt
@@ -34,5 +34,5 @@ set_source_files_properties(memcached-grammar.m
 set_source_files_properties(memcached.m
     PROPERTIES COMPILE_FLAGS "-Wno-uninitialized")
 
-tarantool_module("box" tuple.m index.m tree.m box.m box.lua.c box_lua.m
-    memcached.m memcached-grammar.m)
+tarantool_module("box" tuple.m index.m tree.m space.m box.m box.lua.c
+    box_lua.m memcached.m memcached-grammar.m)
diff --git a/mod/box/box.h b/mod/box/box.h
index bf94f4c8e728034784b62979bde3cd9954938431..a1838244c0b25d8fe9e81d5e9787432b2f2e1cdd 100644
--- a/mod/box/box.h
+++ b/mod/box/box.h
@@ -25,8 +25,6 @@
  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
  * SUCH DAMAGE.
  */
-
-#include <mod/box/index.h>
 #include "exception.h"
 #include "iproto.h"
 #include <tbuf.h>
@@ -35,58 +33,11 @@
 struct tarantool_cfg;
 struct box_tuple;
 
-enum
-{
-	BOX_INDEX_MAX = 10,
-	BOX_SPACE_MAX = 256,
+enum {
 	/** A limit on how many operations a single UPDATE can have. */
 	BOX_UPDATE_OP_CNT_MAX = 128,
 };
 
-struct space {
-	Index *index[BOX_INDEX_MAX];
-	/** If not set (is 0), any tuple in the
-	 * space can have any number of fields (but
-	 * @sa max_fieldno). If set, Each tuple
-	 * must have exactly this many fields.
-	 */
-	int arity;
-
-	/**
-	 * The number of indexes in the space.
-	 *
-	 * It is equal to the number of non-nil members of the index
-	 * array and defines the key_defs array size as well.
-	 */
-	int key_count;
-
-	/**
-	 * The descriptors for all indexes that belong to the space.
-	 */
-	struct key_def *key_defs;
-
-	/**
-	 * Field types of indexed fields. This is an array of size
-	 * field_count. If there are gaps, i.e. fields that do not
-	 * participate in any index and thus we cannot infer their
-	 * type, then respective array members have value UNKNOWN.
-	 * XXX: right now UNKNOWN is also set for fields which types
-	 * in two indexes contradict each other.
-	 */
-	enum field_data_type *field_types;
-
-	/**
-	 * Max field no which participates in any of the space indexes.
-	 * Each tuple in this space must have, therefore, at least
-	 * field_count fields.
-	 */
-	int max_fieldno;
-
-	bool enabled;
-};
-
-extern struct space *space;
-
 struct box_out {
 	void (*add_u32)(u32 *u32);
 	void (*dup_u32)(u32 u32);
@@ -94,6 +45,8 @@ struct box_out {
 };
 
 extern struct box_out box_out_quiet;
+@class Index;
+struct space;
 
 struct box_txn {
 	u16 op;
@@ -171,41 +124,6 @@ ENUM(update_op_codes, UPDATE_OP_CODES);
 
 extern iproto_callback rw_callback;
 
-/**
- * Get space ordinal number.
- */
-static inline int
-space_n(struct space *sp)
-{
-	assert(sp >= space && sp < (space + BOX_SPACE_MAX));
-	return sp - space;
-}
-
-/**
- * Get key_def ordinal number.
- */
-static inline int
-key_def_n(struct space *sp, struct key_def *kp)
-{
-	assert(kp >= sp->key_defs && kp < (sp->key_defs + sp->key_count));
-	return kp - sp->key_defs;
-}
-
-/**
- * Get index ordinal number.
- */
-static inline int
-index_n(Index *index)
-{
-	return key_def_n(index->space, index->key_def);
-}
-
-static inline bool
-index_is_primary(Index *index)
-{
-	return index_n(index) == 0;
-}
-
 /* These are used to implement memcached 'GET' */
 static inline struct box_txn *in_txn() { return fiber->mod_data.txn; }
 struct box_txn *txn_begin();
diff --git a/mod/box/box.m b/mod/box/box.m
index e7abf9b68eb00b5fd6f1809d3190798e7a856a53..7352542a7c13b9cc99ea83000ba646d06f826f33 100644
--- a/mod/box/box.m
+++ b/mod/box/box.m
@@ -47,6 +47,7 @@
 #include <mod/box/tuple.h>
 #include "memcached.h"
 #include "box_lua.h"
+#include "space.h"
 
 extern pid_t logger_pid;
 
@@ -78,11 +79,6 @@ const int BOX_REF_THRESHOLD = 8196;
 
 struct space *space = NULL;
 
-/* Secondary indexes are built in bulk after all data is
-   recovered. This flag indicates that the indexes are
-   already built and ready for use. */
-static bool secondary_indexes_enabled = false;
-
 struct box_snap_row {
 	u32 space;
 	u32 tuple_size;
@@ -91,21 +87,6 @@ struct box_snap_row {
 } __attribute__((packed));
 
 
-static inline int
-index_count(struct space *sp)
-{
-	if (!secondary_indexes_enabled) {
-		/* If the secondary indexes are not enabled yet
-		   then we can use only the primary index. So
-		   return 1 if there is at least one index (which
-		   must be primary) and return 0 otherwise. */
-		return sp->key_count > 0;
-	} else {
-		/* Return the actual number of indexes. */
-		return sp->key_count;
-	}
-}
-
 static inline struct box_snap_row *
 box_snap_row(const struct tbuf *t)
 {
@@ -1482,223 +1463,6 @@ xlog_print(struct recovery_state *r __attribute__((unused)), struct tbuf *t)
 	return res;
 }
 
-/** Free a key definition. */
-static void
-key_free(struct key_def *key_def)
-{
-	free(key_def->parts);
-	free(key_def->cmp_order);
-}
-
-static void
-space_free(void)
-{
-	int i;
-	for (i = 0 ; i < BOX_SPACE_MAX ; i++) {
-		if (!space[i].enabled)
-			continue;
-
-		int j;
-		for (j = 0 ; j < space[i].key_count; j++) {
-			Index *index = space[i].index[j];
-			[index free];
-			key_free(&space[i].key_defs[j]);
-		}
-
-		free(space[i].key_defs);
-		free(space[i].field_types);
-	}
-}
-
-static void
-key_init(struct key_def *def, struct tarantool_cfg_space_index *cfg_index)
-{
-	def->max_fieldno = 0;
-	def->part_count = 0;
-
-	/* Calculate key part count and maximal field number. */
-	for (int k = 0; cfg_index->key_field[k] != NULL; ++k) {
-		typeof(cfg_index->key_field[k]) cfg_key = cfg_index->key_field[k];
-
-		if (cfg_key->fieldno == -1) {
-			/* last filled key reached */
-			break;
-		}
-
-		def->max_fieldno = MAX(def->max_fieldno, cfg_key->fieldno);
-		def->part_count++;
-	}
-
-	/* init def array */
-	def->parts = malloc(sizeof(struct key_part) * def->part_count);
-	if (def->parts == NULL) {
-		panic("can't allocate def parts array for index");
-	}
-
-	/* init compare order array */
-	def->max_fieldno++;
-	def->cmp_order = malloc(def->max_fieldno * sizeof(u32));
-	if (def->cmp_order == NULL) {
-		panic("can't allocate def cmp_order array for index");
-	}
-	memset(def->cmp_order, -1, def->max_fieldno * sizeof(u32));
-
-	/* fill fields and compare order */
-	for (int k = 0; cfg_index->key_field[k] != NULL; ++k) {
-		typeof(cfg_index->key_field[k]) cfg_key = cfg_index->key_field[k];
-
-		if (cfg_key->fieldno == -1) {
-			/* last filled key reached */
-			break;
-		}
-
-		/* fill keys */
-		def->parts[k].fieldno = cfg_key->fieldno;
-		def->parts[k].type = STR2ENUM(field_data_type, cfg_key->type);
-		/* fill compare order */
-		def->cmp_order[cfg_key->fieldno] = k;
-	}
-	def->is_unique = cfg_index->unique;
-}
-
-/**
- * Extract all available field info from keys
- *
- * @param space		space to extract field info for
- * @param key_count	the number of keys
- * @param key_defs	key description array
- */
-static void
-space_init_field_types(struct space *space)
-{
-	int i, max_fieldno;
-	int key_count = space->key_count;
-	struct key_def *key_defs = space->key_defs;
-
-	/* find max max field no */
-	max_fieldno = 0;
-	for (i = 0; i < key_count; i++) {
-		max_fieldno= MAX(max_fieldno, key_defs[i].max_fieldno);
-	}
-
-	/* alloc & init field type info */
-	space->max_fieldno = max_fieldno;
-	space->field_types = malloc(max_fieldno * sizeof(enum field_data_type));
-	for (i = 0; i < max_fieldno; i++) {
-		space->field_types[i] = UNKNOWN;
-	}
-
-	/* extract field type info */
-	for (i = 0; i < key_count; i++) {
-		struct key_def *def = &key_defs[i];
-		for (int pi = 0; pi < def->part_count; pi++) {
-			struct key_part *part = &def->parts[pi];
-			assert(part->fieldno < max_fieldno);
-			space->field_types[part->fieldno] = part->type;
-		}
-	}
-
-#ifndef NDEBUG
-	/* validate field type info */
-	for (i = 0; i < key_count; i++) {
-		struct key_def *def = &key_defs[i];
-		for (int pi = 0; pi < def->part_count; pi++) {
-			struct key_part *part = &def->parts[pi];
-			assert(space->field_types[part->fieldno] == part->type);
-		}
-	}
-#endif
-}
-
-static void
-space_config(void)
-{
-	/* exit if no spaces are configured */
-	if (cfg.space == NULL) {
-		return;
-	}
-
-	/* fill box spaces */
-	for (int i = 0; cfg.space[i] != NULL; ++i) {
-		tarantool_cfg_space *cfg_space = cfg.space[i];
-
-		if (!CNF_STRUCT_DEFINED(cfg_space) || !cfg_space->enabled)
-			continue;
-
-		assert(cfg.memcached_port == 0 || i != cfg.memcached_space);
-
-		space[i].enabled = true;
-		space[i].arity = cfg_space->cardinality;
-
-		/*
-		 * Collect key/field info. We need aggregate
-		 * information on all keys before we can create
-		 * indexes.
-		 */
-		space[i].key_count = 0;
-		for (int j = 0; cfg_space->index[j] != NULL; ++j) {
-			++space[i].key_count;
-		}
-
-		space[i].key_defs = malloc(space[i].key_count *
-					    sizeof(struct key_def));
-		if (space[i].key_defs == NULL) {
-			panic("can't allocate key def array");
-		}
-		for (int j = 0; cfg_space->index[j] != NULL; ++j) {
-			typeof(cfg_space->index[j]) cfg_index = cfg_space->index[j];
-			key_init(&space[i].key_defs[j], cfg_index);
-		}
-		space_init_field_types(&space[i]);
-
-		/* fill space indexes */
-		for (int j = 0; cfg_space->index[j] != NULL; ++j) {
-			typeof(cfg_space->index[j]) cfg_index = cfg_space->index[j];
-			enum index_type type = STR2ENUM(index_type, cfg_index->type);
-			struct key_def *key_def = &space[i].key_defs[j];
-			Index *index = [Index alloc: type :key_def :&space[i]];
-			[index init: key_def :&space[i]];
-			space[i].index[j] = index;
-		}
-		say_info("space %i successfully configured", i);
-	}
-}
-
-static void
-space_init(void)
-{
-	/* Allocate and initialize space memory. */
-	space = palloc(eter_pool, sizeof(struct space) * BOX_SPACE_MAX);
-	memset(space, 0, sizeof(struct space) * BOX_SPACE_MAX);
-
-	/* configure regular spaces */
-	space_config();
-
-	/* configure memcached space */
-	memcached_space_init();
-}
-
-static void
-build_indexes(void)
-{
-	for (u32 n = 0; n < BOX_SPACE_MAX; ++n) {
-		if (space[n].enabled == false)
-			continue;
-		if (space[n].key_count <= 1)
-			continue; /* no secondary keys */
-
-		say_info("Building secondary keys in space %" PRIu32 "...", n);
-
-		Index *pk = space[n].index[0];
-		for (int i = 1; i < space[n].key_count; i++) {
-			Index *index = space[n].index[i];
-			[index build: pk];
-		}
-
-		say_info("Space %"PRIu32": done", n);
-	}
-}
-
 static void
 box_process_rw(u32 op, struct tbuf *request_data)
 {
@@ -1849,179 +1613,6 @@ box_leave_local_standby_mode(void *data __attribute__((unused)))
 	box_enter_master_or_replica_mode(&cfg);
 }
 
-static i32
-check_spaces(struct tarantool_cfg *conf)
-{
-	/* exit if no spaces are configured */
-	if (conf->space == NULL) {
-		return 0;
-	}
-
-	for (size_t i = 0; conf->space[i] != NULL; ++i) {
-		typeof(conf->space[i]) space = conf->space[i];
-
-		if (!CNF_STRUCT_DEFINED(space)) {
-			/* space undefined, skip it */
-			continue;
-		}
-
-		if (!space->enabled) {
-			/* space disabled, skip it */
-			continue;
-		}
-
-		/* check space bound */
-		if (i >= BOX_SPACE_MAX) {
-			/* maximum space is reached */
-			out_warning(0, "(space = %zu) "
-				    "too many spaces (%i maximum)", i, space);
-			return -1;
-		}
-
-		if (conf->memcached_port && i == conf->memcached_space) {
-			out_warning(0, "Space %i is already used as "
-				    "memcached_space.", i);
-			return -1;
-		}
-
-		/* at least one index in space must be defined
-		 * */
-		if (space->index == NULL) {
-			out_warning(0, "(space = %zu) "
-				    "at least one index must be defined", i);
-			return -1;
-		}
-
-		int max_key_fieldno = -1;
-
-		/* check spaces indexes */
-		for (size_t j = 0; space->index[j] != NULL; ++j) {
-			typeof(space->index[j]) index = space->index[j];
-			u32 key_part_count = 0;
-			enum index_type index_type;
-
-			/* check index bound */
-			if (j >= BOX_INDEX_MAX) {
-				/* maximum index in space reached */
-				out_warning(0, "(space = %zu index = %zu) "
-					    "too many indexed (%i maximum)", i, j, BOX_INDEX_MAX);
-				return -1;
-			}
-
-			/* at least one key in index must be defined */
-			if (index->key_field == NULL) {
-				out_warning(0, "(space = %zu index = %zu) "
-					    "at least one field must be defined", i, j);
-				return -1;
-			}
-
-			/* check unique property */
-			if (index->unique == -1) {
-				/* unique property undefined */
-				out_warning(0, "(space = %zu index = %zu) "
-					    "unique property is undefined", i, j);
-			}
-
-			for (size_t k = 0; index->key_field[k] != NULL; ++k) {
-				typeof(index->key_field[k]) key = index->key_field[k];
-
-				if (key->fieldno == -1) {
-					/* last key reached */
-					break;
-				}
-
-				/* key must has valid type */
-				if (STR2ENUM(field_data_type, key->type) == field_data_type_MAX) {
-					out_warning(0, "(space = %zu index = %zu) "
-						    "unknown field data type: `%s'", i, j, key->type);
-					return -1;
-				}
-
-				if (max_key_fieldno < key->fieldno) {
-					max_key_fieldno = key->fieldno;
-				}
-
-				++key_part_count;
-			}
-
-			/* Check key part count. */
-			if (key_part_count == 0) {
-				out_warning(0, "(space = %zu index = %zu) "
-					    "at least one field must be defined", i, j);
-				return -1;
-			}
-
-			index_type = STR2ENUM(index_type, index->type);
-
-			/* check index type */
-			if (index_type == index_type_MAX) {
-				out_warning(0, "(space = %zu index = %zu) "
-					    "unknown index type '%s'", i, j, index->type);
-				return -1;
-			}
-
-			/* First index must be unique. */
-			if (j == 0 && index->unique == false) {
-				out_warning(0, "(space = %zu) space first index must be unique", i);
-				return -1;
-			}
-
-			switch (index_type) {
-			case HASH:
-				/* check hash index */
-				/* hash index must has single-field key */
-				if (key_part_count != 1) {
-					out_warning(0, "(space = %zu index = %zu) "
-					            "hash index must has a single-field key", i, j);
-					return -1;
-				}
-				/* hash index must be unique */
-				if (!index->unique) {
-					out_warning(0, "(space = %zu index = %zu) "
-					            "hash index must be unique", i, j);
-					return -1;
-				}
-				break;
-			case TREE:
-				/* extra check for tree index not needed */
-				break;
-			default:
-				assert(false);
-			}
-		}
-
-		/* Check for index field type conflicts */
-		if (max_key_fieldno >= 0) {
-			char *types = alloca(max_key_fieldno + 1);
-			memset(types, UNKNOWN, max_key_fieldno + 1);
-			for (size_t j = 0; space->index[j] != NULL; ++j) {
-				typeof(space->index[j]) index = space->index[j];
-				for (size_t k = 0; index->key_field[k] != NULL; ++k) {
-					typeof(index->key_field[k]) key = index->key_field[k];
-					int f = key->fieldno;
-					if (f == -1) {
-						break;
-					}
-					enum field_data_type t = STR2ENUM(field_data_type, key->type);
-					assert(t != field_data_type_MAX);
-					if (types[f] != t) {
-						if (types[f] == UNKNOWN) {
-							types[f] = t;
-						} else {
-							out_warning(0, "(space = %zu fieldno = %zu) "
-								    "index field type mismatch", i, f);
-							return -1;
-						}
-					}
-				}
-
-			}
-		}
-	}
-
-	return 0;
-}
-
 i32
 mod_check_config(struct tarantool_cfg *conf)
 {
@@ -2134,6 +1725,8 @@ mod_init(void)
 
 	/* initialization spaces */
 	space_init();
+	/* configure memcached space */
+	memcached_space_init();
 
 	/* recovery initialization */
 	recovery_init(cfg.snap_dir, cfg.wal_dir,
diff --git a/mod/box/box_lua.m b/mod/box/box_lua.m
index c161c3c697daa633f3b9f630a6ecd9acec93d7a2..caa0a047e26e103bc044267f9ebfb295ff53f52b 100644
--- a/mod/box/box_lua.m
+++ b/mod/box/box_lua.m
@@ -43,6 +43,7 @@
 
 #include "pickle.h"
 #include "tuple.h"
+#include "space.h"
 
 /* contents of box.lua */
 extern const char box_lua[];
diff --git a/mod/box/index.m b/mod/box/index.m
index d9a896d6ca70c11d2030565a7408d21d878be1b1..154cd919294bb15c30037e5fc0c0b6c736d9de88 100644
--- a/mod/box/index.m
+++ b/mod/box/index.m
@@ -29,7 +29,7 @@
 #include "tuple.h"
 #include "pickle.h"
 #include "exception.h"
-#include "box.h"
+#include "space.h"
 #include "assoc.h"
 
 const char *field_data_type_strs[] = {"NUM", "NUM64", "STR", "\0"};
diff --git a/mod/box/memcached.m b/mod/box/memcached.m
index a7ad8997b122610334fb8d01d8a583d6db94df60..1641d3241227923687b82500c3454558f2288982 100644
--- a/mod/box/memcached.m
+++ b/mod/box/memcached.m
@@ -33,6 +33,7 @@
 #include "stat.h"
 #include "salloc.h"
 #include "pickle.h"
+#include "space.h"
 
 #define STAT(_)					\
         _(MEMC_GET, 1)				\
diff --git a/mod/box/space.h b/mod/box/space.h
new file mode 100644
index 0000000000000000000000000000000000000000..74256b0baec379c7b2b5467a4424da23da591aa9
--- /dev/null
+++ b/mod/box/space.h
@@ -0,0 +1,142 @@
+#ifndef TARANTOOL_SPACE_H_INCLUDED
+#define TARANTOOL_SPACE_H_INCLUDED
+/*
+ * Redistribution and use in source and binary forms, with or
+ * without modification, are permitted provided that the following
+ * conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above
+ *    copyright notice, this list of conditions and the
+ *    following disclaimer.
+ *
+ * 2. Redistributions in binary form must reproduce the above
+ *    copyright notice, this list of conditions and the following
+ *    disclaimer in the documentation and/or other materials
+ *    provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY <COPYRIGHT HOLDER> ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
+ * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+ * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL
+ * <COPYRIGHT HOLDER> OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT,
+ * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR
+ * BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+ * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF
+ * THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+#include "mod/box/index.h"
+
+struct tarantool_cfg;
+
+enum {
+	BOX_INDEX_MAX = 10,
+	BOX_SPACE_MAX = 256,
+};
+
+struct space {
+	Index *index[BOX_INDEX_MAX];
+	/** If not set (is 0), any tuple in the
+	 * space can have any number of fields (but
+	 * @sa max_fieldno). If set, Each tuple
+	 * must have exactly this many fields.
+	 */
+	int arity;
+
+	/**
+	 * The number of indexes in the space.
+	 *
+	 * It is equal to the number of non-nil members of the index
+	 * array and defines the key_defs array size as well.
+	 */
+	int key_count;
+
+	/**
+	 * The descriptors for all indexes that belong to the space.
+	 */
+	struct key_def *key_defs;
+
+	/**
+	 * Field types of indexed fields. This is an array of size
+	 * field_count. If there are gaps, i.e. fields that do not
+	 * participate in any index and thus we cannot infer their
+	 * type, then respective array members have value UNKNOWN.
+	 * XXX: right now UNKNOWN is also set for fields which types
+	 * in two indexes contradict each other.
+	 */
+	enum field_data_type *field_types;
+
+	/**
+	 * Max field no which participates in any of the space indexes.
+	 * Each tuple in this space must have, therefore, at least
+	 * field_count fields.
+	 */
+	int max_fieldno;
+
+	bool enabled;
+};
+
+extern struct space *space;
+
+/** Get space ordinal number. */
+static inline int
+space_n(struct space *sp)
+{
+	assert(sp >= space && sp < (space + BOX_SPACE_MAX));
+	return sp - space;
+}
+
+/** Get key_def ordinal number. */
+static inline int
+key_def_n(struct space *sp, struct key_def *kp)
+{
+	assert(kp >= sp->key_defs && kp < (sp->key_defs + sp->key_count));
+	return kp - sp->key_defs;
+}
+
+/** Get index ordinal number in space. */
+static inline int
+index_n(Index *index)
+{
+	return key_def_n(index->space, index->key_def);
+}
+
+/** Check whether or not an index is primary in space.  */
+static inline bool
+index_is_primary(Index *index)
+{
+	return index_n(index) == 0;
+}
+
+/**
+ * Secondary indexes are built in bulk after all data is
+ * recovered. This flag indicates that the indexes are
+ * already built and ready for use.
+ */
+extern bool secondary_indexes_enabled;
+
+static inline int
+index_count(struct space *sp)
+{
+	if (!secondary_indexes_enabled) {
+		/* If secondary indexes are not enabled yet,
+		   we can use only the primary index. So return
+		   1 if there is at least one index (which
+		   must be primary) and return 0 otherwise. */
+		return sp->key_count > 0;
+	} else {
+		/* Return the actual number of indexes. */
+		return sp->key_count;
+	}
+}
+
+void space_init(void);
+void space_free(void);
+i32 check_spaces(struct tarantool_cfg *conf);
+/* Build secondary keys. */
+void build_indexes(void);
+
+#endif /* TARANTOOL_SPACE_H_INCLUDED */
diff --git a/mod/box/space.m b/mod/box/space.m
new file mode 100644
index 0000000000000000000000000000000000000000..9d427dad68420334d3b0fcceb1b7b9b3f3b7206b
--- /dev/null
+++ b/mod/box/space.m
@@ -0,0 +1,423 @@
+/*
+ * Redistribution and use in source and binary forms, with or
+ * without modification, are permitted provided that the following
+ * conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above
+ *    copyright notice, this list of conditions and the
+ *    following disclaimer.
+ *
+ * 2. Redistributions in binary form must reproduce the above
+ *    copyright notice, this list of conditions and the following
+ *    disclaimer in the documentation and/or other materials
+ *    provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY <COPYRIGHT HOLDER> ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
+ * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+ * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL
+ * <COPYRIGHT HOLDER> OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT,
+ * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR
+ * BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+ * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF
+ * THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+#include "space.h"
+#include <stdlib.h>
+#include <cfg/tarantool_box_cfg.h>
+#include <cfg/warning.h>
+#include <tarantool.h>
+#include <exception.h>
+
+bool secondary_indexes_enabled = false;
+/** Free a key definition. */
+static void
+key_free(struct key_def *key_def)
+{
+	free(key_def->parts);
+	free(key_def->cmp_order);
+}
+
+void
+space_free(void)
+{
+	int i;
+	for (i = 0 ; i < BOX_SPACE_MAX ; i++) {
+		if (!space[i].enabled)
+			continue;
+
+		int j;
+		for (j = 0 ; j < space[i].key_count; j++) {
+			Index *index = space[i].index[j];
+			[index free];
+			key_free(&space[i].key_defs[j]);
+		}
+
+		free(space[i].key_defs);
+		free(space[i].field_types);
+	}
+}
+
+static void
+key_init(struct key_def *def, struct tarantool_cfg_space_index *cfg_index)
+{
+	def->max_fieldno = 0;
+	def->part_count = 0;
+
+	/* Calculate key part count and maximal field number. */
+	for (int k = 0; cfg_index->key_field[k] != NULL; ++k) {
+		typeof(cfg_index->key_field[k]) cfg_key = cfg_index->key_field[k];
+
+		if (cfg_key->fieldno == -1) {
+			/* last filled key reached */
+			break;
+		}
+
+		def->max_fieldno = MAX(def->max_fieldno, cfg_key->fieldno);
+		def->part_count++;
+	}
+
+	/* init def array */
+	def->parts = malloc(sizeof(struct key_part) * def->part_count);
+	if (def->parts == NULL) {
+		panic("can't allocate def parts array for index");
+	}
+
+	/* init compare order array */
+	def->max_fieldno++;
+	def->cmp_order = malloc(def->max_fieldno * sizeof(u32));
+	if (def->cmp_order == NULL) {
+		panic("can't allocate def cmp_order array for index");
+	}
+	memset(def->cmp_order, -1, def->max_fieldno * sizeof(u32));
+
+	/* fill fields and compare order */
+	for (int k = 0; cfg_index->key_field[k] != NULL; ++k) {
+		typeof(cfg_index->key_field[k]) cfg_key = cfg_index->key_field[k];
+
+		if (cfg_key->fieldno == -1) {
+			/* last filled key reached */
+			break;
+		}
+
+		/* fill keys */
+		def->parts[k].fieldno = cfg_key->fieldno;
+		def->parts[k].type = STR2ENUM(field_data_type, cfg_key->type);
+		/* fill compare order */
+		def->cmp_order[cfg_key->fieldno] = k;
+	}
+	def->is_unique = cfg_index->unique;
+}
+
+/**
+ * Extract all available field info from keys
+ *
+ * @param space		space to extract field info for
+ * @param key_count	the number of keys
+ * @param key_defs	key description array
+ */
+static void
+space_init_field_types(struct space *space)
+{
+	int i, max_fieldno;
+	int key_count = space->key_count;
+	struct key_def *key_defs = space->key_defs;
+
+	/* find max max field no */
+	max_fieldno = 0;
+	for (i = 0; i < key_count; i++) {
+		max_fieldno= MAX(max_fieldno, key_defs[i].max_fieldno);
+	}
+
+	/* alloc & init field type info */
+	space->max_fieldno = max_fieldno;
+	space->field_types = malloc(max_fieldno * sizeof(enum field_data_type));
+	for (i = 0; i < max_fieldno; i++) {
+		space->field_types[i] = UNKNOWN;
+	}
+
+	/* extract field type info */
+	for (i = 0; i < key_count; i++) {
+		struct key_def *def = &key_defs[i];
+		for (int pi = 0; pi < def->part_count; pi++) {
+			struct key_part *part = &def->parts[pi];
+			assert(part->fieldno < max_fieldno);
+			space->field_types[part->fieldno] = part->type;
+		}
+	}
+
+#ifndef NDEBUG
+	/* validate field type info */
+	for (i = 0; i < key_count; i++) {
+		struct key_def *def = &key_defs[i];
+		for (int pi = 0; pi < def->part_count; pi++) {
+			struct key_part *part = &def->parts[pi];
+			assert(space->field_types[part->fieldno] == part->type);
+		}
+	}
+#endif
+}
+
+static void
+space_config()
+{
+	/* exit if no spaces are configured */
+	if (cfg.space == NULL) {
+		return;
+	}
+
+	/* fill box spaces */
+	for (int i = 0; cfg.space[i] != NULL; ++i) {
+		tarantool_cfg_space *cfg_space = cfg.space[i];
+
+		if (!CNF_STRUCT_DEFINED(cfg_space) || !cfg_space->enabled)
+			continue;
+
+		assert(cfg.memcached_port == 0 || i != cfg.memcached_space);
+
+		space[i].enabled = true;
+		space[i].arity = cfg_space->cardinality;
+
+		/*
+		 * Collect key/field info. We need aggregate
+		 * information on all keys before we can create
+		 * indexes.
+		 */
+		space[i].key_count = 0;
+		for (int j = 0; cfg_space->index[j] != NULL; ++j) {
+			++space[i].key_count;
+		}
+
+		space[i].key_defs = malloc(space[i].key_count *
+					    sizeof(struct key_def));
+		if (space[i].key_defs == NULL) {
+			panic("can't allocate key def array");
+		}
+		for (int j = 0; cfg_space->index[j] != NULL; ++j) {
+			typeof(cfg_space->index[j]) cfg_index = cfg_space->index[j];
+			key_init(&space[i].key_defs[j], cfg_index);
+		}
+		space_init_field_types(&space[i]);
+
+		/* fill space indexes */
+		for (int j = 0; cfg_space->index[j] != NULL; ++j) {
+			typeof(cfg_space->index[j]) cfg_index = cfg_space->index[j];
+			enum index_type type = STR2ENUM(index_type, cfg_index->type);
+			struct key_def *key_def = &space[i].key_defs[j];
+			Index *index = [Index alloc: type :key_def :&space[i]];
+			[index init: key_def :&space[i]];
+			space[i].index[j] = index;
+		}
+		say_info("space %i successfully configured", i);
+	}
+}
+
+void
+space_init(void)
+{
+	/* Allocate and initialize space memory. */
+	space = palloc(eter_pool, sizeof(struct space) * BOX_SPACE_MAX);
+	memset(space, 0, sizeof(struct space) * BOX_SPACE_MAX);
+
+	/* configure regular spaces */
+	space_config();
+}
+
+void
+build_indexes(void)
+{
+	for (u32 n = 0; n < BOX_SPACE_MAX; ++n) {
+		if (space[n].enabled == false)
+			continue;
+		if (space[n].key_count <= 1)
+			continue; /* no secondary keys */
+
+		say_info("Building secondary keys in space %" PRIu32 "...", n);
+
+		Index *pk = space[n].index[0];
+		for (int i = 1; i < space[n].key_count; i++) {
+			Index *index = space[n].index[i];
+			[index build: pk];
+		}
+
+		say_info("Space %"PRIu32": done", n);
+	}
+}
+
+i32
+check_spaces(struct tarantool_cfg *conf)
+{
+	/* exit if no spaces are configured */
+	if (conf->space == NULL) {
+		return 0;
+	}
+
+	for (size_t i = 0; conf->space[i] != NULL; ++i) {
+		typeof(conf->space[i]) space = conf->space[i];
+
+		if (!CNF_STRUCT_DEFINED(space)) {
+			/* space undefined, skip it */
+			continue;
+		}
+
+		if (!space->enabled) {
+			/* space disabled, skip it */
+			continue;
+		}
+
+		/* check space bound */
+		if (i >= BOX_SPACE_MAX) {
+			/* maximum space is reached */
+			out_warning(0, "(space = %zu) "
+				    "too many spaces (%i maximum)", i, space);
+			return -1;
+		}
+
+		if (conf->memcached_port && i == conf->memcached_space) {
+			out_warning(0, "Space %i is already used as "
+				    "memcached_space.", i);
+			return -1;
+		}
+
+		/* at least one index in space must be defined
+		 * */
+		if (space->index == NULL) {
+			out_warning(0, "(space = %zu) "
+				    "at least one index must be defined", i);
+			return -1;
+		}
+
+		int max_key_fieldno = -1;
+
+		/* check spaces indexes */
+		for (size_t j = 0; space->index[j] != NULL; ++j) {
+			typeof(space->index[j]) index = space->index[j];
+			u32 key_part_count = 0;
+			enum index_type index_type;
+
+			/* check index bound */
+			if (j >= BOX_INDEX_MAX) {
+				/* maximum index in space reached */
+				out_warning(0, "(space = %zu index = %zu) "
+					    "too many indexed (%i maximum)", i, j, BOX_INDEX_MAX);
+				return -1;
+			}
+
+			/* at least one key in index must be defined */
+			if (index->key_field == NULL) {
+				out_warning(0, "(space = %zu index = %zu) "
+					    "at least one field must be defined", i, j);
+				return -1;
+			}
+
+			/* check unique property */
+			if (index->unique == -1) {
+				/* unique property undefined */
+				out_warning(0, "(space = %zu index = %zu) "
+					    "unique property is undefined", i, j);
+			}
+
+			for (size_t k = 0; index->key_field[k] != NULL; ++k) {
+				typeof(index->key_field[k]) key = index->key_field[k];
+
+				if (key->fieldno == -1) {
+					/* last key reached */
+					break;
+				}
+
+				/* key must has valid type */
+				if (STR2ENUM(field_data_type, key->type) == field_data_type_MAX) {
+					out_warning(0, "(space = %zu index = %zu) "
+						    "unknown field data type: `%s'", i, j, key->type);
+					return -1;
+				}
+
+				if (max_key_fieldno < key->fieldno) {
+					max_key_fieldno = key->fieldno;
+				}
+
+				++key_part_count;
+			}
+
+			/* Check key part count. */
+			if (key_part_count == 0) {
+				out_warning(0, "(space = %zu index = %zu) "
+					    "at least one field must be defined", i, j);
+				return -1;
+			}
+
+			index_type = STR2ENUM(index_type, index->type);
+
+			/* check index type */
+			if (index_type == index_type_MAX) {
+				out_warning(0, "(space = %zu index = %zu) "
+					    "unknown index type '%s'", i, j, index->type);
+				return -1;
+			}
+
+			/* First index must be unique. */
+			if (j == 0 && index->unique == false) {
+				out_warning(0, "(space = %zu) space first index must be unique", i);
+				return -1;
+			}
+
+			switch (index_type) {
+			case HASH:
+				/* check hash index */
+				/* hash index must has single-field key */
+				if (key_part_count != 1) {
+					out_warning(0, "(space = %zu index = %zu) "
+					            "hash index must has a single-field key", i, j);
+					return -1;
+				}
+				/* hash index must be unique */
+				if (!index->unique) {
+					out_warning(0, "(space = %zu index = %zu) "
+					            "hash index must be unique", i, j);
+					return -1;
+				}
+				break;
+			case TREE:
+				/* extra check for tree index not needed */
+				break;
+			default:
+				assert(false);
+			}
+		}
+
+		/* Check for index field type conflicts */
+		if (max_key_fieldno >= 0) {
+			char *types = alloca(max_key_fieldno + 1);
+			memset(types, UNKNOWN, max_key_fieldno + 1);
+			for (size_t j = 0; space->index[j] != NULL; ++j) {
+				typeof(space->index[j]) index = space->index[j];
+				for (size_t k = 0; index->key_field[k] != NULL; ++k) {
+					typeof(index->key_field[k]) key = index->key_field[k];
+					int f = key->fieldno;
+					if (f == -1) {
+						break;
+					}
+					enum field_data_type t = STR2ENUM(field_data_type, key->type);
+					assert(t != field_data_type_MAX);
+					if (types[f] != t) {
+						if (types[f] == UNKNOWN) {
+							types[f] = t;
+						} else {
+							out_warning(0, "(space = %zu fieldno = %zu) "
+								    "index field type mismatch", i, f);
+							return -1;
+						}
+					}
+				}
+
+			}
+		}
+	}
+
+	return 0;
+}
+
diff --git a/mod/box/tree.m b/mod/box/tree.m
index 37b8367850de9bc12606eb1486fcc23a145a867f..1546651ca07304cc7472edf7eceed3be2fe5434d 100644
--- a/mod/box/tree.m
+++ b/mod/box/tree.m
@@ -24,8 +24,9 @@
  */
 
 #include "tree.h"
-#include "box.h"
 #include "tuple.h"
+#include "space.h"
+#include "exception.h"
 #include <pickle.h>
 
 /* {{{ Utilities. *************************************************/