diff --git a/lib/modules/manager/api.ts b/lib/modules/manager/api.ts
index db30f841bb4edc0149602a0eadc28432238779bf..664444223f467c3b1912ef3edd84054eec88c39e 100644
--- a/lib/modules/manager/api.ts
+++ b/lib/modules/manager/api.ts
@@ -37,6 +37,7 @@ import * as gitSubmodules from './git-submodules';
 import * as githubActions from './github-actions';
 import * as gitlabci from './gitlabci';
 import * as gitlabciInclude from './gitlabci-include';
+import * as glasskube from './glasskube';
 import * as gleam from './gleam';
 import * as gomod from './gomod';
 import * as gradle from './gradle';
@@ -138,6 +139,7 @@ api.set('git-submodules', gitSubmodules);
 api.set('github-actions', githubActions);
 api.set('gitlabci', gitlabci);
 api.set('gitlabci-include', gitlabciInclude);
+api.set('glasskube', glasskube);
 api.set('gleam', gleam);
 api.set('gomod', gomod);
 api.set('gradle', gradle);
diff --git a/lib/modules/manager/glasskube/__fixtures__/package-and-repo.yaml b/lib/modules/manager/glasskube/__fixtures__/package-and-repo.yaml
new file mode 100644
index 0000000000000000000000000000000000000000..5b32bdf4765136725204b9914c458114871c922e
--- /dev/null
+++ b/lib/modules/manager/glasskube/__fixtures__/package-and-repo.yaml
@@ -0,0 +1,27 @@
+apiVersion: packages.glasskube.dev/v1alpha1
+kind: PackageRepository
+metadata:
+  annotations:
+    packages.glasskube.dev/default-repository: "true"
+  name: glasskube
+spec:
+  url: https://packages.dl.glasskube.dev/packages
+
+---
+apiVersion: packages.glasskube.dev/v1alpha1
+kind: PackageRepository
+metadata:
+  name: local
+spec:
+  url: http://localhost:9090/packages
+
+---
+apiVersion: packages.glasskube.dev/v1alpha1
+kind: ClusterPackage
+metadata:
+  name: argo-cd
+spec:
+  packageInfo:
+    name: argo-cd
+    repositoryName: glasskube
+    version: v2.11.7+1
diff --git a/lib/modules/manager/glasskube/extract.spec.ts b/lib/modules/manager/glasskube/extract.spec.ts
new file mode 100644
index 0000000000000000000000000000000000000000..7d8455de1acbe3b3557b5936b2f060d48c0173d8
--- /dev/null
+++ b/lib/modules/manager/glasskube/extract.spec.ts
@@ -0,0 +1,161 @@
+import { codeBlock } from 'common-tags';
+import { Fixtures } from '../../../../test/fixtures';
+import { fs } from '../../../../test/util';
+import { GlobalConfig } from '../../../config/global';
+import type { RepoGlobalConfig } from '../../../config/types';
+import { GlasskubePackagesDatasource } from '../../datasource/glasskube-packages';
+import type { ExtractConfig } from '../types';
+import { extractAllPackageFiles, extractPackageFile } from './extract';
+
+const config: ExtractConfig = {};
+const adminConfig: RepoGlobalConfig = { localDir: '' };
+
+const packageWithRepoName = codeBlock`
+apiVersion: packages.glasskube.dev/v1alpha1
+kind: ClusterPackage
+metadata:
+  name: argo-cd
+spec:
+  packageInfo:
+    name: argo-cd
+    repositoryName: glasskube
+    version: v2.11.7+1
+`;
+const repository = codeBlock`
+apiVersion: packages.glasskube.dev/v1alpha1
+kind: PackageRepository
+metadata:
+  annotations:
+    packages.glasskube.dev/default-repository: "true"
+  name: glasskube
+spec:
+  url: https://packages.dl.glasskube.dev/packages
+`;
+
+jest.mock('../../../util/fs');
+
+describe('modules/manager/glasskube/extract', () => {
+  beforeEach(() => {
+    GlobalConfig.set(adminConfig);
+  });
+
+  describe('extractPackageFile()', () => {
+    it('should extract version and registryUrl', () => {
+      const deps = extractPackageFile(
+        Fixtures.get('package-and-repo.yaml'),
+        'package-and-repo.yaml',
+      );
+      expect(deps).toEqual({
+        deps: [
+          {
+            depName: 'argo-cd',
+            currentValue: 'v2.11.7+1',
+            datasource: GlasskubePackagesDatasource.id,
+            registryUrls: ['https://packages.dl.glasskube.dev/packages'],
+          },
+        ],
+      });
+    });
+  });
+
+  describe('extractAllPackageFiles()', () => {
+    it('should return null for empty packageFiles', async () => {
+      const deps = await extractAllPackageFiles(config, []);
+      expect(deps).toBeNull();
+    });
+
+    it('should skip package with non-existing repo', async () => {
+      fs.readLocalFile.mockResolvedValueOnce(packageWithRepoName);
+      const deps = await extractAllPackageFiles(config, ['package.yaml']);
+      expect(deps).toEqual([
+        {
+          packageFile: 'package.yaml',
+          deps: [
+            {
+              depName: 'argo-cd',
+              currentValue: 'v2.11.7+1',
+              datasource: GlasskubePackagesDatasource.id,
+              skipReason: 'unknown-registry',
+            },
+          ],
+        },
+      ]);
+    });
+
+    it('should extract registryUrl from repo in other file', async () => {
+      fs.readLocalFile.mockResolvedValueOnce(packageWithRepoName);
+      fs.readLocalFile.mockResolvedValueOnce(repository);
+      const deps = await extractAllPackageFiles(config, [
+        'package.yaml',
+        'repo.yaml',
+      ]);
+      expect(deps).toEqual([
+        {
+          packageFile: 'package.yaml',
+          deps: [
+            {
+              depName: 'argo-cd',
+              currentValue: 'v2.11.7+1',
+              datasource: GlasskubePackagesDatasource.id,
+              registryUrls: ['https://packages.dl.glasskube.dev/packages'],
+            },
+          ],
+        },
+      ]);
+    });
+
+    it('should extract registryUrl from default repo in other file', async () => {
+      fs.readLocalFile.mockResolvedValueOnce(codeBlock`
+        apiVersion: packages.glasskube.dev/v1alpha1
+        kind: ClusterPackage
+        metadata:
+          name: argo-cd
+        spec:
+          packageInfo:
+            name: argo-cd
+            version: v2.11.7+1
+            repositoryName: ""
+      `);
+      fs.readLocalFile.mockResolvedValueOnce(codeBlock`
+        apiVersion: packages.glasskube.dev/v1alpha1
+        kind: ClusterPackage
+        metadata:
+          name: argo-cd
+        spec:
+          packageInfo:
+            name: argo-cd
+            version: v2.11.7+1
+      `);
+      fs.readLocalFile.mockResolvedValueOnce(repository);
+      const deps = await extractAllPackageFiles(config, [
+        'package-with-empty-reponame.yaml',
+        'package-with-missing-reponame.yaml',
+        'repo.yaml',
+      ]);
+      expect(deps).toEqual([
+        {
+          packageFile: 'package-with-empty-reponame.yaml',
+          deps: [
+            {
+              depName: 'argo-cd',
+              currentValue: 'v2.11.7+1',
+              datasource: GlasskubePackagesDatasource.id,
+              registryUrls: ['https://packages.dl.glasskube.dev/packages'],
+            },
+          ],
+        },
+        {
+          packageFile: 'package-with-missing-reponame.yaml',
+          deps: [
+            {
+              depName: 'argo-cd',
+              currentValue: 'v2.11.7+1',
+              datasource: GlasskubePackagesDatasource.id,
+              registryUrls: ['https://packages.dl.glasskube.dev/packages'],
+            },
+          ],
+        },
+      ]);
+    });
+  });
+});
diff --git a/lib/modules/manager/glasskube/extract.ts b/lib/modules/manager/glasskube/extract.ts
new file mode 100644
index 0000000000000000000000000000000000000000..38f89ceaccfb6ecb3a085a2ad452ab2327de910a
--- /dev/null
+++ b/lib/modules/manager/glasskube/extract.ts
@@ -0,0 +1,126 @@
+import is from '@sindresorhus/is';
+import { readLocalFile } from '../../../util/fs';
+import { parseYaml } from '../../../util/yaml';
+import { GlasskubePackagesDatasource } from '../../datasource/glasskube-packages';
+import type {
+  ExtractConfig,
+  PackageDependency,
+  PackageFile,
+  PackageFileContent,
+} from '../types';
+import {
+  GlasskubeResource,
+  type Package,
+  type PackageRepository,
+} from './schema';
+import type { GlasskubeResources } from './types';
+
+function parseResources(
+  content: string,
+  packageFile: string,
+): GlasskubeResources {
+  const resources: GlasskubeResource[] = parseYaml(content, {
+    json: true,
+    customSchema: GlasskubeResource,
+    failureBehaviour: 'filter',
+  });
+
+  const packages: Package[] = [];
+  const repositories: PackageRepository[] = [];
+
+  for (const resource of resources) {
+    if (resource.kind === 'ClusterPackage' || resource.kind === 'Package') {
+      packages.push(resource);
+    } else if (resource.kind === 'PackageRepository') {
+      repositories.push(resource);
+    }
+  }
+
+  return { packageFile, repositories, packages };
+}
+
+function resolvePackageDependencies(
+  packages: Package[],
+  repositories: PackageRepository[],
+): PackageDependency[] {
+  const deps: PackageDependency[] = [];
+  for (const pkg of packages) {
+    const dep: PackageDependency = {
+      depName: pkg.spec.packageInfo.name,
+      currentValue: pkg.spec.packageInfo.version,
+      datasource: GlasskubePackagesDatasource.id,
+    };
+
+    const repository = findRepository(
+      pkg.spec.packageInfo.repositoryName ?? null,
+      repositories,
+    );
+
+    if (repository === null) {
+      dep.skipReason = 'unknown-registry';
+    } else {
+      dep.registryUrls = [repository.spec.url];
+    }
+
+    deps.push(dep);
+  }
+  return deps;
+}
+
+function findRepository(
+  name: string | null,
+  repositories: PackageRepository[],
+): PackageRepository | null {
+  for (const repository of repositories) {
+    if (name === repository.metadata.name) {
+      return repository;
+    }
+    if (is.falsy(name) && isDefaultRepository(repository)) {
+      return repository;
+    }
+  }
+  return null;
+}
+
+function isDefaultRepository(repository: PackageRepository): boolean {
+  return (
+    repository.metadata.annotations?.[
+      'packages.glasskube.dev/default-repository'
+    ] === 'true'
+  );
+}
+
+export function extractPackageFile(
+  content: string,
+  packageFile: string,
+  config?: ExtractConfig,
+): PackageFileContent | null {
+  const { packages, repositories } = parseResources(content, packageFile);
+  const deps = resolvePackageDependencies(packages, repositories);
+  return { deps };
+}
+
+export async function extractAllPackageFiles(
+  config: ExtractConfig,
+  packageFiles: string[],
+): Promise<PackageFile[] | null> {
+  const allRepositories: PackageRepository[] = [];
+  const glasskubeResourceFiles: GlasskubeResources[] = [];
+  for (const packageFile of packageFiles) {
+    const content = await readLocalFile(packageFile, 'utf8');
+    if (content !== null) {
+      const resources = parseResources(content, packageFile);
+      allRepositories.push(...resources.repositories);
+      glasskubeResourceFiles.push(resources);
+    }
+  }
+
+  const result: PackageFile[] = [];
+  for (const file of glasskubeResourceFiles) {
+    const deps = resolvePackageDependencies(file.packages, allRepositories);
+    if (deps.length > 0) {
+      result.push({ packageFile: file.packageFile, deps });
+    }
+  }
+  return result.length ? result : null;
+}
diff --git a/lib/modules/manager/glasskube/index.ts b/lib/modules/manager/glasskube/index.ts
new file mode 100644
index 0000000000000000000000000000000000000000..5004e61d3fc18e2ec96f715c3b11a3ba57e41425
--- /dev/null
+++ b/lib/modules/manager/glasskube/index.ts
@@ -0,0 +1,9 @@
+import type { Category } from '../../../constants';
+import { GlasskubePackagesDatasource } from '../../datasource/glasskube-packages';
+
+export { extractAllPackageFiles, extractPackageFile } from './extract';
+export const defaultConfig = {
+  fileMatch: [],
+};
+export const categories: Category[] = ['kubernetes', 'cd'];
+export const supportedDatasources = [GlasskubePackagesDatasource.id];
diff --git a/lib/modules/manager/glasskube/readme.md b/lib/modules/manager/glasskube/readme.md
new file mode 100644
index 0000000000000000000000000000000000000000..e115219868e0eca13e1182a6cc07c3d26c9e9dfc
--- /dev/null
+++ b/lib/modules/manager/glasskube/readme.md
@@ -0,0 +1,5 @@
+Extract version data from Packages/ClusterPackages and repository data from PackageRepositories.
+
+To use the `glasskube` manager you must set your own `fileMatch` pattern.
+The `glasskube` manager has no default `fileMatch` pattern, because there is no common filename or directory name convention for Glasskube YAML files.
+By setting your own `fileMatch` Renovate avoids having to check each `*.yaml` file in a repository for a Glasskube definition.
diff --git a/lib/modules/manager/glasskube/schema.ts b/lib/modules/manager/glasskube/schema.ts
new file mode 100644
index 0000000000000000000000000000000000000000..20d32fd5998c2affd9e6102df19ea23c0cc77f25
--- /dev/null
+++ b/lib/modules/manager/glasskube/schema.ts
@@ -0,0 +1,31 @@
+import { z } from 'zod';
+
+export const Package = z.object({
+  apiVersion: z.string().startsWith('packages.glasskube.dev/'),
+  kind: z.literal('Package').or(z.literal('ClusterPackage')),
+  spec: z.object({
+    packageInfo: z.object({
+      name: z.string(),
+      version: z.string(),
+      repositoryName: z.string().optional(),
+    }),
+  }),
+});
+
+export const PackageRepository = z.object({
+  apiVersion: z.string().startsWith('packages.glasskube.dev/'),
+  kind: z.literal('PackageRepository'),
+  metadata: z.object({
+    name: z.string(),
+    annotations: z.record(z.string(), z.string()).optional(),
+  }),
+  spec: z.object({
+    url: z.string(),
+  }),
+});
+
+export const GlasskubeResource = Package.or(PackageRepository);
+
+export type Package = z.infer<typeof Package>;
+export type PackageRepository = z.infer<typeof PackageRepository>;
+export type GlasskubeResource = z.infer<typeof GlasskubeResource>;
diff --git a/lib/modules/manager/glasskube/types.ts b/lib/modules/manager/glasskube/types.ts
new file mode 100644
index 0000000000000000000000000000000000000000..304dcd6174fc8973013262fa81755bcfa868d176
--- /dev/null
+++ b/lib/modules/manager/glasskube/types.ts
@@ -0,0 +1,7 @@
+import type { Package, PackageRepository } from './schema';
+
+export type GlasskubeResources = {
+  packageFile: string;
+  packages: Package[];
+  repositories: PackageRepository[];
+};