Promotion is the one control that moves work from your preview site to your live site. Everything preview has that live does not, released as a single deployment.
The button is on the Deploy screen, in the Preview site card, colored pink: Push preview live. Under it, whenever it is unavailable, is a line of text saying why — and that line is the most useful thing on this page.
What promotion actually moves
It moves the current state of your preview site. Not a selection, not the changes since last time, not the ones you approve — the whole thing, as one unit, in one deployment badged Promoted Preview => Public.
That is most often template and layout work: a developer’s CLI pushes, styling changes. It is the whole reason the preview site exists — preview and live are two separate sites, and this button is the bridge between them.
When the button is available
Five conditions have to hold at once. Any one of them failing grays the button out and prints the reason underneath.
- You hold
deployment:promote— the Content Publisher role or above, or a Developer access override on a lower role. - Preview is genuinely ahead of live.
- Preview is not mid-build.
- Nothing on the preview branch is still waiting to be built — the preview you are looking at is the newest thing on it.
- No promotion is already running.
“Ahead” has a precise meaning
Leed does not diff the two sites. It takes the commit the newest preview deployment was built from and asks whether that commit has ever reached the live site: preview is ahead when it has a deployment and its commit appears on no live deployment at all.
That is a commit-identity check, not a count of changes. Two consequences follow, and both surprise people:
- A change that produces an identical result still counts as ahead, because it is a different commit.
- Publishing to live does not make preview look ahead. A page publish or a Pending changes publish lands on live as a new commit of its own, but the preview commit you already promoted is still on live, so the button stays at Nothing new to push live. until something new reaches preview.
Every reason it is disabled, verbatim
The hint under the button is evaluated in a fixed order and only ever shows one line. Four of the seven render as small gray text; the other three are red alert boxes, split across two lines at the sentence break, because they are real blockers rather than status reports.
| What you see | Tone | What it means | What to do |
|---|---|---|---|
You don't have permission to push live. | muted | You lack deployment:promote and have no Developer override | Ask an administrator for the Content Publisher role or the Developer checkbox |
Pushing live — merging staging → main… | muted | Your own promotion is in flight right now | Wait — this clears itself |
Preview is still building. Wait for it to finish. | muted | The newest preview deployment has not settled | Wait for its row to reach Active |
Blocked: the preview site's latest deployment failed. Resolve the error before promoting to public. | red alert | The newest preview build failed | Fix the failure and publish to preview again |
Blocked: the preview site is still building. Wait for it to finish. | red alert | Same condition as the third row, reported by the server rather than the browser | Wait |
Blocked: the preview branch has commits that have not been built yet. Wait for the preview build to finish. | red alert | Something reached the preview branch after the newest preview deployment was built, so promoting now would release more than the preview you can see | Wait for the new preview build to finish |
Nothing new to push live. | muted | The newest preview commit is already on the live site | Nothing is wrong — there is genuinely nothing to release |
Two rows say the same thing in different words because two layers check it independently: the browser checks the deployment it is already displaying, and the server re-checks before answering. Both wordings are real and you may see either.
The second is the state you will see most of the time, and it is not an error. Preview and live agree.
If the red alert says the preview build failed, start with the failure page — the fix is always on the preview side. A content or template error has to be corrected and pushed again; an infrastructure failure can be retried from the row itself.
What happens while it runs
Press the button and, in this order:
- The button changes to Pushing live… and its hint becomes “Pushing live — merging staging → main…”.
- A Promotion started toast appears. If it turns out there was nothing to move, the toast says so instead — Nothing to promote — the public site already matches the preview. or Already promoted — this preview commit is on the public site. — and no new row is created.
- A new row lands in the Live site history column badged Promoted Preview => Public, with your name on it — reading the history.
Underneath, Leed merges the site repository’s staging branch into main — reusing an open merge request if one exists, and opening one if not — then starts an ordinary live build of the merged result. The merge is pinned to the exact commit your preview was built from, so a push that lands on staging in the seconds after you click cannot ride along unseen: GitLab refuses the merge and nothing is promoted. From there it is the same pipeline as any other deployment: build, upload a version, activate it.
One asymmetry is worth knowing. Publishing to live normally fires a parallel preview rebuild so preview does not fall behind. A promotion skips that, because preview is by definition already in sync with what you just released. So a promotion produces one row, not two.
Promotion is also available outside the browser, for a developer or a release script: leed deployments promote from the CLI, or the promote_staging_to_production tool over MCP. Both ask for confirmation first, and both are safe to repeat — a promotion that has already happened, or one with nothing to move, answers with that outcome rather than an error and starts no build.
If you get an error instead
The server re-checks the conditions when it receives the request, so a state that changed between the page loading and you clicking is caught there rather than in the browser. The three state refusals are 409 Conflict; a merge that GitLab would not complete is a 502.
| HTTP status | Server message | What the CMS shows you |
|---|---|---|
| 409 | Preview site's latest deployment is in an error state. Resolve and retry. | “The preview site’s latest deployment failed. Resolve it before promoting.” |
| 409 | Preview site is still building. Wait for it to finish before promoting. | “The preview site’s latest deployment failed. Resolve it before promoting.” |
| 409 | The preview branch has commits that have not been built yet, so promoting now would publish more than the preview you are looking at. Wait for the preview build to finish, then promote. | “The preview site’s latest deployment failed. Resolve it before promoting.” |
| 502 | The staging→main merge did not complete. Nothing was promoted; check the merge request in GitLab. | The server’s message, as an error. Nothing was written, so trying again is safe |
Promotion versus publishing
Two buttons on the same screen, both of which put something on your live site. The difference is what they carry.
| Publish N to live site | Push preview live | |
|---|---|---|
| Moves | CMS records — labels, menus, forms, settings, team profiles | files — templates, layouts, anything a developer pushed |
| Source | the Pending changes list | the current state of the preview site |
| Selection | tick exactly what you want | all of it, no selection |
| Reason badge | one per kind of item ticked | Promoted Preview => Public |
Reaching for the wrong one is harmless — neither can move the other’s work — but it does waste a build. Rule of thumb: if you changed it by typing in the CMS, publish it. If it arrived as a file, promote it.
A developer who has just pushed will usually want this button next — what their push already did covers the half that happens before you get here, and the CLI side is in Validate, Commit and Push.