diff --git a/readme.md b/readme.md
index a3a19de..32c3c5f 100644
--- a/readme.md
+++ b/readme.md
@@ -33,6 +33,12 @@ sourceSets {
 }
 ```
 
+> [!IMPORTANT]
+> When processing templates, Pebble will trim newlines immediately after a template tag.
+> This might not be desired when using formats like YAML or Java properties files, so Blossom offers a way to disable this behaviour:
+>
+> Add a `trimNewlines = false` line to the template set block and stripping will be disabled for any templates in the set.
+
 Then place a file in the `src/main/resource-templates` folder:
 
 `build-vars.properties`:
diff --git a/src/main/java/net/kyori/blossom/GenerateTemplates.java b/src/main/java/net/kyori/blossom/GenerateTemplates.java
index 8473221..db2588a 100644
--- a/src/main/java/net/kyori/blossom/GenerateTemplates.java
+++ b/src/main/java/net/kyori/blossom/GenerateTemplates.java
@@ -142,6 +142,7 @@ void generate() throws IOException {
 
       // general properties
       spec.getHeader().set(this.getBaseSet().flatMap(TemplateSet::getHeader));
+      spec.getTrimNewlines().set(this.getBaseSet().flatMap(TemplateSet::getTrimNewlines));
       spec.getSourceDirectories().from(this.getSourceDirectories());
       spec.getIncludesDirectories().from(this.getIncludesDirectories());
       spec.getDestinationDirectory().set(this.getOutputDir());
diff --git a/src/main/java/net/kyori/blossom/TemplateSet.java b/src/main/java/net/kyori/blossom/TemplateSet.java
index 6dac752..85fd002 100644
--- a/src/main/java/net/kyori/blossom/TemplateSet.java
+++ b/src/main/java/net/kyori/blossom/TemplateSet.java
@@ -51,6 +51,18 @@ public interface TemplateSet extends Named {
   @Override
   @NotNull String getName();
 
+  /**
+   * Whether to strip trailing newlines from Pebble tokens that occur at the end of a line.
+   *
+   * 
The default is {@code true}, which can cause strange behaviour in some file formats
+   * that are whitespace-sensitive.
+   *
+   * @return whether to trim newlines
+   * @since 2.2.0
+   */
+  @Input
+  @NotNull Property getTrimNewlines();
+
   /**
    * A collection of data files in YAML format.
    *
diff --git a/src/main/java/net/kyori/blossom/internal/ResourceTemplateSetImpl.java b/src/main/java/net/kyori/blossom/internal/ResourceTemplateSetImpl.java
index 793695a..17f331d 100644
--- a/src/main/java/net/kyori/blossom/internal/ResourceTemplateSetImpl.java
+++ b/src/main/java/net/kyori/blossom/internal/ResourceTemplateSetImpl.java
@@ -24,6 +24,7 @@
 import net.kyori.blossom.GenerateTemplates;
 import net.kyori.blossom.ResourceTemplateSet;
 import org.gradle.api.file.Directory;
+import org.gradle.api.model.ObjectFactory;
 import org.gradle.api.tasks.SourceSet;
 import org.gradle.api.tasks.TaskProvider;
 
@@ -32,8 +33,8 @@
  */
 public abstract class ResourceTemplateSetImpl extends TemplateSetImpl implements ResourceTemplateSet {
   @Inject
-  public ResourceTemplateSetImpl(final String name) {
-    super(name);
+  public ResourceTemplateSetImpl(final ObjectFactory objects, final String name) {
+    super(objects, name);
   }
 
   @Override
diff --git a/src/main/java/net/kyori/blossom/internal/SourceTemplateSetImpl.java b/src/main/java/net/kyori/blossom/internal/SourceTemplateSetImpl.java
index aa15d32..a128726 100644
--- a/src/main/java/net/kyori/blossom/internal/SourceTemplateSetImpl.java
+++ b/src/main/java/net/kyori/blossom/internal/SourceTemplateSetImpl.java
@@ -27,6 +27,7 @@
 import org.gradle.api.GradleException;
 import org.gradle.api.file.Directory;
 import org.gradle.api.file.SourceDirectorySet;
+import org.gradle.api.model.ObjectFactory;
 import org.gradle.api.tasks.SourceSet;
 import org.gradle.api.tasks.TaskProvider;
 import org.jetbrains.annotations.NotNull;
@@ -36,8 +37,8 @@ public abstract class SourceTemplateSetImpl extends TemplateSetImpl implements S
   private transient TaskProvider pendingGenerateTask;
 
   @Inject
-  public SourceTemplateSetImpl(final String name) {
-    super(name);
+  public SourceTemplateSetImpl(final ObjectFactory objects, final String name) {
+    super(objects, name);
   }
 
   @Override
diff --git a/src/main/java/net/kyori/blossom/internal/TemplateSetImpl.java b/src/main/java/net/kyori/blossom/internal/TemplateSetImpl.java
index 82e864e..1fb04c2 100644
--- a/src/main/java/net/kyori/blossom/internal/TemplateSetImpl.java
+++ b/src/main/java/net/kyori/blossom/internal/TemplateSetImpl.java
@@ -32,8 +32,6 @@
 
 /**
  * A directory of templates.
- *
- * While it's perfectly possible to 
  */
 public abstract class TemplateSetImpl implements TemplateSetInternal {
   // shared
@@ -41,24 +39,23 @@ public abstract class TemplateSetImpl implements TemplateSetInternal {
   private final MapProperty properties;
   private final NamedDomainObjectContainer variants;
   private final Property header;
+  private final Property trimNewlines;
   private transient final SourceDirectorySet includes;
   private transient final SourceDirectorySet templates;
   private final String name;
 
   @Inject
-  public TemplateSetImpl(final String name) {
+  public TemplateSetImpl(final ObjectFactory objects, final String name) {
     this.name = name;
-    this.dataFiles = this.getObjects().fileCollection();
-    this.properties = this.getObjects().mapProperty(String.class, Object.class);
-    this.variants = this.getObjects().domainObjectContainer(Variant.class, n -> this.getObjects().newInstance(VariantImpl.class, n));
-    this.header = this.getObjects().property(String.class);
-    this.includes = this.getObjects().sourceDirectorySet(name + "-template-includes", name + " template includes");
-    this.templates = this.getObjects().sourceDirectorySet(name + "-templates", name + " templates");
+    this.dataFiles = objects.fileCollection();
+    this.properties = objects.mapProperty(String.class, Object.class);
+    this.variants = objects.domainObjectContainer(Variant.class, n -> objects.newInstance(VariantImpl.class, n));
+    this.header = objects.property(String.class);
+    this.trimNewlines = objects.property(Boolean.class).convention(true);
+    this.includes = objects.sourceDirectorySet(name + "-template-includes", name + " template includes");
+    this.templates = objects.sourceDirectorySet(name + "-templates", name + " templates");
   }
 
-  @Inject
-  protected abstract ObjectFactory getObjects();
-
   @Override
   public @NotNull String getName() {
     return this.name;
@@ -81,6 +78,11 @@ public TemplateSetImpl(final String name) {
     return this.header;
   }
 
+  @Override
+  public @NotNull Property getTrimNewlines() {
+    return this.trimNewlines;
+  }
+
   @Override
   public @NotNull SourceDirectorySet getIncludes() {
     return this.includes;
diff --git a/src/main/java/net/kyori/blossom/internal/worker/GenerateWorker.java b/src/main/java/net/kyori/blossom/internal/worker/GenerateWorker.java
index a853cf9..ec542ee 100644
--- a/src/main/java/net/kyori/blossom/internal/worker/GenerateWorker.java
+++ b/src/main/java/net/kyori/blossom/internal/worker/GenerateWorker.java
@@ -53,6 +53,8 @@ public interface Params extends WorkParameters {
 
     Property getHeader();
 
+    Property getTrimNewlines();
+
     ConfigurableFileCollection getSourceDirectories();
 
     ConfigurableFileCollection getIncludesDirectories();
@@ -92,7 +94,8 @@ public void execute() {
         toPaths(params.getIncludesDirectories()),
         toPaths(params.getSourceDirectories()),
         params.getDestinationDirectory().get().getAsFile().toPath(),
-        params.getHeader().getOrNull()
+        params.getHeader().getOrNull(),
+        params.getTrimNewlines().getOrElse(false)
       );
     } catch (final IOException ex) {
       throw new GradleException("Failed to process templates:" + ex.getMessage(), ex);
diff --git a/src/test/java/net/kyori/blossom/ResourceTemplateSetTest.java b/src/test/java/net/kyori/blossom/ResourceTemplateSetTest.java
index 3601968..b6eebb8 100644
--- a/src/test/java/net/kyori/blossom/ResourceTemplateSetTest.java
+++ b/src/test/java/net/kyori/blossom/ResourceTemplateSetTest.java
@@ -65,4 +65,19 @@ void testResourceSingleSet(final TestContext ctx) throws IOException {
     }
   }
 
+  @BlossomFunctionalTest
+  void testTrimTrailing(final TestContext ctx) throws IOException {
+    SettingsFactory.writeSettings(ctx, "trimTrailing");
+    ctx.copyInput("build.gradle");
+    ctx.copyInput("meta.yaml.peb", "src/main/resource-templates/meta.yaml.peb");
+
+    final BuildResult result = ctx.build("generateTemplates"); // build templates
+    assertEquals(TaskOutcome.SUCCESS, result.task(":generateResourceTemplates").getOutcome());
+
+    ctx.assertOutputEquals(
+      "meta.yaml",
+      "build/generated/resources/blossom/main/resource/meta.yaml"
+    );
+  }
+
 }
diff --git a/src/test/java/net/kyori/blossom/test/BlossomFunctionalTest.java b/src/test/java/net/kyori/blossom/test/BlossomFunctionalTest.java
index 355a81b..5419127 100644
--- a/src/test/java/net/kyori/blossom/test/BlossomFunctionalTest.java
+++ b/src/test/java/net/kyori/blossom/test/BlossomFunctionalTest.java
@@ -34,7 +34,7 @@
 @GradleParameters({"--warning-mode", "fail", "--stacktrace"}) // parameters for all variants
 @TestVariant(gradleVersion = "7.6.4", maximumRuntimeVersion = 20)
 @TestVariant(gradleVersion = "8.9", extraArguments = {"--configuration-cache"}) // last version with non-deprecated support for runtimes <17
-@TestVariant(gradleVersion = "8.10", extraArguments = {"--configuration-cache"}, minimumRuntimeVersion = 17)
+@TestVariant(gradleVersion = "8.14.2", extraArguments = {"--configuration-cache"}, minimumRuntimeVersion = 17)
 @TestVariantResource(value = "/injected-gradle-versions", optional = true, minimumRuntimeVersion = 17) // newer Gradle versions deprecate running on JDK <17, and this is only for RC's
 @Documented
 @Retention(RetentionPolicy.RUNTIME)
diff --git a/src/test/resources/net/kyori/blossom/trimTrailing/in/build.gradle b/src/test/resources/net/kyori/blossom/trimTrailing/in/build.gradle
new file mode 100644
index 0000000..2f3afe9
--- /dev/null
+++ b/src/test/resources/net/kyori/blossom/trimTrailing/in/build.gradle
@@ -0,0 +1,18 @@
+plugins {
+  id 'java'
+  id 'net.kyori.blossom'
+}
+
+version = "1.0.3"
+
+sourceSets {
+  main {
+    blossom {
+      resources {
+        trimNewlines = false
+        property('name', project.name)
+        property('version', project.version.toString())
+      }
+    }
+  }
+}
diff --git a/src/test/resources/net/kyori/blossom/trimTrailing/in/meta.yaml.peb b/src/test/resources/net/kyori/blossom/trimTrailing/in/meta.yaml.peb
new file mode 100644
index 0000000..774a032
--- /dev/null
+++ b/src/test/resources/net/kyori/blossom/trimTrailing/in/meta.yaml.peb
@@ -0,0 +1,3 @@
+info:
+  name: {{ name }}
+  version: {{ version }}
diff --git a/src/test/resources/net/kyori/blossom/trimTrailing/out/meta.yaml b/src/test/resources/net/kyori/blossom/trimTrailing/out/meta.yaml
new file mode 100644
index 0000000..3610b37
--- /dev/null
+++ b/src/test/resources/net/kyori/blossom/trimTrailing/out/meta.yaml
@@ -0,0 +1,3 @@
+info:
+  name: trimTrailing
+  version: 1.0.3
diff --git a/src/workerClasspath/java/net/kyori/blossom/internal/worker/GenerateWorkerInvokerImpl.java b/src/workerClasspath/java/net/kyori/blossom/internal/worker/GenerateWorkerInvokerImpl.java
index 94548f5..148fb45 100644
--- a/src/workerClasspath/java/net/kyori/blossom/internal/worker/GenerateWorkerInvokerImpl.java
+++ b/src/workerClasspath/java/net/kyori/blossom/internal/worker/GenerateWorkerInvokerImpl.java
@@ -53,13 +53,15 @@ public void generate(
     final Set includePaths,
     final Set sourcePaths,
     final Path outputDirectory,
-    final @Nullable String header
+    final @Nullable String header,
+    final boolean trimNewlines
   ) throws IOException {
     // By default, resolves FS paths
     // todo: restrict inputs to inputs and includes
     final Loader> loader = this.makeLoader(sourcePaths, includePaths);
     final PebbleEngine engine = new PebbleEngine.Builder()
       .autoEscaping(false) // no html escaping
+      .newLineTrimming(trimNewlines) // can mess with line breaks in yaml files
       .defaultLocale(Locale.ROOT)
       .loader(loader)
       // .cacheActive(false) // xX: overlap between file names and template names causes issues
diff --git a/src/workerShared/java/net/kyori/blossom/internal/worker/GenerateWorkerInvoker.java b/src/workerShared/java/net/kyori/blossom/internal/worker/GenerateWorkerInvoker.java
index 925073a..16b7448 100644
--- a/src/workerShared/java/net/kyori/blossom/internal/worker/GenerateWorkerInvoker.java
+++ b/src/workerShared/java/net/kyori/blossom/internal/worker/GenerateWorkerInvoker.java
@@ -31,6 +31,7 @@ void generate(
     final Set includePaths,
     final Set sourcePaths,
     final Path outputDir,
-    final /* @Nullable */ String header
+    final /* @Nullable */ String header,
+    final boolean trimNewlines
   ) throws IOException;
 }