diff --git a/lib/modules/datasource/api.ts b/lib/modules/datasource/api.ts
index e7284442c0a36ba4638f81e168f6f93e81e45c5c..84ba7fc52c5d2a4713dfa0e40a0253c80bfb588d 100644
--- a/lib/modules/datasource/api.ts
+++ b/lib/modules/datasource/api.ts
@@ -17,6 +17,7 @@ import { DartDatasource } from './dart';
 import { DartVersionDatasource } from './dart-version';
 import { DebDatasource } from './deb';
 import { DenoDatasource } from './deno';
+import { DevboxDatasource } from './devbox';
 import { DockerDatasource } from './docker';
 import { DotnetVersionDatasource } from './dotnet-version';
 import { EndoflifeDateDatasource } from './endoflife-date';
@@ -88,6 +89,7 @@ api.set(DartDatasource.id, new DartDatasource());
 api.set(DartVersionDatasource.id, new DartVersionDatasource());
 api.set(DebDatasource.id, new DebDatasource());
 api.set(DenoDatasource.id, new DenoDatasource());
+api.set(DevboxDatasource.id, new DevboxDatasource());
 api.set(DockerDatasource.id, new DockerDatasource());
 api.set(DotnetVersionDatasource.id, new DotnetVersionDatasource());
 api.set(EndoflifeDateDatasource.id, new EndoflifeDateDatasource());
