diff --git a/pkg/extension/extension.go b/pkg/extension/extension.go
index 26d9b6d51171307161e68ceef0830857fbb569a8..fb8044c7a2f4c76b775840c5628319196a0493ae 100644
--- a/pkg/extension/extension.go
+++ b/pkg/extension/extension.go
@@ -21,6 +21,8 @@ const (
 	StateInstalled  = pb.SpaceExtensions_INSTALLED
 	StateInProgress = pb.SpaceExtensions_IN_PROGRESS
 	StateFail       = pb.SpaceExtensions_FAIL
+
+	ExtensionMetadataKey = "extension"
 )
 
 type (
diff --git a/pkg/schema/schema.go b/pkg/schema/schema.go
index e0eca6b6ffc02511acbe3712b2f2bf83fc5bc05e..c468ee99f156b3acf09435474e23c5427702b2e7 100644
--- a/pkg/schema/schema.go
+++ b/pkg/schema/schema.go
@@ -12,7 +12,8 @@ import (
 
 type Schema struct {
 	field.Field
-	Loaded bool `json:"loaded"`
+	Loaded   bool              `json:"loaded"`
+	Metadata map[string]string `json:"metadata"`
 }
 
 func New(kv ...interface{}) *Schema {
@@ -33,8 +34,9 @@ var (
 
 func (s *Schema) Clone(reset bool) *Schema {
 	return &Schema{
-		Field:  *s.Field.Clone(reset),
-		Loaded: s.Loaded,
+		Field:    *s.Field.Clone(reset),
+		Loaded:   s.Loaded,
+		Metadata: s.Metadata,
 	}
 }
 
@@ -43,6 +45,21 @@ func (s Schema) WithIncludes(includes ...interface{}) *Schema {
 	return &s
 }
 
+func (s *Schema) WithMetadata(kv ...string) *Schema {
+	if s.Metadata == nil {
+		s.Metadata = make(map[string]string)
+	}
+	for i := 0; i < len(kv); i += 2 {
+		s.Metadata[kv[i]] = kv[i+1]
+	}
+	return s
+}
+
+func (s Schema) SetMetadata(md map[string]string) *Schema {
+	s.Metadata = md
+	return &s
+}
+
 func (s *Schema) Load(ctx context.Context) error {
 	if s.Loaded {
 		return nil
diff --git a/pkg/schema/schema_json.go b/pkg/schema/schema_json.go
index 906acb5d0d361611d01f43d10bd0f80377ccb4a7..72b4ac25bb78c6d8cb868bd7018e81acf2d2080c 100644
--- a/pkg/schema/schema_json.go
+++ b/pkg/schema/schema_json.go
@@ -6,8 +6,8 @@ import (
 )
 
 type jsonSchema struct {
-	//Field  json.RawMessage `json:"field,inline"`
-	Loaded bool `json:"loaded"`
+	Loaded   bool              `json:"loaded"`
+	Metadata map[string]string `json:"metadata"`
 }
 
 func (s *Schema) UnmarshalJSON(b []byte) error {
@@ -17,20 +17,12 @@ func (s *Schema) UnmarshalJSON(b []byte) error {
 		return errors.Wrapf(err, "error unmarshal json into field")
 	}
 	s.Loaded = j.Loaded
+	s.Metadata = j.Metadata
 
 	if err := s.Field.UnmarshalJSON(b); err != nil {
 		return err
 	}
 
-	//if len(j.Field) > 0 {
-	//	if err := s.Field.UnmarshalJSON(j.Field); err != nil {
-	//		return err
-	//	}
-	//	//if err := jsoniter.Unmarshal(j.Field, &s.Field); err != nil {
-	//	//	return err
-	//	//}
-	//}
-
 	return nil
 }
 
@@ -42,8 +34,8 @@ func (s *Schema) MarshalJSON() ([]byte, error) {
 	}
 
 	jsonSch, err := jsoniter.Marshal(jsonSchema{
-		//Field: b,
-		Loaded: s.Loaded,
+		Loaded:   s.Loaded,
+		Metadata: s.Metadata,
 	})
 	if err != nil {
 		return nil, err
diff --git a/pkg/schema/test/object_test.go b/pkg/schema/test/object_test.go
index e5af975cdb9ed8e86044e5d357530dd2420afee9..9ba618794f122ed8c5551324b02d6b8cf9e46829 100644
--- a/pkg/schema/test/object_test.go
+++ b/pkg/schema/test/object_test.go
@@ -110,6 +110,9 @@ func TestSchema_JSON(t *testing.T) {
 		"evaluatedField", field.String(modify.Value("stringField2 + '_' 	")),
 	)
 	sch.Loaded = true
+	sch.Metadata = map[string]string{
+		"extension": "test-extension",
+	}
 
 	b, err := json.MarshalIndent(sch, "", "  ")
 	require.NoError(t, err)
diff --git a/pkg/setup/collection.go b/pkg/setup/collection.go
index d61ba14d18f2d8bc93d2a535392b8b608ba6e2d0..1ebd0a9933e72ab85622d39e86bf06e6421d8975 100644
--- a/pkg/setup/collection.go
+++ b/pkg/setup/collection.go
@@ -8,13 +8,15 @@ import (
 	"git.perx.ru/perxis/perxis-go/pkg/collections"
 	"git.perx.ru/perxis/perxis-go/pkg/environments"
 	"git.perx.ru/perxis/perxis-go/pkg/errors"
+	"git.perx.ru/perxis/perxis-go/pkg/extension"
 	"go.uber.org/zap"
 )
 
 var (
-	ErrCheckCollections     = errors.New("collections check error")
-	ErrInstallCollections   = errors.New("failed to install collections")
-	ErrUninstallCollections = errors.New("failed to uninstall collections")
+	ErrCheckCollections        = errors.New("collections check error")
+	ErrInstallCollections      = errors.New("failed to install collections")
+	ErrUninstallCollections    = errors.New("failed to uninstall collections")
+	ErrCollectionAlreadyExists = errors.New("failed to uninstall collections")
 )
 
 type CollectionsOption func(c *CollectionConfig)
@@ -23,6 +25,7 @@ type DeleteCollectionFn func(s *Setup, col *collections.Collection) bool
 
 type CollectionConfig struct {
 	collection *collections.Collection
+	metadata   map[string]string
 	UpdateFn   UpdateCollectionFn
 	DeleteFn   DeleteCollectionFn
 }
@@ -80,6 +83,17 @@ func UpdateExistingCollection() CollectionsOption {
 	}
 }
 
+func SetSchemaMetadata(kv ...string) CollectionsOption {
+	return func(c *CollectionConfig) {
+		if c.metadata == nil {
+			c.metadata = make(map[string]string)
+		}
+		for i := 0; i < len(kv); i += 2 {
+			c.metadata[kv[i]] = kv[i+1]
+		}
+	}
+}
+
 func (s *Setup) InstallCollections(ctx context.Context) (err error) {
 	if len(s.Collections) == 0 {
 		return nil
@@ -125,19 +139,31 @@ func (s *Setup) InstallCollection(ctx context.Context, c CollectionConfig) (setS
 	collection.SpaceID, collection.EnvID = s.SpaceID, s.EnvironmentID
 
 	var exist *collections.Collection
-	// isForce - не удалять коллекцию, если она уже существует
+
 	exist, err = s.content.Collections.Get(ctx, collection.SpaceID, collection.EnvID, collection.ID)
 	if err != nil && !strings.Contains(err.Error(), collections.ErrNotFound.Error()) {
 		return false, err
 	}
 
+	if !collection.IsView() {
+		for k, v := range c.metadata {
+			collection.Schema.WithMetadata(k, v)
+		}
+		//setMD = true
+	}
+
 	if exist == nil {
 		setSchema = !collection.IsView()
-		exist, err = s.content.Collections.Create(ctx, collection)
+		_, err = s.content.Collections.Create(ctx, collection)
 		if err != nil {
 			return false, err
 		}
 	} else {
+		if !collection.IsView() && !exist.IsView() {
+			if collection.Schema.Metadata != nil && exist.Schema.Metadata[extension.ExtensionMetadataKey] != collection.Schema.Metadata[extension.ExtensionMetadataKey] && !s.IsForce() {
+				return false, ErrCollectionAlreadyExists
+			}
+		}
 		var upd bool
 		collection, upd, setSchema = c.UpdateFn(s, exist, c.collection)
 		if upd {
diff --git a/pkg/setup/collection_test.go b/pkg/setup/collection_test.go
index 433aa7397d450f8226a95af032a7cd177697fbed..f23aa2b4949d79605c39e356aafe1592c616301f 100644
--- a/pkg/setup/collection_test.go
+++ b/pkg/setup/collection_test.go
@@ -22,6 +22,8 @@ func TestSetup_InstallCollections(t *testing.T) {
 		collections     []*collections.Collection
 		collectionsCall func(svc *mockscollections.Collections)
 		envsCall        func(svc *envmocks.Environments)
+		co              CollectionsOption
+		force           bool
 		wantErr         func(t *testing.T, err error)
 	}{
 		{
@@ -91,6 +93,81 @@ func TestSetup_InstallCollections(t *testing.T) {
 				assert.Contains(t, errors.GetDetail(err), "Возникла ошибка при настройке коллекции space(1)")
 			},
 		},
+		{
+			name:        "Update extension collection with metadata",
+			collections: []*collections.Collection{{ID: "1", SpaceID: "sp", EnvID: "env", Schema: schema.New("name", field.String()).WithMetadata("extension", "test-extension")}},
+			collectionsCall: func(svc *mockscollections.Collections) {
+				svc.On("Get", mock.Anything, "sp", "env", "1").Return(&collections.Collection{ID: "1", SpaceID: "sp", EnvID: "env", Schema: schema.New("name", field.String()).WithMetadata("extension", "test-extension")}, nil).Once()
+				svc.On("Update", mock.Anything, &collections.Collection{ID: "1", SpaceID: "sp", EnvID: "env", Schema: schema.New("name", field.String()).WithMetadata("extension", "test-extension")}).Return(nil).Once()
+				svc.On("SetSchema", mock.Anything, "sp", "env", "1", schema.New("name", field.String())).Return(nil).Once()
+			},
+			envsCall: func(svc *envmocks.Environments) {
+				svc.On("Migrate", mock.Anything, "sp", "env", &environments.MigrateOptions{Wait: true}).Return(nil).Once()
+			},
+			wantErr: func(t *testing.T, err error) {
+				assert.NoError(t, err)
+			},
+			co: SetSchemaMetadata("extension", "test-extension"),
+		},
+		{
+			name:        "Fail to update collection with the same id",
+			collections: []*collections.Collection{{ID: "1", SpaceID: "sp", EnvID: "env", Schema: schema.New("name", field.String()).WithMetadata("extension", "test-extension")}},
+			collectionsCall: func(svc *mockscollections.Collections) {
+				svc.On("Get", mock.Anything, "sp", "env", "1").Return(&collections.Collection{ID: "1", SpaceID: "sp", EnvID: "env", Schema: schema.New("surname", field.String())}, nil).Once()
+			},
+			wantErr: func(t *testing.T, err error) {
+				assert.Error(t, err)
+			},
+			co: SetSchemaMetadata("extension", "test-extension"),
+		},
+		{
+			name:        "Update collection with the same id, with force",
+			collections: []*collections.Collection{{ID: "1", SpaceID: "sp", EnvID: "env", Schema: schema.New("name", field.String()).WithMetadata("extension", "test-extension")}},
+			collectionsCall: func(svc *mockscollections.Collections) {
+				svc.On("Get", mock.Anything, "sp", "env", "1").Return(&collections.Collection{ID: "1", SpaceID: "sp", EnvID: "env", Schema: schema.New("name", field.String()).WithMetadata("extension", "test-extension")}, nil).Once()
+				svc.On("Update", mock.Anything, &collections.Collection{ID: "1", SpaceID: "sp", EnvID: "env", Schema: schema.New("name", field.String()).WithMetadata("extension", "test-extension")}).Return(nil).Once()
+				svc.On("SetSchema", mock.Anything, "sp", "env", "1", schema.New("name", field.String())).Return(nil).Once()
+			},
+			envsCall: func(svc *envmocks.Environments) {
+				svc.On("Migrate", mock.Anything, "sp", "env", &environments.MigrateOptions{Wait: true}).Return(nil).Once()
+			},
+			wantErr: func(t *testing.T, err error) {
+				assert.NoError(t, err)
+			},
+			co:    SetSchemaMetadata("extension", "test-extension"),
+			force: true,
+		},
+		{
+			name:        "Update exist view collection with the same id",
+			collections: []*collections.Collection{{ID: "1", SpaceID: "sp", EnvID: "env", Schema: schema.New("name", field.String()).WithMetadata("extension", "test-extension")}},
+			collectionsCall: func(svc *mockscollections.Collections) {
+				svc.On("Get", mock.Anything, "sp", "env", "1").Return(&collections.Collection{ID: "1", SpaceID: "sp", EnvID: "env", View: &collections.View{SpaceID: "sp2", EnvID: environments.DefaultEnvironment, CollectionID: "2"}}, nil).Once()
+				svc.On("Update", mock.Anything, &collections.Collection{ID: "1", SpaceID: "sp", EnvID: "env", Schema: schema.New("name", field.String()).WithMetadata("extension", "test-extension")}).Return(nil).Once()
+				svc.On("SetSchema", mock.Anything, "sp", "env", "1", schema.New("name", field.String()).WithMetadata("extension", "test-extension")).Return(nil).Once()
+			},
+			envsCall: func(svc *envmocks.Environments) {
+				svc.On("Migrate", mock.Anything, "sp", "env", &environments.MigrateOptions{Wait: true}).Return(nil).Once()
+			},
+			wantErr: func(t *testing.T, err error) {
+				assert.NoError(t, err)
+			},
+			co: SetSchemaMetadata("extension", "test-extension"),
+		},
+		{
+			name:        "Update view collection with the same id to new view collection",
+			collections: []*collections.Collection{{ID: "1", SpaceID: "sp", EnvID: "env", View: &collections.View{SpaceID: "sp3", EnvID: environments.DefaultEnvironment, CollectionID: "3"}}},
+			collectionsCall: func(svc *mockscollections.Collections) {
+				svc.On("Get", mock.Anything, "sp", "env", "1").Return(&collections.Collection{ID: "1", SpaceID: "sp", EnvID: "env", View: &collections.View{SpaceID: "sp2", EnvID: environments.DefaultEnvironment, CollectionID: "2"}}, nil).Once()
+				svc.On("Update", mock.Anything, &collections.Collection{ID: "1", SpaceID: "sp", EnvID: "env", View: &collections.View{SpaceID: "sp3", EnvID: environments.DefaultEnvironment, CollectionID: "3"}}).Return(nil).Once()
+			},
+			envsCall: func(svc *envmocks.Environments) {
+				svc.On("Migrate", mock.Anything, "sp", "env", &environments.MigrateOptions{Wait: true}).Return(nil).Once()
+			},
+			wantErr: func(t *testing.T, err error) {
+				assert.NoError(t, err)
+			},
+			co: SetSchemaMetadata("extension", "test-extension"),
+		},
 	}
 
 	for _, tt := range tests {
@@ -105,7 +182,12 @@ func TestSetup_InstallCollections(t *testing.T) {
 			}
 
 			s := NewSetup(&content.Content{Collections: c, Environments: e}, "sp", "env", nil)
-			s.AddCollections(tt.collections)
+			s.force = tt.force
+			if tt.co != nil {
+				s.AddCollections(tt.collections, tt.co)
+			} else {
+				s.AddCollections(tt.collections)
+			}
 			tt.wantErr(t, s.InstallCollections(context.Background()))
 		})
 	}