Releases

A release is a version of your app: the exact images and files that ship together as, say, 2.4.1. Publishing one records each image by its digest, so what a customer gets for 2.4.1 can't change afterwards, even if you later push a different build to the same tag.

Releases do three things:

  • They give your customers a page for each version, with release notes, pull commands that fetch exactly the released images, and the files you attached.
  • They tell Instances what "the newest version" is, so you can see who is behind.
  • They are what a customer on an air-gapped network bundles with the Breakwater CLI (see Air-gapped bundles).

Releases and their files are on every plan. The customer API, bundle events and the air-gapped panel on the customer's release page are part of the Growth, Scale and Enterprise plans.

Publishing a release

Open a product and click New release in its Releases card. Members, admins and owners can publish; viewers can read releases and download their files.

Field Rules
Version 1 to 64 characters from letters, digits and . _ + -, starting with a letter or digit, such as 2.4.1 or 2.4.1+build.5. Unique within the product. new is reserved.
Release notes Optional Markdown, up to 64 KB. Customers see them with the release.
Images 1 to 50 images (see below).
Files Optional, up to 20 files, 1 MB each and 5 MB in all.

A version can never be used again for that product, not even after you delete the release. This keeps 2.4.1 from meaning two different things to a customer who remembers the first.

Images

Each image is one of:

  • One of the product's repositories, plus a tag. Breakwater looks up the digest the tag points at when you publish. A multi-platform image is recorded as its index, so every platform is part of the release.
  • An outside image, such as postgres:16.4, for things your app needs that you don't host. It must be on a public registry (anonymous pulls) and must have a tag; a bare digest isn't enough, though postgres:16.4@sha256:... is fine. Breakwater looks up its digest when you publish. Short names are expanded, so postgres:16.4 is recorded as docker.io/library/postgres:16.4. Only HTTPS registries are used, and redirects aren't followed. Outside images stay on their own registry: Breakwater records them but doesn't store or serve them.

A Zarf package in a product repository can be one of a release's images: add its repository and the package version as the tag (see Zarf packages).

If you add no images, Breakwater uses every repository of the product that has a tag named exactly like the version. If you attach a Compose file and add no images, it reads them from the file instead (see below). If neither finds anything, publishing fails and says so.

Filling images from a Compose file

On the form, Fill from a Compose file reads a compose.yaml (or docker-compose.yml) and adds a row for each service's image:. Images under registry.breakwaterapp.com/<your-slug>/... must be repositories of this product, or the form says which service doesn't match; images on any other registry are treated as outside images. A digest the file pins (image: acme/server:2.4.1@sha256:...) must match what the tag points at now, or publishing fails and names the service. Images that use variables, such as ${TAG}, can't be resolved: write the full name and tag.

If you attach a file named compose.yaml, compose.yml, docker-compose.yaml or docker-compose.yml through the API without listing images, the same reading applies.

Files

Files are for what an install needs next to the images: a Compose file, a Helm values file, an env template. Names are 1 to 100 characters from letters, digits and . _ -, unique within the release. Breakwater stores each file's size and SHA-256 and shows them on the release page and in the API.

Anything that's a secret doesn't belong in a release file: customers can read every file of a release they can see, and the files are kept in Breakwater's database, not the registry.

Images stay put

Publishing a release pins its images in the registry, so they stay available for as long as the release exists:

  • Pins keep the images and their storage. Registry garbage collection never removes an image a release pins, even if you delete its tag, and that storage counts toward your usage until the release is deleted. Delete releases you no longer need to give it back.
  • Withdrawn releases keep their pins. Withdrawing hides a release from customers; it doesn't release the storage, and you can restore it.
  • Deleting is what frees the pins. The pins are removed shortly after you delete a release.

While a release is published, the registry also protects what it recorded:

  • Released tags are locked. You can't move a tag a published release recorded to a different image, or delete it. Pushing the same image to the tag again works, and a new tag works. The error looks like this:
tag acme/server:2.4.1 is part of release 2.4.1 of Acme Server; push a new tag or withdraw the release first
  • Pinned manifests can't be deleted. Deleting a manifest by digest is refused while a release still pins it, or while it's a platform manifest of a multi-platform image a release pins. The error is manifest sha256:... is part of a release; delete the release first. This holds for withdrawn releases too.
  • A push during publishing withdraws the release. Tags lock when the release is published. If a push moves one of its tags while it's still publishing, Breakwater withdraws the release and says which tag moved. Its pins still hold the images it was published with, so you can restore it if those are what you meant, or publish a new version. Through the API this is a 422 whose body also carries the withdrawn release's release_id, and the audit trail shows the publish and the withdrawal.
  • Floating tags stay free. Tags no release recorded, such as latest, move as usual.

Tags starting with bw-release- are reserved: Breakwater uses them for pins, and you can't push or pull them.

If a tag has moved since a release was published, its page says Tag moved since release. Customers still get the recorded digest.

Who sees a release

A customer sees a published release when they have an active license for the product whose tag pattern allows the tag of every one of the release's images from your repositories. Outside images don't count towards this. A release whose repositories were deleted or removed from the product is marked incomplete and customers don't see it until you fix that. The release page shows how many customers it's available to.

A customer who can see a release can pull exactly that release's images by digest, even after a tag has moved on to a different image.

Withdraw, restore and delete

Admins and owners manage a release's life from its page.

Action What happens
Withdraw Customers stop seeing the release and its images stop being granted through it. Its tags are no longer locked. It keeps its pins.
Restore A withdrawn release is published again.
Delete Only for a withdrawn release. Its images and files are removed and its pins are cleaned up. A tombstone stays so the version can't be reused.

Each is recorded in the audit log.

Instances and releases

Instances are judged against releases: an instance is behind when a release newer than the version it reports is available to its customer. See Instances. A product with no releases shows no "available" badges.

Air-gapped bundles

Some customers run where nothing reaches the internet. With the Breakwater CLI they pick a release, the CLI pulls its images and files with their pull token and writes one file they can carry across, and loads it into their own registry on the other side. They use the same pull tokens and licenses as everyone else, so there's nothing for you to do beyond publishing releases.

Offline installs can't check in, so Breakwater records bundle events: when a customer starts and finishes a bundle, with the release, platforms, CLI version, size and the address they ran it from. They show on:

  • the release page, in its Bundles card, with how many customers have bundled it;
  • the customer's page;
  • the Instances page, in its Bundles card.

Bundle events and the customer API behind them are part of the Growth, Scale and Enterprise plans. On other plans the portal shows no Bundles cards, and the customer's release page hides the whole air-gapped panel, Docker commands included. Their digest-pinned docker pull commands under Images still work.

Through the API

Releases are in the Vendor API, nested under products, and keyed by version rather than by uid:

Method Endpoint Description
GET /api/v1/vendor/products/{uid}/releases List the product's published and withdrawn releases, newest first
POST /api/v1/vendor/products/{uid}/releases Publish a release
GET /api/v1/vendor/products/{uid}/releases/{version} Get a release
POST /api/v1/vendor/products/{uid}/releases/{version}/withdraw Withdraw it
POST /api/v1/vendor/products/{uid}/releases/{version}/restore Restore it
DELETE /api/v1/vendor/products/{uid}/releases/{version} Delete a withdrawn release (409 if it isn't withdrawn)
GET /api/v1/vendor/products/{uid}/releases/{version}/files/{name} Download a file
GET /api/v1/vendor/bundle_events List bundle events. Filters: customer_id, product_id, release_id (uids). Growth plans and up; otherwise 403 with {"error": "plan_required"}

Publishing takes a body like this. images and files are optional, and each file gives content (text) or content_base64, not both:

{
  "release": {
    "version": "2.4.1",
    "notes": "Fixes the importer.",
    "images": [
      {"repository_id": "repo_def456", "tag": "2.4.1"},
      {"external": "postgres:16.4"}
    ],
    "files": [
      {"name": "compose.yaml", "content": "services:\n  ..."}
    ]
  }
}

A release comes back with its status (published or withdrawn), incomplete and incomplete_reasons, its images (kind, repository_path, tag, digest, reference, media_type, size) and its files (name, size, sha256, and a url to download). Problems are 422 with {"error": "Validation failed", "details": [...]}. The API Explorer has the full schema.

Webhooks

Three events cover releases (see Webhooks):

  • release.published, when a release is published or restored (data.restored tells which);
  • release.withdrawn;
  • release.bundled, when a customer finishes a bundle.

Payloads carry file names, sizes and checksums, never file contents.

Last updated October 30, 2026