diff --git a/docs/book/src/SUMMARY.md b/docs/book/src/SUMMARY.md
index dfb3953d967..bae178b6276 100644
--- a/docs/book/src/SUMMARY.md
+++ b/docs/book/src/SUMMARY.md
@@ -128,6 +128,7 @@
- [Extending](./plugins/extending.md)
- [CLI and Plugins](./plugins/extending/extending_cli_features_and_plugins.md)
- [External Plugins](./plugins/extending/external-plugins.md)
+ - [Custom Markers](./plugins/extending/custom-markers.md)
- [E2E Tests](./plugins/extending/testing-plugins.md)
- [Plugins Versioning](./plugins/plugins-versioning.md)
diff --git a/docs/book/src/plugins/extending/custom-markers.md b/docs/book/src/plugins/extending/custom-markers.md
new file mode 100644
index 00000000000..69adfe02322
--- /dev/null
+++ b/docs/book/src/plugins/extending/custom-markers.md
@@ -0,0 +1,238 @@
+# Creating Custom Markers
+
+## Overview
+
+When using Kubebuilder as a library, you may need to scaffold files with extensions that aren't natively supported by Kubebuilder's marker system. This guide shows you how to create custom marker support for any file extension.
+
+## When to Use Custom Markers
+
+Custom markers are useful when:
+
+- You're building an external plugin for languages not natively supported by Kubebuilder
+- You want to scaffold files with custom extensions (`.rs`, `.java`, `.py`, `.tpl`, etc.)
+- You need scaffolding markers in non-Go files for your own use cases
+- Your file extensions aren't (and shouldn't be) part of the core `commentsByExt` map
+
+## Understanding Markers
+
+Markers are special comments used by Kubebuilder for scaffolding purposes. They indicate where code can be inserted or modified. The core Kubebuilder marker system only supports `.go`, `.yaml`, and `.yml` files by default.
+
+Example of a marker in a Go file:
+```go
+// +kubebuilder:scaffold:imports
+```
+
+## Implementation Example
+
+Here's how to implement custom markers for Rust files (`.rs`). This same pattern can be applied to any file extension.
+
+### Define Your Marker Type
+
+```go
+// pkg/markers/rust.go
+package markers
+
+import (
+ "fmt"
+ "path/filepath"
+ "strings"
+)
+
+const RustPluginPrefix = "+rust:scaffold:"
+
+type RustMarker struct {
+ prefix string
+ comment string
+ value string
+}
+
+func NewRustMarker(path string, value string) (RustMarker, error) {
+ ext := filepath.Ext(path)
+ if ext != ".rs" {
+ return RustMarker{}, fmt.Errorf("expected .rs file, got %s", ext)
+ }
+
+ return RustMarker{
+ prefix: formatPrefix(RustPluginPrefix),
+ comment: "//",
+ value: value,
+ }, nil
+}
+
+func (m RustMarker) String() string {
+ return m.comment + " " + m.prefix + m.value
+}
+
+func formatPrefix(prefix string) string {
+ trimmed := strings.TrimSpace(prefix)
+ var builder strings.Builder
+ if !strings.HasPrefix(trimmed, "+") {
+ builder.WriteString("+")
+ }
+ builder.WriteString(trimmed)
+ if !strings.HasSuffix(trimmed, ":") {
+ builder.WriteString(":")
+ }
+ return builder.String()
+}
+```
+
+
+
+### Use in Template Generation
+
+```go
+package templates
+
+import (
+ "fmt"
+ "github.com/yourorg/yourplugin/pkg/markers"
+)
+
+func GenerateRustFile(projectName string) (string, error) {
+ marker, err := markers.NewRustMarker("src/main.rs", "imports")
+ if err != nil {
+ return "", err
+ }
+
+ content := fmt.Sprintf(`// Generated by Rust Plugin
+%s
+
+use std::error::Error;
+
+fn main() -> Result<(), Box> {
+ println!("Hello from %s!");
+ Ok(())
+}
+`, marker.String(), projectName)
+
+ return content, nil
+}
+
+func GenerateCargoToml(projectName string) string {
+ return fmt.Sprintf(`[package]
+name = "%s"
+version = "0.1.0"
+edition = "2021"
+
+[dependencies]
+`, projectName)
+}
+```
+
+### Integrate with External Plugin
+
+```go
+package main
+
+import (
+ "bufio"
+ "encoding/json"
+ "fmt"
+ "io"
+ "os"
+
+ "sigs.k8s.io/kubebuilder/v4/pkg/plugin/external"
+ "github.com/yourorg/yourplugin/pkg/markers"
+)
+
+func main() {
+ // External plugins communicate via JSON over STDIN/STDOUT
+ reader := bufio.NewReader(os.Stdin)
+ input, err := io.ReadAll(reader)
+ if err != nil {
+ returnError(fmt.Errorf("error reading STDIN: %w", err))
+ return
+ }
+
+ pluginRequest := &external.PluginRequest{}
+ err = json.Unmarshal(input, pluginRequest)
+ if err != nil {
+ returnError(fmt.Errorf("error unmarshaling request: %w", err))
+ return
+ }
+
+ var response external.PluginResponse
+
+ switch pluginRequest.Command {
+ case "init":
+ response = handleInit(pluginRequest)
+ default:
+ response = external.PluginResponse{
+ Command: pluginRequest.Command,
+ Error: true,
+ ErrorMsgs: []string{fmt.Sprintf("unknown command: %s", pluginRequest.Command)},
+ }
+ }
+
+ output, err := json.Marshal(response)
+ if err != nil {
+ fmt.Fprintf(os.Stderr, "failed to marshal response: %v\n", err)
+ os.Exit(1)
+ }
+ fmt.Printf("%s", output)
+}
+
+func handleInit(req *external.PluginRequest) external.PluginResponse {
+ // Create Rust file with custom markers
+ marker, err := markers.NewRustMarker("src/main.rs", "imports")
+ if err != nil {
+ return external.PluginResponse{
+ Command: "init",
+ Error: true,
+ ErrorMsgs: []string{fmt.Sprintf("failed to create Rust marker: %v", err)},
+ }
+ }
+
+ fileContent := fmt.Sprintf(`// Generated by Rust Plugin
+%s
+
+use std::error::Error;
+
+fn main() -> Result<(), Box> {
+ println!("Hello from Rust!");
+ Ok(())
+}
+`, marker.String())
+
+ // External plugins use "universe" to represent file changes.
+ // "universe" is a map from file paths to their file contents,
+ // passed through the plugin chain to coordinate file generation.
+ universe := make(map[string]string)
+ universe["src/main.rs"] = fileContent
+
+ return external.PluginResponse{
+ Command: "init",
+ Universe: universe,
+ }
+}
+
+func returnError(err error) {
+ response := external.PluginResponse{
+ Error: true,
+ ErrorMsgs: []string{err.Error()},
+ }
+ output, marshalErr := json.Marshal(response)
+ if marshalErr != nil {
+ fmt.Fprintf(os.Stderr, "failed to marshal error response: %v\n", marshalErr)
+ os.Exit(1)
+ }
+ fmt.Printf("%s", output)
+}
+```
+
+## Adapting for Other Languages
+
+To support other file extensions, modify the marker implementation by changing:
+
+- The comment syntax (e.g., `//` for Java, `#` for Python, `{{/* ... */}}` for templates)
+- The file extension check (e.g., `.java`, `.py`, `.tpl`)
+- The marker prefix (e.g., `+java:scaffold:`, `+python:scaffold:`)
+
+For more information on creating external plugins, see [External Plugins](external-plugins.md).
\ No newline at end of file