Content Releases
Group documents into a release and publish or unpublish them together, at a time you choose.
A release is a set of documents that go live at the same instant. A product launch touching a landing page, three blog posts and a pricing single becomes one thing you schedule, rather than five things someone has to publish in the right order at 9am.
Each member of a release is set to either publish or unpublish, so the same release can put new pages live and retire old ones in the same moment.
Availability
| Built in | No plugin required |
| Admin | A Releases section with a calendar view, and an "Add to release" action on documents |
| Permission | Scheduling requires publish access. A key scoped only to publishing releases can schedule without holding a general read grant |
What it is not
This is scheduled publishing, not an approval workflow. There are no review stages, transitions or assignees. If you need "editor submits, manager approves, then it goes live", that is not what a release does.
The shape of a release
| Step | What happens |
|---|---|
| Create | An empty release with a name |
| Add members | Each member names a document and an action, publish or unpublish |
| Schedule | The release is given a time. Blocking problems are checked first |
| Apply | On a background-jobs pass after that time, every member's action is applied |
| Cancel | An unapplied release can be cancelled |
Scheduling stores a time; a job runner applies it
Scheduling records when a release is due. The work is done by a releases:drain sweep that
runs as part of the background jobs pass, which happens only when something
calls the jobs route.
Running the queue is where that is set up: the path, the shared secret the caller needs, and the two ways a scheduler ends up silently doing nothing. Wire it there once and every scheduled release depends on it.
With no scheduler configured, a release is never applied. Its due time passes and the sweep that would carry it out never runs. A scheduler configured but refused, by the wrong path or a missing secret, leaves you in the same place.
What that does not mean is that the content change is invisible. A read consults the same due-release rule the sweep does, so a scheduled draft can be revealed, and a takedown hidden, before anything has run. On a multi-instance deployment an instance other than the one that scheduled it can be up to 30 seconds behind, so treat this as "within seconds of the instant" rather than exactly at it.
As apply-due-releases.ts puts it, the read path can only make a due release LOOK applied.
The sweep is what applies it for real, writing as the member's own author, and it is the
only thing that advances the release's own state.
What a reader sees and what the stored rows hold are therefore two different questions
while a release is waiting for the sweep, and the second one has more cases than the first:
overlapping releases, a member whose author is gone, and a pass that applied some members
before failing on another. If you integrate against the database directly rather than
through Nextly, read apply-due-releases.ts before relying on release state.
A pass is bounded, which is why a due release is not always applied by the next tick: it claims at most 10 queued jobs and stops starting new ones after 20 seconds. The bounds and what they mean for a serverless timeout are on the jobs page.
The sweep can also defer work past its deadline while still reporting success. So a release is applied on the first pass that actually reaches and completes it, which is not always the first pass after its due time. With a backlog, or several expensive releases due together, expect it a pass or two later. Treat the scheduled time as "not before", not as "at".
Blocking reasons
Before a release can be scheduled, it is checked for problems that would make it fail half-way — a member pointing at a document that no longer exists, for example. Those are reported as blocking reasons so they can be fixed while there is still time, rather than surfacing as a partial publish at 9am.
An empty release has no blockers; there is nothing to go wrong.
One release covers every language
A member is the whole document. Adding a post to a release schedules that post in every language it has.
Per-locale scheduling is not supported: adding a member that names a single locale is refused, and the admin only offers the action from a document's default locale. Each language does have its own published state — see Localization — but that is publishing, not scheduling.
Related
- Localization — per-language publishing
- Draft and published status
- Versions — what a publish changed