diff --git a/lib/modules/datasource/devbox/common.ts b/lib/modules/datasource/devbox/common.ts
new file mode 100644
index 0000000000000000000000000000000000000000..9149c754171e9ae1e632605de388d4b74c5832f3
--- /dev/null
+++ b/lib/modules/datasource/devbox/common.ts
@@ -0,0 +1,3 @@
+export const defaultRegistryUrl = 'https://search.devbox.sh/v2/';
+
+export const datasource = 'devbox';
diff --git a/lib/modules/datasource/devbox/index.spec.ts b/lib/modules/datasource/devbox/index.spec.ts
new file mode 100644
index 0000000000000000000000000000000000000000..5b525a34d9c0b8dafb8d0a417df9898640e0b226
--- /dev/null
+++ b/lib/modules/datasource/devbox/index.spec.ts
@@ -0,0 +1,159 @@
+import { getPkgReleases } from '..';
+import * as httpMock from '../../../../test/http-mock';
+import { EXTERNAL_HOST_ERROR } from '../../../constants/error-messages';
+import { datasource, defaultRegistryUrl } from './common';
+
+const packageName = 'nodejs';
+
+function getPath(packageName: string): string {
+  return `/pkg?name=${encodeURIComponent(packageName)}`;
+}
+
+const sampleReleases = [
+  {
+    version: '22.2.0',
+    last_updated: '2024-05-22T06:18:38Z',
+  },
+  {
+    version: '22.0.0',
+    last_updated: '2024-05-12T16:19:40Z',
+  },
+  {
+    version: '21.7.3',
+    last_updated: '2024-04-19T21:36:04Z',
+  },
+];
+
+describe('modules/datasource/devbox/index', () => {
+  describe('getReleases', () => {
+    it('throws for error', async () => {
+      httpMock
+        .scope(defaultRegistryUrl)
+        .get(getPath(packageName))
+        .replyWithError('error');
+      await expect(
+        getPkgReleases({
+          datasource,
+          packageName,
+        }),
+      ).rejects.toThrow(EXTERNAL_HOST_ERROR);
+    });
+  });
+
+  it('returns null for 404', async () => {
+    httpMock.scope(defaultRegistryUrl).get(getPath(packageName)).reply(404);
+    expect(
+      await getPkgReleases({
+        datasource,
+        packageName,
+      }),
+    ).toBeNull();
+  });
+
+  it('returns null for empty result', async () => {
+    httpMock.scope(defaultRegistryUrl).get(getPath(packageName)).reply(200, {});
+    expect(
+      await getPkgReleases({
+        datasource,
+        packageName,
+      }),
+    ).toBeNull();
+  });
+
+  it('returns null for empty 200 OK', async () => {
+    httpMock
+      .scope(defaultRegistryUrl)
+      .get(getPath(packageName))
+      .reply(200, { versions: [] });
+    expect(
+      await getPkgReleases({
+        datasource,
+        packageName,
+      }),
+    ).toBeNull();
+  });
+
+  it('throws for 5xx', async () => {
+    httpMock.scope(defaultRegistryUrl).get(getPath(packageName)).reply(502);
+    await expect(
+      getPkgReleases({
+        datasource,
+        packageName,
+      }),
+    ).rejects.toThrow(EXTERNAL_HOST_ERROR);
+  });
+
+  it('processes real data', async () => {
+    httpMock.scope(defaultRegistryUrl).get(getPath(packageName)).reply(200, {
+      name: 'nodejs',
+      summary: 'Event-driven I/O framework for the V8 JavaScript engine',
+      homepage_url: 'https://nodejs.org',
+      license: 'MIT',
+      releases: sampleReleases,
+    });
+    const res = await getPkgReleases({
+      datasource,
+      packageName,
+    });
+    expect(res).toEqual({
+      homepage: 'https://nodejs.org',
+      registryUrl: 'https://search.devbox.sh/v2',
+      releases: [
+        {
+          version: '21.7.3',
+          releaseTimestamp: '2024-04-19T21:36:04.000Z',
+        },
+        {
+          version: '22.0.0',
+          releaseTimestamp: '2024-05-12T16:19:40.000Z',
+        },
+        {
+          version: '22.2.0',
+          releaseTimestamp: '2024-05-22T06:18:38.000Z',
+        },
+      ],
+    });
+  });
+
+  it('processes empty data', async () => {
+    httpMock.scope(defaultRegistryUrl).get(getPath(packageName)).reply(200, {
+      name: 'nodejs',
+      summary: 'Event-driven I/O framework for the V8 JavaScript engine',
+      homepage_url: 'https://nodejs.org',
+      license: 'MIT',
+      releases: [],
+    });
+    const res = await getPkgReleases({
+      datasource,
+      packageName,
+    });
+    expect(res).toBeNull();
+  });
+
+  it('returns null when no body is returned', async () => {
+    httpMock
+      .scope(defaultRegistryUrl)
+      .get(getPath(packageName))
+      .reply(200, undefined);
+    const res = await getPkgReleases({
+      datasource,
+      packageName,
+    });
+    expect(res).toBeNull();
+  });
+
+  it('falls back to a default homepage_url', async () => {
+    httpMock.scope(defaultRegistryUrl).get(getPath(packageName)).reply(200, {
+      name: 'nodejs',
+      summary: 'Event-driven I/O framework for the V8 JavaScript engine',
+      homepage_url: undefined,
+      license: 'MIT',
+      releases: sampleReleases,
+    });
+    const res = await getPkgReleases({
+      datasource,
+      packageName,
+    });
+    expect(res?.homepage).toBeUndefined();
+  });
+});
diff --git a/lib/modules/datasource/devbox/index.ts b/lib/modules/datasource/devbox/index.ts
new file mode 100644
index 0000000000000000000000000000000000000000..be89dced54ee8fe22e2dcfe36178ea3e9394348e
--- /dev/null
+++ b/lib/modules/datasource/devbox/index.ts
@@ -0,0 +1,57 @@
+import { logger } from '../../../logger';
+import { ExternalHostError } from '../../../types/errors/external-host-error';
+import { HttpError } from '../../../util/http';
+import { joinUrlParts } from '../../../util/url';
+import * as devboxVersioning from '../../versioning/devbox';
+import { Datasource } from '../datasource';
+import type { GetReleasesConfig, ReleaseResult } from '../types';
+import { datasource, defaultRegistryUrl } from './common';
+import { DevboxResponse } from './schema';
+
+export class DevboxDatasource extends Datasource {
+  static readonly id = datasource;
+
+  constructor() {
+    super(datasource);
+  }
+
+  override readonly customRegistrySupport = true;
+  override readonly releaseTimestampSupport = true;
+
+  override readonly registryStrategy = 'first';
+
+  override readonly defaultVersioning = devboxVersioning.id;
+
+  override readonly defaultRegistryUrls = [defaultRegistryUrl];
+
+  async getReleases({
+    registryUrl,
+    packageName,
+  }: GetReleasesConfig): Promise<ReleaseResult | null> {
+    const res: ReleaseResult = {
+      releases: [],
+    };
+
+    logger.trace({ registryUrl, packageName }, 'fetching devbox release');
+
+    const devboxPkgUrl = joinUrlParts(
+      registryUrl!,
+      `/pkg?name=${encodeURIComponent(packageName)}`,
+    );
+
+    try {
+      const response = await this.http.getJson(devboxPkgUrl, DevboxResponse);
+      res.releases = response.body.releases;
+      res.homepage = response.body.homepage;
+    } catch (err) {
+      // istanbul ignore else: not testable with nock
+      if (err instanceof HttpError) {
+        if (err.response?.statusCode !== 404) {
+          throw new ExternalHostError(err);
+        }
+      }
+      this.handleGenericErrors(err);
+    }
+    return res.releases.length ? res : null;
+  }
+}
diff --git a/lib/modules/datasource/devbox/schema.ts b/lib/modules/datasource/devbox/schema.ts
new file mode 100644
index 0000000000000000000000000000000000000000..bc31efae879850cde7898aebf4417110ccd37e4d
--- /dev/null
+++ b/lib/modules/datasource/devbox/schema.ts
@@ -0,0 +1,23 @@
+import { z } from 'zod';
+
+export const DevboxRelease = z.object({
+  version: z.string(),
+  last_updated: z.string(),
+});
+
+export const DevboxResponse = z
+  .object({
+    name: z.string(),
+    summary: z.string().optional(),
+    homepage_url: z.string().optional(),
+    license: z.string().optional(),
+    releases: DevboxRelease.array(),
+  })
+  .transform((response) => ({
+    name: response.name,
+    homepage: response.homepage_url,
+    releases: response.releases.map((release) => ({
+      version: release.version,
+      releaseTimestamp: release.last_updated,
+    })),
+  }));