diff --git a/src/box/alter.cc b/src/box/alter.cc
index 013d3e0e516cb2f3e950d33a0922c756fc97a0e3..809a45db575d4d1f78fefe920557892fb6052680 100644
--- a/src/box/alter.cc
+++ b/src/box/alter.cc
@@ -35,16 +35,18 @@
 #include "scoped_guard.h"
 #include <new> /* for placement new */
 #include <stdio.h> /* snprintf() */
+#include <ctype.h>
 
 /** _space columns */
-#define ID 0
-#define ARITY 1
-#define NAME 2
+#define ID			0
+#define ARITY			1
+#define NAME			2
+#define FLAGS			3
 /** _index columns */
-#define INDEX_ID 1
-#define INDEX_TYPE 3
-#define INDEX_IS_UNIQUE 4
-#define INDEX_PART_COUNT 5
+#define INDEX_ID		1
+#define INDEX_TYPE		3
+#define INDEX_IS_UNIQUE		4
+#define INDEX_PART_COUNT	5
 
 /* {{{ Auxiliary functions and methods. */
 
@@ -94,6 +96,28 @@ key_def_new_from_tuple(struct tuple *tuple)
 	return key_def;
 }
 
+static void
+space_def_init_flags(struct space_def *def, struct tuple *tuple)
+{
+	/* default values of flags */
+	def->temporary = false;
+
+	/* there is no property in the space */
+	if (tuple->field_count <= FLAGS)
+		return;
+
+	const char *flags = tuple_field_cstr(tuple, FLAGS);
+	while (flags && *flags) {
+		while (isspace(*flags)) /* skip space */
+			flags++;
+		if (strncmp(flags, "temporary", strlen("temporary")) == 0)
+			def->temporary = true;
+		flags = strchr(flags, ',');
+		if (flags)
+			flags++;
+	}
+}
+
 /**
  * Fill space_def structure from struct tuple.
  */
@@ -105,6 +129,8 @@ space_def_create_from_tuple(struct space_def *def, struct tuple *tuple,
 	def->arity = tuple_field_u32(tuple, ARITY);
 	int n = snprintf(def->name, sizeof(def->name),
 			 "%s", tuple_field_cstr(tuple, NAME));
+
+	space_def_init_flags(def, tuple);
 	space_def_check(def, n, errcode);
 	if (errcode != ER_ALTER_SPACE &&
 	    def->id >= SC_SYSTEM_ID_MIN && def->id < SC_SYSTEM_ID_MAX) {
@@ -449,6 +475,13 @@ ModifySpace::prepare(struct alter_space *alter)
 			  (unsigned) def.id,
 			  "can not change arity on a non-empty space");
 	}
+	if (def.temporary != alter->old_space->def.temporary &&
+	    alter->old_space->engine.state != READY_NO_KEYS &&
+	    space_size(alter->old_space) > 0) {
+		tnt_raise(ClientError, ER_ALTER_SPACE,
+			  (unsigned) space_id(alter->old_space),
+			  "can not switch temporary flag on a non-empty space");
+	}
 }
 
 /** Amend the definition of the new space. */
