Skip to content

NixOS channels and builds ​

Where nixpkgs stands, as a set of small questions with separate answers: which channels exist, whether a CVE's fix reached them, whether a pull request was backported, when a commit landed in a stable channel and whether Hydra built a package. Each answers from one upstream and says so when that upstream could not be read.

GET /api/v1/nix/channels
GET /api/v1/nix/channels/history?channel=
GET /api/v1/cve/:id/nix/channels
GET /api/v1/nix/pr/:number/backports
GET /api/v1/nix/stable/:series/containment/:sha
GET /api/v1/nix/hydra/:package

Channel overview ​

GET /api/v1/nix/channels

Where each channel stands, as NixOS's own monitoring reports it. The channel_revision metric on prometheus.nixos.org carries one row per channel with the revision it points at, its status and its variant; this service reads it every half hour, keeps the latest reading and answers from that. The unstable channel, the current stable release and the previous one are named at the top, every channel the monitoring reports sits under allChannels, and basis: "observed" says the answer is that reading. There is no data envelope: the channels sit at the top level.

json
{
  "success": true,
  "basis": "observed",
  "source": "prometheus.nixos.org",
  "sourceUrl": "https://prometheus.nixos.org/api/v1/query?query=channel_revision",
  "observedAt": "2026-09-28T02:30:04.118Z",
  "evaluatedAt": "2026-09-28T02:30:03.902Z",
  "unchangedSince": "2026-09-27T22:00:03.551Z",
  "snapshot": "4f9c1e7a3b2d8e6f0a1b2c3d4e5f60718293a4b5c6d7e8f90a1b2c3d4e5f6071",
  "timestamp": "2026-09-28T02:41:12.006Z",
  "channels": {
    "unstable": { "name": "nixos-unstable", "version": "unstable", "branch": "nixos-unstable", "revision": "e158d9ed9b51c98974c5e66e1ba1c9e0255fecaa", "latestCommit": "e158d9e", "status": "rolling", "variant": "primary" },
    "currentStable": { "name": "nixos-26.05", "version": "26.05", "branch": "release-26.05", "revision": "5e2305d577ca00acbba631b05cb1094d172b29f3", "latestCommit": "5e2305d", "status": "stable", "variant": "primary" },
    "previousStable": { "name": "nixos-25.11", "version": "25.11", "branch": "release-25.11", "revision": "b6018f87da91d19d0ab4cf979885689b469cdd41", "latestCommit": "b6018f8", "status": "unmaintained", "variant": "primary" }
  },
  "allChannels": [
    { "name": "nixos-25.11", "version": "25.11", "branch": "release-25.11", "revision": "b6018f87da91d19d0ab4cf979885689b469cdd41", "latestCommit": "b6018f8", "status": "unmaintained", "variant": "primary" },
    { "name": "nixos-25.11-small", "version": "25.11", "branch": "release-25.11", "revision": "c9bfd86ed684d27e63b0ff9ebb18699f84f27a3b", "latestCommit": "c9bfd86", "status": "unmaintained", "variant": "small" },
    { "name": "nixpkgs-unstable", "version": "unstable", "branch": "nixpkgs-unstable", "revision": "3181085bfd08663b6b9e60bc7a8395c2aaa741bd", "latestCommit": "3181085", "status": "rolling", "variant": null }
  ],
  "history": "/api/v1/nix/channels/history?channel={name}",
  "nextRelease": { "version": "26.11", "expectedDate": "2026-11-30", "daysUntil": 63 }
}
FieldMeaning
observedAtWhen this service last read the channel set.
evaluatedAtThe sample time Prometheus reported for that reading.
unchangedSinceWhen this exact channel set was first seen. A set that has stood for days and one read a moment ago both carry a fresh observedAt; this tells them apart.
snapshotThe SHA-256 digest of the channel rows, which names the archived snapshot. It proves the rows are what this service normalised from the response it received, not that prometheus.nixos.org sent them: the endpoint signs nothing.
statusAs the monitoring labels it: rolling, stable, unmaintained or beta.
branchThe nixpkgs branch a channel is published from: release-YY.MM for a release series, and for a rolling channel the branch of the same name, which nixpkgs advances to each published revision.

