diff --git a/yaml/file_resolver.go b/yaml/file_resolver.go
new file mode 100644
index 0000000000000000000000000000000000000000..03a795d9871571c1c5b640ed9c91877b8918de18
--- /dev/null
+++ b/yaml/file_resolver.go
@@ -0,0 +1,31 @@
+package yaml
+
+import (
+	"io"
+
+	"git.perx.ru/perxis/perxis-go/pkg/errors"
+	"gopkg.in/yaml.v3"
+)
+
+// FileResolver подключает содержимое целевого файла в качестве значения поля.
+func FileResolver(tp TagProcessor, node *yaml.Node) (*yaml.Node, error) {
+	if node.Kind != yaml.ScalarNode {
+		return nil, errors.New("!include on a non-scalar node")
+	}
+
+	file, err := tp.FS().Open(node.Value)
+	if err != nil {
+		return nil, err
+	}
+	defer func() { _ = file.Close() }()
+
+	bytes, err := io.ReadAll(file)
+	if err != nil {
+		return nil, err
+	}
+
+	out := new(yaml.Node)
+	out.SetString(string(bytes))
+
+	return out, err
+}
diff --git a/yaml/include_resolver.go b/yaml/include_resolver.go
new file mode 100644
index 0000000000000000000000000000000000000000..0f459c539ab2a244413eab46f2a67181cfce0a0a
--- /dev/null
+++ b/yaml/include_resolver.go
@@ -0,0 +1,33 @@
+package yaml
+
+import (
+	"path/filepath"
+
+	"git.perx.ru/perxis/perxis-go/pkg/errors"
+	"gopkg.in/yaml.v3"
+)
+
+// IncludeResolver включает содержимое целевого YAML-файла в поле.
+func IncludeResolver(tp TagProcessor, node *yaml.Node) (*yaml.Node, error) {
+	if node.Kind != yaml.ScalarNode {
+		return nil, errors.New("!include on a non-scalar node")
+	}
+
+	if ext := filepath.Ext(node.Value); ext != ".yaml" && ext != ".yml" {
+		return nil, errors.New("!include on file with unknown extension")
+	}
+
+	file, err := tp.FS().Open(node.Value)
+	if err != nil {
+		return nil, err
+	}
+	defer func() { _ = file.Close() }()
+
+	out := new(yaml.Node)
+	err = yaml.NewDecoder(file).Decode(WithTagProcessor(tp.FS())(out))
+	if err != nil {
+		return nil, err
+	}
+
+	return out, nil
+}
diff --git a/yaml/tag_processor.go b/yaml/tag_processor.go
new file mode 100644
index 0000000000000000000000000000000000000000..89678b93d8242fb15260193208d8445404570cc4
--- /dev/null
+++ b/yaml/tag_processor.go
@@ -0,0 +1,61 @@
+package yaml
+
+import (
+	"io/fs"
+
+	"gopkg.in/yaml.v3"
+)
+
+type TagProcessor interface {
+	yaml.Unmarshaler
+	FS() fs.FS
+}
+
+// tagProcessor обёртка для декодирования целевого значения.
+//
+// Перед декодированием значения обрабатываются все теги узла.
+type tagProcessor struct {
+	fsys   fs.FS
+	target any
+}
+
+// WithTagProcessor возвращает функцию, которая оборачивает декодируемые значения для поддержки обработки тегов YAML.
+//
+// Для путей к файлам, используемых в качестве значений тегов, пути должны быть указаны относительно переданной файловой системы.
+func WithTagProcessor(fsys fs.FS) func(any) *tagProcessor {
+	return func(v any) *tagProcessor {
+		return &tagProcessor{fsys: fsys, target: v}
+	}
+}
+
+func (tp *tagProcessor) FS() fs.FS {
+	return tp.fsys
+}
+
+func (tp *tagProcessor) UnmarshalYAML(value *yaml.Node) error {
+	resolved, err := resolveTags(tp, value)
+	if err != nil {
+		return err
+	}
+	return resolved.Decode(tp.target)
+}
+
+// resolveTags обрабатывает все теги YAML для переданного узла и возвращает исправленный узел.
+// Если узел представляет собой последовательность или словарь, то обрабатываются теги для его дочерних элементов.
+func resolveTags(tp TagProcessor, node *yaml.Node) (*yaml.Node, error) {
+	switch node.Kind {
+	case yaml.SequenceNode, yaml.MappingNode:
+		for i := range node.Content {
+			var err error
+			node.Content[i], err = resolveTags(tp, node.Content[i])
+			if err != nil {
+				return nil, err
+			}
+		}
+	default:
+		if resolver, ok := tagResolvers[node.Tag]; ok {
+			return resolver.Resolve(tp, node)
+		}
+	}
+	return node, nil
+}
diff --git a/yaml/tag_processor_test.go b/yaml/tag_processor_test.go
new file mode 100644
index 0000000000000000000000000000000000000000..79e9f5f80dc452b099176d922a7c4fc5eda008bf
--- /dev/null
+++ b/yaml/tag_processor_test.go
@@ -0,0 +1,44 @@
+package yaml
+
+import (
+	"testing"
+
+	"git.perx.ru/perxis/perxis-go/yaml/testdata"
+	"github.com/stretchr/testify/assert"
+	"github.com/stretchr/testify/require"
+	"gopkg.in/yaml.v3"
+)
+
+func TestTagProcessor(t *testing.T) {
+	t.Run("!file", func(t *testing.T) {
+		file, err := testdata.FS.Open("file/file_simple.yaml")
+		require.NoError(t, err)
+		defer file.Close()
+
+		var result any
+		decoder := yaml.NewDecoder(file)
+		err = decoder.Decode(WithTagProcessor(testdata.FS)(&result))
+		require.NoError(t, err)
+		assert.Equal(t, map[string]any{"config": `server {
+    listen 80;
+    server_name example.com;
+
+    location / {
+        root /var/www/example.com/html;
+        index index.html index.htm;
+        try_files $uri $uri/ =404;
+    }
+}`}, result)
+	})
+	t.Run("!include", func(t *testing.T) {
+		file, err := testdata.FS.Open("include/include_simple.yaml")
+		require.NoError(t, err)
+		defer file.Close()
+
+		var result any
+		decoder := yaml.NewDecoder(file)
+		err = decoder.Decode(WithTagProcessor(testdata.FS)(&result))
+		require.NoError(t, err)
+		assert.Equal(t, map[string]any{"data": map[string]any{"text": "Hello, World!"}}, result)
+	})
+}
diff --git a/yaml/testdata/file/file_simple.yaml b/yaml/testdata/file/file_simple.yaml
new file mode 100644
index 0000000000000000000000000000000000000000..6e7181a3eb1ae553aa5a8055c9a95f772cb43e0b
--- /dev/null
+++ b/yaml/testdata/file/file_simple.yaml
@@ -0,0 +1 @@
+config: !file file/nginx.conf
\ No newline at end of file
diff --git a/yaml/testdata/file/nginx.conf b/yaml/testdata/file/nginx.conf
new file mode 100644
index 0000000000000000000000000000000000000000..a9bbbc506ed333d44456d224cecda27d77d65827
--- /dev/null
+++ b/yaml/testdata/file/nginx.conf
@@ -0,0 +1,10 @@
+server {
+    listen 80;
+    server_name example.com;
+
+    location / {
+        root /var/www/example.com/html;
+        index index.html index.htm;
+        try_files $uri $uri/ =404;
+    }
+}
\ No newline at end of file
diff --git a/yaml/testdata/include/include_simple.yaml b/yaml/testdata/include/include_simple.yaml
new file mode 100644
index 0000000000000000000000000000000000000000..689f5f9f0cd8e2f5acf9f05eb248bf92e3df168a
--- /dev/null
+++ b/yaml/testdata/include/include_simple.yaml
@@ -0,0 +1 @@
+data: !include include/simple_data.yaml
\ No newline at end of file
diff --git a/yaml/testdata/include/simple_data.yaml b/yaml/testdata/include/simple_data.yaml
new file mode 100644
index 0000000000000000000000000000000000000000..a0bfdf8ea14a5dae1dbe66345309cc67df0b2f4e
--- /dev/null
+++ b/yaml/testdata/include/simple_data.yaml
@@ -0,0 +1 @@
+text: Hello, World!
\ No newline at end of file
diff --git a/yaml/testdata/testdata.go b/yaml/testdata/testdata.go
new file mode 100644
index 0000000000000000000000000000000000000000..f8b469980cf76e31cd31f40dfae6f6cc0f92dcb1
--- /dev/null
+++ b/yaml/testdata/testdata.go
@@ -0,0 +1,6 @@
+package testdata
+
+import "embed"
+
+//go:embed *
+var FS embed.FS
diff --git a/yaml/yaml.go b/yaml/yaml.go
new file mode 100644
index 0000000000000000000000000000000000000000..3830b76d2f606f32013236152479a2eaa2c1609f
--- /dev/null
+++ b/yaml/yaml.go
@@ -0,0 +1,34 @@
+package yaml
+
+import (
+	"gopkg.in/yaml.v3"
+)
+
+var (
+	NewDecoder = yaml.NewDecoder
+	NewEncoder = yaml.NewEncoder
+	Unmarshal  = yaml.Unmarshal
+	Marshal    = yaml.Marshal
+)
+
+var tagResolvers = make(map[string]Resolver)
+
+func RegisterTagResolver(tag string, resolver Resolver) {
+	tagResolvers[tag] = resolver
+}
+
+// Resolver обрабатывает тег YAML и возвращает его обработанный вариант
+type Resolver interface {
+	Resolve(tp TagProcessor, node *yaml.Node) (*yaml.Node, error)
+}
+
+type ResolverFunc func(TagProcessor, *yaml.Node) (*yaml.Node, error)
+
+func (fn ResolverFunc) Resolve(tp TagProcessor, node *yaml.Node) (*yaml.Node, error) {
+	return fn(tp, node)
+}
+
+func init() {
+	RegisterTagResolver("!include", ResolverFunc(IncludeResolver))
+	RegisterTagResolver("!file", ResolverFunc(FileResolver))
+}