diff --git a/src/box/box.cc b/src/box/box.cc
index ab20ae872615770e4baeba26a241006215dd9ae9..324255cf65ff5c07aa47333783b24f0c0350e8c4 100644
--- a/src/box/box.cc
+++ b/src/box/box.cc
@@ -309,6 +309,8 @@ struct snapshot_space_param {
 static void
 snapshot_space(struct space *sp, void *udata)
 {
+	if (space_is_temporary(sp))
+		return;
 	struct tuple *tuple;
 	struct snapshot_space_param *ud = (struct snapshot_space_param *) udata;
 	Index *pk = space_index(sp, 0);
diff --git a/src/box/box_lua_space.cc b/src/box/box_lua_space.cc
index 4a6b39c813fe32e0e9fe8cb77b6e6085c44809bf..4a153a45d52eb3821dc03e1e0e678062692dc3e6 100644
--- a/src/box/box_lua_space.cc
+++ b/src/box/box_lua_space.cc
@@ -57,6 +57,11 @@ lbox_fillspace(struct lua_State *L, struct space *space, int i)
 	lua_pushnumber(L, space_id(space));
 	lua_settable(L, i);
 
+	/* space.is_temp */
+	lua_pushstring(L, "temporary");
+	lua_pushboolean(L, space_is_temporary(space));
+	lua_settable(L, i);
+
 	/* space.name */
 	lua_pushstring(L, "name");
 	lua_pushstring(L, space_name(space));
diff --git a/src/box/key_def.h b/src/box/key_def.h
index 56bfc26dff50a2472b9bcd69ad2638bf5044fdc2..ee0f61c98a55df6e0646615bcbd752facde145f2 100644
--- a/src/box/key_def.h
+++ b/src/box/key_def.h
@@ -194,6 +194,13 @@ struct space_def {
 	 */
 	uint32_t arity;
 	char name[BOX_NAME_MAX + 1];
+        /**
+	 * The space is a temporary:
+	 * - it is empty at server start
+	 * - changes are not written to WAL
+	 * - changes are not part of a snapshot
+	 */
+	bool temporary;
 };
 
 /** Check space definition structure for errors. */
diff --git a/src/box/lua/schema.lua b/src/box/lua/schema.lua
index f92066fb79e468691642401ccac3989f91ed2e30..ca07fc2cf81f7ce0780a6878cfb8fb767db960c9 100644
--- a/src/box/lua/schema.lua
+++ b/src/box/lua/schema.lua
@@ -13,6 +13,9 @@ box.schema.space.create = function(name, options)
         options = {}
     end
     local if_not_exists = options.if_not_exists
+
+    local temporary = options.temporary and "temporary" or ""
+
     if box.space[name] then
         if options.if_not_exists then
             return box.space[name], "not created"
@@ -35,7 +38,7 @@ box.schema.space.create = function(name, options)
     if options.arity == nil then
         options.arity = 0
     end
-    _space:insert(id, options.arity, name)
+    _space:insert(id, options.arity, name, temporary)
     return box.space[id], "created"
 end
 box.schema.create_space = box.schema.space.create
diff --git a/src/box/schema.cc b/src/box/schema.cc
index 065f28ec10c5dfc5de59a2d76afa3f00255fe820..3b69278e0f219a4e9a4a3d740f2dc6b02f8c927c 100644
--- a/src/box/schema.cc
+++ b/src/box/schema.cc
@@ -217,7 +217,7 @@ schema_init()
 	 * (and re-created) first.
 	 */
 	/* _schema - key/value space with schema description */
-	struct space_def def = { SC_SCHEMA_ID, 0, "_schema" };
+	struct space_def def = { SC_SCHEMA_ID, 0, "_schema", false };
 	struct key_def *key_def = key_def_new(def.id,
 					      0 /* index id */,
 					      "primary", /* name */
diff --git a/src/box/space.h b/src/box/space.h
index 98ba9fb211440270bbd56e56e645a9514ce6810e..c62558e6c57cab21a1d0085f02205e7fce5fdc37 100644
--- a/src/box/space.h
+++ b/src/box/space.h
@@ -130,6 +130,11 @@ space_id(struct space *space) { return space->def.id; }
 static inline const char *
 space_name(struct space *space) { return space->def.name; }
 
+
+/** Return true if space is temporary. */
+static inline bool
+space_is_temporary(struct space *space) { return space->def.temporary; }
+
 /**
  * @brief A single method to handle REPLACE, DELETE and UPDATE.
  *
diff --git a/src/box/txn.cc b/src/box/txn.cc
index 96f4f2316c911b13a682a5e2a80fbd972f466fa7..1dcfd99f3c2d66f81b16639bbe923afe86ed8820 100644
--- a/src/box/txn.cc
+++ b/src/box/txn.cc
@@ -82,7 +82,8 @@ txn_begin()
 void
 txn_commit(struct txn *txn)
 {
-	if (txn->old_tuple || txn->new_tuple) {
+	if ((txn->old_tuple || txn->new_tuple) &&
+	    !space_is_temporary(txn->space)) {
 		int64_t lsn = next_lsn(recovery_state);
 
 		ev_tstamp start = ev_now(), stop;
diff --git a/test/box/alter_limits.result b/test/box/alter_limits.result
index d6ef57175960da88c851969e0bfe4fae88fd94f7..6dbc6c985477625730dbf5846e629d209b3fb008 100644
--- a/test/box/alter_limits.result
+++ b/test/box/alter_limits.result
@@ -133,7 +133,7 @@ s:select(0)
 ...
 s:select_range(0, 0, 0)
 ---
-- error: '[string "-- schema.lua (internal file)..."]:205: attempt to index a nil
+- error: '[string "-- schema.lua (internal file)..."]:208: attempt to index a nil
     value'
 ...
 s:delete(0)
@@ -266,7 +266,7 @@ box.space['_space']:update(s.n, "=p", 1, 1)
 -- remove arity - ok
 box.space['_space']:update(s.n, "=p", 1, 0)
 ---
-- [512, 0, 1953719668]
+- [512, 0, 1953719668, '']
 ...
 s:select(0)
 ---
@@ -286,7 +286,7 @@ s:select(0)
 -- set arity of an empty space
 box.space['_space']:update(s.n, "=p", 1, 3)
 ---
-- [512, 3, 1953719668]
+- [512, 3, 1953719668, '']
 ...
 s:select(0)
 ---
diff --git a/test/box/lua.result b/test/box/lua.result
index 85d52d76664b1fb2895c412fc1c513725f8b939b..d0a3a5b6832350b6ce4c0d3a050ff1802c80309a 100644
--- a/test/box/lua.result
+++ b/test/box/lua.result
@@ -529,10 +529,11 @@ t = {} for k,v in pairs(box.space[0]) do if type(v) ~= 'table' then table.insert
 ...
 t
 ---
-- - 'arity: 0'
+- - 'temporary: false'
   - 'n: 0'
   - 'enabled: true'
   - 'name: tweedledum'
+  - 'arity: 0'
 ...
 box.cfg.reload()
 ---
@@ -578,10 +579,11 @@ t = {} for k,v in pairs(box.space[0]) do if type(v) ~= 'table' then table.insert
 ...
 t
 ---
-- - 'arity: 0'
+- - 'temporary: false'
   - 'n: 0'
   - 'enabled: true'
   - 'name: tweedledum'
+  - 'arity: 0'
 ...
 box.cfg.nosuchoption = 1
 ---
diff --git a/test/box/temp_spaces.result b/test/box/temp_spaces.result
new file mode 100644
index 0000000000000000000000000000000000000000..eb11a93fb044a13e8d32d70e99ea5756aac0e08e
--- /dev/null
+++ b/test/box/temp_spaces.result
@@ -0,0 +1,131 @@
+-- temporary spaces
+-- not a temporary
+s = box.schema.create_space('t', { temporary = true })
+---
+...
+s.temporary
+---
+- true
+...
+s:drop()
+---
+...
+-- not a temporary, too
+s = box.schema.create_space('t', { temporary = false })
+---
+...
+s.temporary
+---
+- false
+...
+s:drop()
+---
+...
+-- not a temporary, too
+s = box.schema.create_space('t', { temporary = nil })
+---
+...
+s.temporary
+---
+- false
+...
+s:drop()
+---
+...
+s = box.schema.create_space('t', { temporary = true })
+---
+...
+s:create_index('primary', 'hash')
+---
+...
+s:insert(1, 2, 3)
+---
+- [1, 2, 3]
+...
+s:select(0, 1)
+---
+- [1, 2, 3]
+...
+s:len()
+---
+- 1
+...
+box.space[box.schema.SPACE_ID]:update(s.n, '=p', 3, 'temporary')
+---
+- [512, 0, 't', 'temporary']
+...
+box.space[box.schema.SPACE_ID]:update(s.n, '=p', 3, '')
+---
+- error: 'Can''t modify space 512: can not switch temporary flag on a non-empty space'
+...
+--# stop server default
+--# start server default
+s = box.space.t
+---
+...
+s:len()
+---
+- 0
+...
+s.temporary
+---
+- true
+...
+box.space[box.schema.SPACE_ID]:update(s.n, '=p', 3, 'no-temporary')
+---
+- [512, 0, 't', 'no-temporary']
+...
+s.temporary
+---
+- false
+...
+box.space[box.schema.SPACE_ID]:update(s.n, '=p', 3, ',:asfda:temporary')
+---
+- [512, 0, 't', ',:asfda:temporary']
+...
+s.temporary
+---
+- false
+...
+box.space[box.schema.SPACE_ID]:update(s.n, '=p', 3, 'a,b,c,d,e')
+---
+- [512, 0, 't', 'a,b,c,d,e']
+...
+s.temporary
+---
+- false
+...
+box.space[box.schema.SPACE_ID]:update(s.n, '=p', 3, 'temporary')
+---
+- [512, 0, 't', 'temporary']
+...
+s.temporary
+---
+- true
+...
+s:select(0, 1)
+---
+...
+s:insert(1, 2, 3)
+---
+- [1, 2, 3]
+...
+box.space[box.schema.SPACE_ID]:update(s.n, '=p', 3, 'temporary')
+---
+- [512, 0, 't', 'temporary']
+...
+box.space[box.schema.SPACE_ID]:update(s.n, '=p', 3, 'no-temporary')
+---
+- error: 'Can''t modify space 512: can not switch temporary flag on a non-empty space'
+...
+s:delete(1)
+---
+- [1, 2, 3]
+...
+box.space[box.schema.SPACE_ID]:update(s.n, '=p', 3, 'no-temporary')
+---
+- [512, 0, 't', 'no-temporary']
+...
+s:drop()
+---
+...
diff --git a/test/box/temp_spaces.test.lua b/test/box/temp_spaces.test.lua
new file mode 100644
index 0000000000000000000000000000000000000000..a21ae36bdfd80800acdfa2725fdb71a4c41043e8
--- /dev/null
+++ b/test/box/temp_spaces.test.lua
@@ -0,0 +1,51 @@
+-- temporary spaces
+-- not a temporary
+s = box.schema.create_space('t', { temporary = true })
+s.temporary
+s:drop()
+
+-- not a temporary, too
+s = box.schema.create_space('t', { temporary = false })
+s.temporary
+s:drop()
+
+-- not a temporary, too
+s = box.schema.create_space('t', { temporary = nil })
+s.temporary
+s:drop()
+
+s = box.schema.create_space('t', { temporary = true })
+s:create_index('primary', 'hash')
+
+s:insert(1, 2, 3)
+s:select(0, 1)
+s:len()
+
+box.space[box.schema.SPACE_ID]:update(s.n, '=p', 3, 'temporary')
+box.space[box.schema.SPACE_ID]:update(s.n, '=p', 3, '')
+
+--# stop server default
+--# start server default
+
+s = box.space.t
+s:len()
+s.temporary
+
+box.space[box.schema.SPACE_ID]:update(s.n, '=p', 3, 'no-temporary')
+s.temporary
+box.space[box.schema.SPACE_ID]:update(s.n, '=p', 3, ',:asfda:temporary')
+s.temporary
+box.space[box.schema.SPACE_ID]:update(s.n, '=p', 3, 'a,b,c,d,e')
+s.temporary
+box.space[box.schema.SPACE_ID]:update(s.n, '=p', 3, 'temporary')
+s.temporary
+
+s:select(0, 1)
+s:insert(1, 2, 3)
+
+box.space[box.schema.SPACE_ID]:update(s.n, '=p', 3, 'temporary')
+box.space[box.schema.SPACE_ID]:update(s.n, '=p', 3, 'no-temporary')
+
+s:delete(1)
+box.space[box.schema.SPACE_ID]:update(s.n, '=p', 3, 'no-temporary')
+s:drop()