The current stable release is the highest series among the primary channels and the previous one is the next below it, whatever their status: for a month after a release both read stable, and the status is carried so a reader sees which.

Cached for fifteen minutes, against a half-hourly reading. When no reading is held, because the poll has not run yet or has been failing since the last one expired, the route infers the channel list from the release calendar and four GitHub branch lookups instead, as it always did. It says so: basis: "inferred", source: "nixpkgs-github", commitDate in place of revision and status, and an observation block with the reason and the last failed poll when there is one. A GitHub failure on that path answers 500 with success: false, never a stale channel presented as current.

Channel history ​

GET /api/v1/nix/channels/history?channel=nixos-unstable

Every change this service has observed to one channel's revision or status, newest first. Nothing upstream keeps this: Prometheus holds the series for its retention window and channels.nixos.org shows the present revision only, so the record begins when this service began observing.

json
{
  "success": true,
  "data": {
    "channel": "nixos-unstable",
    "source": "prometheus.nixos.org",
    "entries": [
      { "channel": "nixos-unstable", "kind": "revision", "from": "e158d9ed9b51c98974c5e66e1ba1c9e0255fecaa", "to": "0b7a2d9c4e6f8a1b3c5d7e9f0a2b4c6d8e0f1a2b", "observedAt": "2026-09-29T04:00:03.917Z", "status": "rolling", "previousStatus": "rolling", "snapshot": "9a8b7c6d5e4f30211a2b3c4d5e6f708192a3b4c5d6e7f8091a2b3c4d5e6f7081" },
      { "channel": "nixos-unstable", "kind": "appeared", "from": null, "to": "e158d9ed9b51c98974c5e66e1ba1c9e0255fecaa", "observedAt": "2026-09-28T00:00:03.551Z", "status": "rolling", "snapshot": "4f9c1e7a3b2d8e6f0a1b2c3d4e5f60718293a4b5c6d7e8f90a1b2c3d4e5f6071" }
    ],
    "count": 2,
    "limit": 50,
    "nextBefore": null,
    "retained": { "held": 2, "cap": 1000, "dropped": 0, "oldestHeld": "2026-09-28T00:00:03.551Z", "recordedSince": "2026-09-28T00:00:03.551Z" },
    "note": "Changes since observation began, at each half-hourly poll. from and to are nixpkgs revisions; kind is revision, status, appeared or retired. dropped entries beyond the cap remain in the archived snapshots."
  },
  "timestamp": "2026-09-29T09:12:40.210Z"
}
ParameterMeaning
channelRequired. The name as NixOS publishes it. Without one the answer is 400 with code CHANNEL_REQUIRED and the channels known.
limitEntries per page, 1 to 200, default 50.
beforeThe nextBefore of the previous page. Entries observed strictly before it are returned.
kindMeaning
revisionThe channel advanced. from and to are the revisions either side.
statusThe same revision under a new status, as when a release goes out of support and reads unmaintained. previousStatus carries the old one.
appearedThe channel was seen for the first time, including every channel at the first observation. from is null.
retiredThe channel is no longer reported. to is null.

observedAt is when this service noticed, at a half-hourly poll, not when the channel advanced. snapshot is the digest of the archived channel set the change was seen in, and each archived snapshot names the one before it, so the chain of snapshots is the full record even once the working copy has dropped entries beyond its cap; retained.dropped counts those.

A channel in the latest observation with no recorded change answers 200 with an empty page. A name never observed is 404 with code CHANNEL_UNKNOWN. A history that could not be read is 503 with code HISTORY_UNAVAILABLE, which says nothing about the channel.

Fix status per channel for a CVE ​

GET /api/v1/cve/:id/nix/channels

The nixpkgs pull requests whose text names the CVE, and for each channel whether one of them has reached it.

