diff --git a/lib/modules/datasource/conan/index.ts b/lib/modules/datasource/conan/index.ts
index b944a97dc633be199c57442208633a4b6112490f..0319d16719bc9491d552f64578f0773d0d87ed41 100644
--- a/lib/modules/datasource/conan/index.ts
+++ b/lib/modules/datasource/conan/index.ts
@@ -53,10 +53,12 @@ export class ConanDatasource extends Datasource {
       return null;
     }
     const url = `https://api.github.com/repos/conan-io/conan-center-index/contents/recipes/${conanName}/config.yml`;
-    const res = await this.githubHttp.get(url, {
-      headers: { accept: 'application/vnd.github.v3.raw' },
-    });
-    return ConanCenterReleases.parse(res.body);
+    const { body: result } = await this.githubHttp.getYaml(
+      url,
+      { headers: { accept: 'application/vnd.github.v3.raw' } },
+      ConanCenterReleases,
+    );
+    return result;
   }
 
   @cache({
diff --git a/lib/modules/datasource/conan/schema.ts b/lib/modules/datasource/conan/schema.ts
index 2ca42c64f9fd97e7dccfcd11048e3f6fae2a2584..c13ec77b8c845fbb6594c7341312954c0498b2f3 100644
--- a/lib/modules/datasource/conan/schema.ts
+++ b/lib/modules/datasource/conan/schema.ts
@@ -1,13 +1,12 @@
 import { z } from 'zod';
-import { LooseArray, Yaml } from '../../../util/schema-utils';
+import { LooseArray } from '../../../util/schema-utils';
 import type { ReleaseResult } from '../types';
 import { conanDatasourceRegex } from './common';
 
-export const ConanCenterReleases = Yaml.pipe(
-  z.object({
+export const ConanCenterReleases = z
+  .object({
     versions: z.record(z.string(), z.unknown()),
-  }),
-)
+  })
   .transform(
     ({ versions }): ReleaseResult => ({
       releases: Object.keys(versions).map((version) => ({ version })),
diff --git a/lib/modules/datasource/glasskube-packages/index.ts b/lib/modules/datasource/glasskube-packages/index.ts
index a289e79682dfa5aafef315655b2c7387d92a693f..f36daeb381bf146c9b14389fcb509aa9a993bef3 100644
--- a/lib/modules/datasource/glasskube-packages/index.ts
+++ b/lib/modules/datasource/glasskube-packages/index.ts
@@ -3,11 +3,7 @@ import { joinUrlParts } from '../../../util/url';
 import * as glasskubeVersioning from '../../versioning/glasskube';
 import { Datasource } from '../datasource';
 import type { GetReleasesConfig, ReleaseResult } from '../types';
-import type { GlasskubePackageVersions } from './schema';
-import {
-  GlasskubePackageManifestYaml,
-  GlasskubePackageVersionsYaml,
-} from './schema';
+import { GlasskubePackageManifest, GlasskubePackageVersions } from './schema';
 
 export class GlasskubePackagesDatasource extends Datasource {
   static readonly id = 'glasskube-packages';
@@ -33,16 +29,17 @@ export class GlasskubePackagesDatasource extends Datasource {
     packageName,
     registryUrl,
   }: GetReleasesConfig): Promise<ReleaseResult | null> {
-    let versions: GlasskubePackageVersions;
     const result: ReleaseResult = { releases: [] };
 
-    try {
-      const response = await this.http.get(
+    const { val: versions, err: versionsErr } = await this.http
+      .getYamlSafe(
         joinUrlParts(registryUrl!, packageName, 'versions.yaml'),
-      );
-      versions = GlasskubePackageVersionsYaml.parse(response.body);
-    } catch (err) {
-      this.handleGenericErrors(err);
+        GlasskubePackageVersions,
+      )
+      .unwrap();
+
+    if (versionsErr) {
+      this.handleGenericErrors(versionsErr);
     }
 
     result.releases = versions.versions.map((it) => ({
@@ -50,25 +47,28 @@ export class GlasskubePackagesDatasource extends Datasource {
     }));
     result.tags = { latest: versions.latestVersion };
 
-    try {
-      const response = await this.http.get(
+    const { val: latestManifest, err: latestManifestErr } = await this.http
+      .getYamlSafe(
         joinUrlParts(
           registryUrl!,
           packageName,
           versions.latestVersion,
           'package.yaml',
         ),
-      );
-      const latestManifest = GlasskubePackageManifestYaml.parse(response.body);
-      for (const ref of latestManifest?.references ?? []) {
-        if (ref.label.toLowerCase() === 'github') {
-          result.sourceUrl = ref.url;
-        } else if (ref.label.toLowerCase() === 'website') {
-          result.homepage = ref.url;
-        }
+        GlasskubePackageManifest,
+      )
+      .unwrap();
+
+    if (latestManifestErr) {
+      this.handleGenericErrors(latestManifestErr);
+    }
+
+    for (const ref of latestManifest?.references ?? []) {
+      if (ref.label.toLowerCase() === 'github') {
+        result.sourceUrl = ref.url;
+      } else if (ref.label.toLowerCase() === 'website') {
+        result.homepage = ref.url;
       }
-    } catch (err) {
-      this.handleGenericErrors(err);
     }
 
     return result;
diff --git a/lib/modules/datasource/glasskube-packages/schema.ts b/lib/modules/datasource/glasskube-packages/schema.ts
index 5e299304b4104fe203540b04c2117d61af03e119..e459912c14bfb43c629527bed63955222cb100e9 100644
--- a/lib/modules/datasource/glasskube-packages/schema.ts
+++ b/lib/modules/datasource/glasskube-packages/schema.ts
@@ -1,12 +1,11 @@
 import { z } from 'zod';
-import { Yaml } from '../../../util/schema-utils';
 
-const GlasskubePackageVersions = z.object({
+export const GlasskubePackageVersions = z.object({
   latestVersion: z.string(),
   versions: z.array(z.object({ version: z.string() })),
 });
 
-const GlasskubePackageManifest = z.object({
+export const GlasskubePackageManifest = z.object({
   references: z.optional(
     z.array(
       z.object({
@@ -16,8 +15,3 @@ const GlasskubePackageManifest = z.object({
     ),
   ),
 });
-
-export const GlasskubePackageVersionsYaml = Yaml.pipe(GlasskubePackageVersions);
-export const GlasskubePackageManifestYaml = Yaml.pipe(GlasskubePackageManifest);
-
-export type GlasskubePackageVersions = z.infer<typeof GlasskubePackageVersions>;
diff --git a/lib/util/http/index.spec.ts b/lib/util/http/index.spec.ts
index 3c1b37bb755f65259e03d37f2dd831579c8d0379..8eda7e35e7e140c71d323580ab9fc9f8c27fb66b 100644
--- a/lib/util/http/index.spec.ts
+++ b/lib/util/http/index.spec.ts
@@ -342,6 +342,147 @@ describe('util/http/index', () => {
       memCache.reset();
     });
 
+    describe('getPlain', () => {
+      it('gets plain text with correct headers', async () => {
+        httpMock.scope(baseUrl).get('/').reply(200, 'plain text response', {
+          'content-type': 'text/plain',
+        });
+
+        const res = await http.getPlain('http://renovate.com');
+        expect(res.body).toBe('plain text response');
+        expect(res.headers['content-type']).toBe('text/plain');
+      });
+
+      it('works with custom options', async () => {
+        httpMock
+          .scope(baseUrl)
+          .get('/')
+          .matchHeader('custom', 'header')
+          .reply(200, 'plain text response');
+
+        const res = await http.getPlain('http://renovate.com', {
+          headers: { custom: 'header' },
+        });
+        expect(res.body).toBe('plain text response');
+      });
+    });
+
+    describe('getYaml', () => {
+      it('parses yaml response without schema', async () => {
+        httpMock.scope(baseUrl).get('/').reply(200, 'x: 2\ny: 2');
+
+        const res = await http.getYaml('http://renovate.com');
+        expect(res.body).toEqual({ x: 2, y: 2 });
+      });
+
+      it('parses yaml with schema validation', async () => {
+        httpMock.scope(baseUrl).get('/').reply(200, 'x: 2\ny: 2');
+
+        const res = await http.getYaml('http://renovate.com', SomeSchema);
+        expect(res.body).toBe('2 + 2 = 4');
+      });
+
+      it('parses yaml with options and schema', async () => {
+        httpMock
+          .scope(baseUrl)
+          .get('/')
+          .matchHeader('custom', 'header')
+          .reply(200, 'x: 2\ny: 2');
+
+        const res = await http.getYaml(
+          'http://renovate.com',
+          { headers: { custom: 'header' } },
+          SomeSchema,
+        );
+        expect(res.body).toBe('2 + 2 = 4');
+      });
+
+      it('throws on invalid yaml', async () => {
+        httpMock.scope(baseUrl).get('/').reply(200, '!@#$%^');
+
+        await expect(http.getYaml('http://renovate.com')).rejects.toThrow();
+      });
+
+      it('throws on schema validation failure', async () => {
+        httpMock.scope(baseUrl).get('/').reply(200, 'foo: bar');
+
+        await expect(
+          http.getYaml('http://renovate.com', SomeSchema),
+        ).rejects.toThrow(z.ZodError);
+      });
+    });
+
+    describe('getYamlSafe', () => {
+      it('returns successful result with schema validation', async () => {
+        httpMock.scope('http://example.com').get('/').reply(200, 'x: 2\ny: 2');
+
+        const { val, err } = await http
+          .getYamlSafe('http://example.com', SomeSchema)
+          .unwrap();
+
+        expect(val).toBe('2 + 2 = 4');
+        expect(err).toBeUndefined();
+      });
+
+      it('returns schema error result', async () => {
+        httpMock
+          .scope('http://example.com')
+          .get('/')
+          .reply(200, 'x: "2"\ny: "2"');
+
+        const { val, err } = await http
+          .getYamlSafe('http://example.com', SomeSchema)
+          .unwrap();
+
+        expect(val).toBeUndefined();
+        expect(err).toBeInstanceOf(ZodError);
+      });
+
+      it('returns error result for invalid yaml', async () => {
+        httpMock.scope('http://example.com').get('/').reply(200, '!@#$%^');
+
+        const { val, err } = await http
+          .getYamlSafe('http://example.com', SomeSchema)
+          .unwrap();
+
+        expect(val).toBeUndefined();
+        expect(err).toBeDefined();
+      });
+
+      it('returns error result for network errors', async () => {
+        httpMock
+          .scope('http://example.com')
+          .get('/')
+          .replyWithError('network error');
+
+        const { val, err } = await http
+          .getYamlSafe('http://example.com', SomeSchema)
+          .unwrap();
+
+        expect(val).toBeUndefined();
+        expect(err).toBeInstanceOf(HttpError);
+      });
+
+      it('works with options and schema', async () => {
+        httpMock
+          .scope('http://example.com')
+          .get('/')
+          .matchHeader('custom', 'header')
+          .reply(200, 'x: 2\ny: 2');
+
+        const { val, err } = await http
+          .getYamlSafe(
+            'http://example.com',
+            { headers: { custom: 'header' } },
+            SomeSchema,
+          )
+          .unwrap();
+
+        expect(val).toBe('2 + 2 = 4');
+        expect(err).toBeUndefined();
+      });
+    });
+
     describe('getJson', () => {
       it('uses schema for response body', async () => {
         httpMock
diff --git a/lib/util/http/index.ts b/lib/util/http/index.ts
index 34ce5a691ecfa4e1018b12c26f10d89c6bce03bf..b0968a15bb950f45f0c87a314fbd4d55fb1ec1f2 100644
--- a/lib/util/http/index.ts
+++ b/lib/util/http/index.ts
@@ -15,6 +15,7 @@ import { hash } from '../hash';
 import { type AsyncResult, Result } from '../result';
 import { type HttpRequestStatsDataPoint, HttpStats } from '../stats';
 import { resolveBaseUrl } from '../url';
+import { parseSingleYaml } from '../yaml';
 import { applyAuthorization, removeAuthorization } from './auth';
 import { hooks } from './hooks';
 import { applyHostRule, findMatchingRule } from './host-rules';
@@ -38,7 +39,7 @@ export { RequestError as HttpError };
 export class EmptyResultError extends Error {}
 export type SafeJsonError = RequestError | ZodError | EmptyResultError;
 
-type JsonArgs<
+type HttpFnArgs<
   Opts extends HttpOptions,
   ResT = unknown,
   Schema extends ZodType<ResT> = ZodType<ResT>,
@@ -272,7 +273,7 @@ export class Http<Opts extends HttpOptions = HttpOptions> {
 
   private async requestJson<ResT = unknown>(
     method: InternalHttpOptions['method'],
-    { url, httpOptions: requestOptions, schema }: JsonArgs<Opts, ResT>,
+    { url, httpOptions: requestOptions, schema }: HttpFnArgs<Opts, ResT>,
   ): Promise<HttpResponse<ResT>> {
     const { body, ...httpOptions } = { ...requestOptions };
     const opts: InternalHttpOptions = {
@@ -302,8 +303,8 @@ export class Http<Opts extends HttpOptions = HttpOptions> {
     arg1: string,
     arg2: Opts | ZodType<ResT> | undefined,
     arg3: ZodType<ResT> | undefined,
-  ): JsonArgs<Opts, ResT> {
-    const res: JsonArgs<Opts, ResT> = { url: arg1 };
+  ): HttpFnArgs<Opts, ResT> {
+    const res: HttpFnArgs<Opts, ResT> = { url: arg1 };
 
     if (arg2 instanceof ZodType) {
       res.schema = arg2;
@@ -328,6 +329,81 @@ export class Http<Opts extends HttpOptions = HttpOptions> {
     });
   }
 
+  async getYaml<ResT>(url: string, options?: Opts): Promise<HttpResponse<ResT>>;
+  async getYaml<ResT, Schema extends ZodType<ResT> = ZodType<ResT>>(
+    url: string,
+    schema: Schema,
+  ): Promise<HttpResponse<Infer<Schema>>>;
+  async getYaml<ResT, Schema extends ZodType<ResT> = ZodType<ResT>>(
+    url: string,
+    options: Opts,
+    schema: Schema,
+  ): Promise<HttpResponse<Infer<Schema>>>;
+  async getYaml<ResT = unknown, Schema extends ZodType<ResT> = ZodType<ResT>>(
+    arg1: string,
+    arg2?: Opts | Schema,
+    arg3?: Schema,
+  ): Promise<HttpResponse<ResT>> {
+    const { url, httpOptions, schema } = this.resolveArgs<ResT>(
+      arg1,
+      arg2,
+      arg3,
+    );
+    const opts: InternalHttpOptions = {
+      ...httpOptions,
+      method: 'get',
+    };
+
+    const res = await this.get(url, opts);
+    if (!schema) {
+      const body = parseSingleYaml<ResT>(res.body);
+      return { ...res, body };
+    }
+
+    const body = await schema.parseAsync(parseSingleYaml(res.body));
+    return { ...res, body };
+  }
+
+  getYamlSafe<
+    ResT extends NonNullable<unknown>,
+    Schema extends ZodType<ResT> = ZodType<ResT>,
+  >(url: string, schema: Schema): AsyncResult<Infer<Schema>, SafeJsonError>;
+  getYamlSafe<
+    ResT extends NonNullable<unknown>,
+    Schema extends ZodType<ResT> = ZodType<ResT>,
+  >(
+    url: string,
+    options: Opts,
+    schema: Schema,
+  ): AsyncResult<Infer<Schema>, SafeJsonError>;
+  getYamlSafe<
+    ResT extends NonNullable<unknown>,
+    Schema extends ZodType<ResT> = ZodType<ResT>,
+  >(
+    arg1: string,
+    arg2: Opts | Schema,
+    arg3?: Schema,
+  ): AsyncResult<ResT, SafeJsonError> {
+    const url = arg1;
+    let schema: Schema;
+    let httpOptions: Opts | undefined;
+    if (arg3) {
+      schema = arg3;
+      httpOptions = arg2 as Opts;
+    } else {
+      schema = arg2 as Schema;
+    }
+
+    let res: AsyncResult<HttpResponse<ResT>, SafeJsonError>;
+    if (httpOptions) {
+      res = Result.wrap(this.getYaml<ResT>(url, httpOptions, schema));
+    } else {
+      res = Result.wrap(this.getYaml<ResT>(url, schema));
+    }
+
+    return res.transform((response) => Result.ok(response.body));
+  }
+
   getJson<ResT>(url: string, options?: Opts): Promise<HttpResponse<ResT>>;
   getJson<ResT, Schema extends ZodType<ResT> = ZodType<ResT>>(
     url: string,