diff --git a/commands/sketch/new.go b/commands/sketch/new.go
index b4fe3ab0e82..e91444e2e59 100644
--- a/commands/sketch/new.go
+++ b/commands/sketch/new.go
@@ -55,19 +55,33 @@ func NewSketch(ctx context.Context, req *rpc.NewSketchRequest) (*rpc.NewSketchRe
 		return nil, err
 	}
 
+	templateDir := configuration.Settings.GetString("sketch.template")
 	sketchDirPath := paths.New(sketchesDir).Join(req.SketchName)
-	if err := sketchDirPath.MkdirAll(); err != nil {
-		return nil, &arduino.CantCreateSketchError{Cause: err}
-	}
 	sketchName := sketchDirPath.Base()
 	sketchMainFilePath := sketchDirPath.Join(sketchName + globals.MainFileValidExtension)
-	if !req.Overwrite {
-		if sketchMainFilePath.Exist() {
-			return nil, &arduino.CantCreateSketchError{Cause: errors.New(tr(".ino file already exists"))}
+
+	if templateDir != "" {
+		templateDirPath := paths.New(templateDir)
+		if err := templateDirPath.CopyDirTo(sketchDirPath); err != nil {
+			return nil, &arduino.CantCreateSketchError{Cause: err}
+		}
+
+		oldMainFilePath := sketchDirPath.Join(templateDirPath.Base() + globals.MainFileValidExtension)
+		if err := oldMainFilePath.Rename(sketchMainFilePath); err != nil {
+			return nil, &arduino.CantCreateSketchError{Cause: err}
+		}
+	} else {
+		if err := sketchDirPath.MkdirAll(); err != nil {
+			return nil, &arduino.CantCreateSketchError{Cause: err}
+		}
+		if !req.Overwrite {
+			if sketchMainFilePath.Exist() {
+				return nil, &arduino.CantCreateSketchError{Cause: errors.New(tr(".ino file already exists"))}
+			}
+		}
+		if err := sketchMainFilePath.WriteFile(emptySketch); err != nil {
+			return nil, &arduino.CantCreateSketchError{Cause: err}
 		}
-	}
-	if err := sketchMainFilePath.WriteFile(emptySketch); err != nil {
-		return nil, &arduino.CantCreateSketchError{Cause: err}
 	}
 
 	return &rpc.NewSketchResponse{MainFile: sketchMainFilePath.String()}, nil
diff --git a/configuration/defaults.go b/configuration/defaults.go
index be1a0088a62..bc9a8f5b331 100644
--- a/configuration/defaults.go
+++ b/configuration/defaults.go
@@ -69,4 +69,6 @@ func SetDefaults(settings *viper.Viper) {
 	settings.BindEnv("directories.Downloads", "ARDUINO_DOWNLOADS_DIR")
 	settings.BindEnv("directories.Data", "ARDUINO_DATA_DIR")
 	settings.BindEnv("sketch.always_export_binaries", "ARDUINO_SKETCH_ALWAYS_EXPORT_BINARIES")
+
+	settings.SetDefault("sketch.template", "")
 }
diff --git a/internal/cli/config/validate.go b/internal/cli/config/validate.go
index f494bfaac0b..c16e4a4be59 100644
--- a/internal/cli/config/validate.go
+++ b/internal/cli/config/validate.go
@@ -36,6 +36,7 @@ var validMap = map[string]reflect.Kind{
 	"logging.format":                reflect.String,
 	"logging.level":                 reflect.String,
 	"sketch.always_export_binaries": reflect.Bool,
+	"sketch.template":               reflect.String,
 	"metrics.addr":                  reflect.String,
 	"metrics.enabled":               reflect.Bool,
 	"network.proxy":                 reflect.String,