json
{
  "success": true,
  "data": {
    "cveId": "CVE-2024-3094",
    "fixPRs": [
      { "number": 300028, "title": "Revert \"xz: 5.4.6 -> 5.6.1\"", "state": "closed", "url": "https://github.com/NixOS/nixpkgs/pull/300028", "createdAt": "2024-03-29T16:20:25Z" }
    ],
    "channelStatus": {
      "unstable": { "fixed": true, "pr": { "number": 300028, "title": "Revert \"xz: 5.4.6 -> 5.6.1\"", "state": "closed", "url": "https://github.com/NixOS/nixpkgs/pull/300028", "createdAt": "2024-03-29T16:20:25Z" } },
      "26.05": { "fixed": false, "pr": null },
      "25.11": { "fixed": false, "pr": null }
    }
  },
  "timestamp": "2026-09-28T01:04:06.748Z"
}

fixPRs is a text search: a pull request that mentions the CVE is evidence that somebody connected the two, not a fix claim. fixed: false for a stable channel means no mentioning PR was found to have reached it. For a dated answer about one specific commit, use fix timing, which asks you to name the commit and records that the attribution is yours.

Backports of a pull request ​

GET /api/v1/nix/pr/:number/backports

Whether the pull request merged and which release branches carry a backport of it. A backport is a separate commit with its own sha, so use the mergeCommit under each branch, not the original, when asking a containment question.

json
{
  "success": true,
  "data": {
    "merged": true,
    "prNumber": "300000",
    "mergedAt": "2024-04-01T20:04:00Z",
    "mergeCommit": "b399a09e3b4dc7ef23505513d39dd2c0dcf6b842",
    "targetBranch": "master",
    "title": "python312Packages.threadpoolctl: 3.3.0 -> 3.4.0",
    "backports": {}
  },
  "timestamp": "2026-09-28T01:03:04.292Z"
}

An empty backports object is a statement that no backport was found, which for a pull request merged only to master is the ordinary answer.

Stable channel containment ​

GET /api/v1/nix/stable/:series/containment/:sha

When a commit reached a published nixos-<series> channel release: the first release whose revision contains it and the last one that did not, so the window is visible. There is no data envelope.

json
{
  "series": "25.05",
  "commit": "29e290002bff",
  "outcome": "landed",
  "landedIn": { "release": "nixos-25.05.5210.f1a7c8d9e2b3", "revision": "f1a7c8d9e2b3...", "publishedAt": "2025-08-12T03:14:00Z" },
  "lastUncontainedRelease": { "release": "nixos-25.05.5188.0c2e4b6a8d1f", "revision": "0c2e4b6a8d1f...", "publishedAt": null },
  "checkedAgainst": { "latestRelease": "nixos-25.05.6012.9b8a7c6d5e4f", "publishedAt": "2026-09-27T22:40:00Z" },
  "note": null,
  "checkedAt": "2026-09-28T01:03:10.000Z"
}
OutcomeMeaning
landedA published release of the series contains the commit. landedIn names it.
not_landedNo published release contains this exact commit. Cherry-picks get new shas, so a fix merged to master and backported under a different sha reads not_landed here; supply the commit that landed on the release branch.
commit_unknownGitHub does not know the sha.
series_unknownNo channel history is published for that series.
release_history_unresolvableThe published history names a revision GitHub cannot resolve, so containment cannot be computed. A property of the channel history rather than an outage; retrying will not change it.

An upstream failure answers 502 naming the source. The route never answers 200 with an invented outcome.

Hydra build status ​

GET /api/v1/nix/hydra/:package

The latest Hydra builds of one job in the nixpkgs unstable jobset, grouped by platform. The path segment is the package attribute as Hydra names the job (curl, python3Packages.requests), not a channel name.

json
{
  "success": true,
  "data": {
    "found": true,
    "package": "curl",
    "platforms": {
      "x86_64-linux": { "latestBuild": 312345678, "buildStatus": 0, "timestamp": 1758999600, "hydraUrl": "https://hydra.nixos.org/build/312345678" }
    }
  },
  "timestamp": "2026-09-28T01:02:58.877Z"
}

buildStatus is Hydra's own code: 0 is a successful build. found: false means Hydra had no matching job, which is not a failed build. Hydra not answering is 502 with data: null, never found: false. Cached for four hours.

  • Fix timing: when a named fix commit reached each supported stable channel.
  • Nix CVEs: the open nixpkgs security issues with their linked pull requests.
  • Repology lookup: the versions each nixpkgs channel carries.