Using the dashboard
What a row means, what a tick does, what the job log of a scan says, and the limits to know about. The dashboard itself is shown in What it looks like. A tick on a row with deletes deploys those deletes too, as the row shows them.
Rows and ticks
- A row with a box has changes waiting. Its details show the resources that would change and the paths of the properties that change, down to the key inside a map or a list. A delete or a replace is always shown open under the row, never folded away, and a caution block above the pending list names every pending stack that has one. When the dashboard grows past what an issue holds, the biggest rows are shortened first and link to the full diff in the run’s summary.
- The line under a row says where its change came from. It names the merged pull requests and direct pushes the stack claims since its last deploy from the dashboard, five unless
attribution.namessays otherwise, and counts the changes outside the stack, such as a lockfile bump or a shared package. A fold at the end of the row names those. Thecomparelink shows every commit in between. previewopens that stack’s preview page. It is a check run on the scanned commit, namedsluiceway / <stack id>, with the stack’s whole diff: every resource that changes and every property path, and a value only wheredashboard.showValueslists the path. It links back to the dashboard, the run’s summary and the job log. The scan updates the page in place when it scans the same commit again. GitHub files the page as a job of whichever workflow run came first on that commit, which may be another workflow of yours, and lists it in a pull request’s checks as neutral. It never fails a check. Withoutchecks: writein the workflow’s permissions there is no page, andpreviewopens the run’s summary.- Tick the box to deploy that stack. Sluiceway checks that you may tick it, previews the stack again, and deploys only if the fresh preview still matches what the row showed. The row says deploying, then goes back to in sync, or shows a failure line with a link to the run.
- A stack set to deploy on merge goes out without a tick. A stack whose entry in
sluiceway.yamlsaysdeploy: on-mergeis deployed by the run a merge starts, once its scan finds it pending, through the same fresh preview and hash check as a tick. Its row saysdeploying on merge · merged by alicewhile it goes out, neverticked by, and Recently deployed saysmerged by alice. Every other stack waits for a tick, which is the default. - A pending row with a line that starts
this stack deploys on merge, and this change waits for a tick:belongs to a stack set to on-merge whose change did not go out by itself. The line says why: it deletes or replaces a resource, the stack drifted, a stack it depends on has a change waiting, it costs more a month thancost.thresholdor its cost could not be estimated while a threshold is set, or the scan that found it did not follow a merge.deploys are turned offsays the same fordeploys: false. Tick the box to deploy it, as for any stack. - A line under the first line that says
about **31.20 USD** more a monthis what the change does to the monthly bill, a delta and never the bill, for an OpenTofu or Terraform stack of a repo that turnedcost.enabledon.less a monthis a change that saves,about the same cost a monthone that changes nothing. It is an estimate against a price list, not in the diff hash, and a stack whose estimate failed has no line: the run says why. - A tick covers the change as shown, and the values it does not show. The row shows which properties change, not their values, and a value fingerprint on the row covers the values anyway: a value that changed between the tick and the deploy stops the deploy, the row comes back with the change as it is now, and a comment asks you to look at it and tick again, without naming the value. A value the row does show, through
dashboard.showValues, is approved as shown. A new resource, a delete or a different property stops the deploy the same way. docs/security.md has the whole promise, and what to do about a value that differs on every run. - A row without a box that says
N policies failedbelongs to a stack whose change fails a policy of the repo (policies). The lines under it name each failed policy in its own words, at most five, and the preview page names them all. The box comes back with the first scan whose preview passes. A line that saysthe policies did not runmeans conftest could not judge the change, and the row keeps its box. - A refused tick deploys nothing. The box is cleared and one comment on the dashboard says why.
- A change that moved since the tick deploys nothing. The row shows the change as it is now, and one comment on the dashboard tells the person who ticked. A push to the stack that lands after the tick and before its deploy starts stops the deploy too: the row says deploying until the scan the refusal starts shows the change. When the fresh preview has nothing to deploy at all, because the stack was deployed from somewhere else, the deploy ends as a success that says so.
- A row under Drifted changed outside the code. With
drift.enabled, the daily scan also checks every stack against its real infrastructure. A stack whose code has nothing to deploy and whose real resources changed gets a row there, with a box: what changed, and what is gone. A tick deploys the code as it is, which puts it back, after the drift is checked again. A pending stack that also drifted shows its drift on its own row. The row’spreviewlink opens a page that lists the drift, and a stack entry can turn the check on or off for its own stacks. A row that saysdrift not checkedbelongs to a stack whose check failed, such as on an expired token: it is not known to have drifted, only not known not to. The warning “Drift check failed” on the run says why. - Updates waiting to merge, above Pending, are pull requests one tick merges and deploys. With
mergeAndDeploy.authors, a green pull request by an author on the list whose files a stack claims gets a row with a box. A line with no box under them, ending inwaits on its checks, is one that qualifies in every way but its checks, which have not all finished yet, such as Renovate’srenovate/stability-daysor a CI run still going. It gets its box once its checks are green, and it goes when a check fails or it stops qualifying. It names the stacks it would deploy, the title, and the pull request with its author. A pull request whose checks failed, or that would never qualify, is not shown: the job log of the scan says why, one line each. - To deploy every pending stack at once, tick
Deploy all N pending stacksunder the pending rows. That tick deploys nothing yet. The next run puts a confirm box in its place:Confirm: deploy all N pending stacks, with the stacks it would deploy (ten by name, then how many more) and who asked. Ticking the confirm box is what deploys them, anyone with write access may tick it, and it goes the same way as a tick on each row: the tick rule of every stack is checked on its own, a stack you may not tick is refused in the comment and the rest still go,dependsOnand phases still decide the order, and every stack gets its own deployment record, its own fresh preview and its own line on the trail, all under the name of the person who ticked the confirm box.Repair all N drifted stacksunder the drifted rows does the same for drift. A section with one row has no such box, since the row has its own, and there is none whiledeploysisfalseor on a read-only dashboard. Deploying rows get none: they are on their way already. - A confirm box is only good for the rows it names. When a stack joins or leaves the section, or a row gets a new diff, before anyone ticks it, the confirm box goes and the bulk box comes back with a note that says what changed. Tick again to confirm the rows as they are now. A confirm box nobody ticks is taken back by the second scan after it, with a note, and a tick on either box that no run picked up is cleared like any other.
- A line under the scan line that says
A scan is running since …means a scan of this dashboard’s workflow is under way: it wrote that line as its first act, before it previewed anything, and links its run. The rows above and below it are still the last scan’s until that scan writes the body, which takes the line away. A ticked rescan box is taken back in the same first write, so the tick is seen at once. A line whose time is long past belongs to a scan that died before it wrote the body: open the run, and the next scan replaces the line. Nothing is decided from it, and a tick made while a scan runs is judged as any other. - A line under the scan line that says a run of the dashboard’s workflow has been waiting for a runner means GitHub has kept that run queued for ten minutes or more: no runner took its job, so none of its work has started. It names how long, since when, and links the run, and counts any other runs that waited that long. A shorter wait says nothing, since a busy runner or one that is still starting is normal. The line only says the run waits, not why: open the run, and see self-hosted runners for what to check. It goes as soon as the run starts, or with the next scan once it ended. It never holds anything back, and only a run that did get a runner can write it: when no run of the workflow gets one, the scan line stops moving and nothing on the dashboard changes.
- A line right under the scan line that starts with
The last run of this dashboard's workflowmeans the newest run did not get to do its work: a step before Sluiceway failed, orsluiceway.yamlhas a problem, as the line says. The rows are the last scan’s. Open the run for why. The line goes with the next run that gets as far as Sluiceway (a run that failed). - A line under the scan line that says runs of the dashboard’s workflow failed since the scan before counts the runs that failed, timed out or never started between the scan before and the one the scan line shows, and links the newest. In that time no scan ran, so a scheduled drift check or a scan of a push may be missing. It goes with the next scan.
- The rescan box,
Rescan all stacksat the bottom, starts a full scan, for example after you deployed a stack from somewhere else. It deploys nothing. - A row under Preview failed could not be previewed, twice. A scan tries a failed preview once more after a short pause, so a registry or a network that fails once never reaches the dashboard. A row that stays has no box and links to the run, where the job log shows the tool’s own words right under the line that says the preview failed. Every scan tries it again.
- A row under Busy says
busy: another update holds the stack's lock. Another run of the tool was deploying the stack when the scan previewed it, on both tries. Nothing is wrong with the stack, the header does not turn failing and no job turns red. The next scan previews it, or tickRescan all stacks. - To try a failed deploy again, tick the box again. Re-running the job deploys nothing. The failure line goes once a deploy of the stack ends after it: one from the dashboard, or, for a Pulumi stack, one made outside the dashboard that a full scan found. The failed deploy stays in Recently deployed.
- Recently deployed, at the bottom, is the trail. Every deploy from the dashboard that ended, newest first: what went out, what found nothing to deploy, a rehearsal, and a failed deploy. Each is one line: the stack id, a short word for the result (
drift fixed,no changes,drift gonefor a drift repair that found the drift already put back,rehearsedorfailed, none for a plain deploy), who ticked it (merged by alicefor a stack that deployed on merge), the time without the year of the scan, and a link to the run. A failed deploy’s reason stays on its row. For Pulumi stacks it also lists deploys made outside the dashboard, from a laptop, a script or another pipeline, asdeployed outside the dashboard, from 59ff6e7with the time. Ten lines unlessdashboard.recentlyDeployedsays otherwise. Under a deploy from the dashboard that went out, a line says what it shipped since the stack’s deploy before it, in the words of a row’s line. - Times are in the repo’s zone. Every time on the dashboard is in UTC, or in the zone
dashboard.timeZonenames (configuration), the same for every reader. The line under Recently deployed names the zone,Times are in UTC.orTimes are in Europe/Brussels., and a time that stands alone, on the scan line, the waiting-run line or a failure line, says its offset:2026-09-21 08:52 UTC, or2026-07-21 12:02 UTC+2. - The layout is the repo’s choice. The order of the sections, whether Deploying, Drifted and In sync are shown, the zero counts, how much a pending row shows, the deploy all and repair all boxes, the rescan box and the footer are keys under
dashboardinsluiceway.yaml, fromdashboard.sectionson. Without them the dashboard looks as described here. A section that is off keeps its rows in one closed fold at the end of the sections, so no stack leaves the issue. Pending and Preview failed are always shown, and no setting hides a delete or replace line, a preview failure or a failure line.
Reading the job log
The job log of a scan says what it did, in fixed lines:
| Line | What it tells you |
|---|---|
This is a full scan: ... |
Every stack is previewed, and why. After a push it reads This is a full scan. A push gives a narrowed scan, and this one fell back to a full scan: ... with the reason. When the reason is a changed file that no stack claims, the group Changed files that no stack claims lists them. |
This is a narrowed scan: it previews 2 of 58 stacks and keeps the rows of the other 56 as they are. |
Only those stacks are previewed. One line per stack follows: <stack id> is previewed: it claims <file>. |
The pool is 2 previews at once, one for each core of this machine, which has 2. The concurrency input sets another size. |
How many previews run at the same time, and where the number came from: the cores of the machine, up to 8, or from the concurrency input when the step sets it. A machine that does not say how many cores it has gets a pool of 1, and the line says so. |
Previewing 58 stacks with a pool of 2 and a time limit of 10 minutes for each preview. |
The pool and the preview-timeout this scan ran with. |
Previewed <stack id> in 8.3 s: pending |
How long one preview took, and how it ended. One line per preview, in the order they finish. |
What the tool wrote for <stack id>: |
Under a preview that failed or found the stack busy: the tool’s last lines, at most 20, each behind the stack id. The group of the stack further down holds them all. |
2 previews did not work and are tried once more after a pause of 10 s: ... |
The second try. Previewed <stack id> again in 9.1 s: in sync follows for each. Only a preview that failed twice is a preview failure, and its warning says failed twice. |
Previewed 58 stacks in 412.6 s with a pool of 2. Added up, the previews took 1530.2 s. The slowest was <stack id> with 45.1 s. |
The total. The total against the sum shows what the pool gains. The slowest preview is what preview-timeout has to clear. A pool larger than the cores leaves the total about the same and makes every preview slower. |
Wrote the preview pages of 12 pending stacks on 0123456: 2 created, 10 updated. |
One page per pending stack this scan previewed. Without checks: write it reads No preview page was written: ... and says what to add. |
Wrote the dashboard: <url> (41,210 of 65,536 characters). |
Where the dashboard is, and how full the issue body is. Carried 56 rows through as they were follows on a narrowed scan. The line starts with the dot of the header state the scan wrote. |
Created the dashboard: <url> (3,120 of 65,536 characters). |
The same, on the first scan, which opens the issue. |
Reopened the dashboard and wrote it: <url> (...) |
The same, when the dashboard issue was closed: the scan opens it again, so its number and every link stay. |
#1137 is not listed to merge yet: its checks have not all finished. Its line has no box until they are green. |
With merge and deploy on, this pull request has a line under Updates waiting to merge, with no box, and gets its box once its checks are green. 1 pull request waits on its checks: #1137. counts them. The dashboard shows the oldest 10, and 2 more pull requests wait on their checks and are not listed: ... names the rest. |
#1137 is not listed to merge: its checks are not all green. |
A check of this pull request failed, or it has no checks at all, so it gets no line on the dashboard. The same line with another reason, such as no stack claims some of its files., names what keeps it off the list for good. A pull request whose author is not in mergeAndDeploy.authors gets no line at all. |
The open pull requests could not be read: ... |
With merge and deploy on, the scan could not read the open pull requests, so no update is listed that was not listed before. It is a warning on the run, “Open pull requests not read”, and a line under the counts of the summary. It names the permission the job lacks when GitHub’s refusal says which, and otherwise gives GitHub’s words. The scan goes on. |
The dashboard says a scan is running, under the scan line, until this scan writes the body (record 0108): <url> |
The scan’s first write, before any preview. The dashboard could not say a scan is running: ... says why when that write failed, and the scan goes on. A first scan, which has no dashboard yet, says nothing here. |
Run 900 of deploy-dashboard.yml has been waiting for a runner for 32 minutes. The dashboard says so under the scan line until it starts (record 0086): <url> |
A run of this workflow has been queued for ten minutes or more, and the dashboard has a line for it. 1 more run of it waited 10 minutes or more too. counts the others. When the scan cannot read the runs, The queued runs of deploy-dashboard.yml could not be read: ... says why, the scan goes on and the dashboard has no such line. |
3 runs of deploy-dashboard.yml failed since the scan before this one. The dashboard says so under the scan line until the next scan (record 0120): <url> |
Runs of this workflow failed, timed out or never started since the scan the dashboard showed before, and the dashboard has a line for them that links the newest. When the scan cannot read the runs, The runs of deploy-dashboard.yml that ended could not be read: ... says why, and the scan goes on. |
The dashboard says this run failed before Sluiceway ran, under the scan line, until a run gets as far as Sluiceway (record 0120): <url> |
A step before the Sluiceway step failed, and the step ran with if: ${{ !cancelled() }}. It did none of its work, after An earlier step of this job failed, so this step does not run auto mode. On a problem in sluiceway.yaml it reads ... found a problem in sluiceway.yaml ..., before the problem itself. The dashboard could not say this run failed: ... says why when it could not. |
Cleared an orphan tick on the box that deploys all pending stacks: ... |
The same for the bulk box or its confirm box: nobody picked the tick up, and the bulk box asks for a fresh tick. It reads Left the tick on ... alone: ... while a run that an issue edit started is on its way. |
Took back the confirm box of the pending stacks: ... |
Nobody ticked the confirm box before the second scan after it, or its rows changed, as the line says. The bulk box is back, with a note, for a fresh tick. |
<stack id> deploys on merge: deployment record 12 is queued with diff hash ..., merged by alice, and handed to apply. |
The scan of a push to the default branch found a stack set to on-merge pending, and the same run deploys it. ... is queued behind <stack id>, and a later run starts it. is a stack whose dependency goes out first. <stack id> deploys on merge, and this change waits for a tick: ... is one that did not go, with the reason its row shows. |
<stack id> costs about 31.20 USD more a month. |
The cost estimate of the stack’s change (cost.enabled), as its row shows it. The cost of <stack id> was not estimated: ... says why a row has no cost line, and the run carries the warning “Cost not estimated”; the Infracost CLI’s own words are in the stack’s group. |
Cleared an orphan tick on <stack id>: ... |
A box was ticked and nothing picked the tick up, so the scan cleared it and the row asks for a fresh one. A scan never deploys a tick it finds. While a run that an issue edit started is queued or in progress the line reads Left the tick on <stack id> alone: ... and the box stays ticked. |
<stack id> is ticked outside its deploy window, which opens 2026-09-28 09:00 UTC+2. Its deployment record waits for the window, and a run inside the window starts it. |
The stack has a deploy window and the tick came outside it. The row says queued for the deploy window, which opens ... until a resolve inside the window starts it, whose log says <stack id>: its deploy window is open, so it starts now. A run outside it says <stack id> waits for its deploy window, which opens ... and starts nothing. |
<stack id> is ticked during a deploy freeze. Its deployment record waits for the end of the deploy freeze (Year-end freeze) at 2027-01-05 00:00 UTC+1, and the first run after that starts it. |
The repo has a deploy freeze that holds now. The row says queued for the end of the deploy freeze ..., and the line under the scan line names the freeze while it holds. Nothing passes a freeze, a destroy included; the first resolve after the end starts it, and with a deploy window too, the first run when both allow. |
The job log of the run a tick starts ends its resolve part with one line that says where its time went: Resolve took 4.1 s: reading the dashboard 0.4 s, reading the config 0.0 s, discovery 0.1 s, reading deployment records 0.3 s, judging ticks 0.6 s, opening records 0.5 s, writing the body 2.2 s. Starting the action took 0.3 s before that. A part that did not run is left out, and the rest is the time between the parts. It is the part of the wait between a tick and waiting to start that Sluiceway owns: the rest is GitHub starting the run, finding a runner and the steps before Sluiceway’s.
Under those lines there is one group per previewed stack, titled with the stack id. It holds the whole diff and everything the tool printed. The tool’s own words never leave the job log.
To see the values a tick would deploy, turn on scan.logDiff in sluiceway.yaml. Every pending stack’s group then also holds the tool’s own diff, values included, and the stack’s preview page says it is there. The page itself never shows a value. It costs one more tool run per pending stack, and anyone who can read the repo can read its job logs: in a public repo, anyone. docs/configuration.md and docs/security.md say what to weigh first.
Limits
- A change that touches only a stack’s outputs is not shown. The tool’s preview does not report it, so a stack whose only change is an added, removed or changed output is in sync and has no box. Deploy it from outside Sluiceway. Another stack that reads that output keeps failing its preview until then. When an output changes together with a resource, the row is pending anyway.
- Deploys from somewhere else are allowed. A full scan reads Pulumi’s own history of each stack and lists every deploy there that did not come from the dashboard under recently deployed, with when and from which commit. It never says who: Pulumi’s history does not record the person, and the commit’s author is not who deployed it. OpenTofu, Helm and kubectl keep no such history, so their deploys from somewhere else are not listed. A row they made stale stays pending until the next full scan or the rescan box, and a tick on a stale row deploys nothing.
- Sluiceway only previews, checks for drift and deploys. Destroying a stack, a refresh that writes the state and repairing state stay with your own tooling. The drift check is opt-in, for Pulumi and Helm so far, and never changes the state (
drift.enabled). - No values on the dashboard unless you list their paths. Rows show resource types, resource names and property names.
dashboard.showValueslets the old and new value of the paths you list appear, such as a chart’sversion, and a tick then approves that value. Every other value is covered by a fingerprint on the row, sixteen hex characters that name nothing.dashboard.redact: truekeeps names and values out of the issue. The job log holds the tool’s own values only when you turn onscan.logDiff. - Pulumi, OpenTofu, Terraform, Helm and Kubernetes manifests so far, with Terragrunt or CDK for Terraform in front of OpenTofu or Terraform. OpenTofu and Terraform root modules are found from their files when the files say plainly that they are root modules, in the default workspace. Every other OpenTofu and Terraform stack, Helm releases and Kubernetes manifests are declared in
sluiceway.yaml(configuration). Kubernetes manifests are pruned only with thepruneoption: without it an object taken out of them stays in the cluster. An OpenTofu preview runs with-refresh=false: like Pulumi’s, it compares the code with the state and never reads every real resource. A Helm preview compares the chart with the release helm stored, not with what runs in the cluster: a change made there by hand shows only through the drift check. A chart that renders differently every time cannot be deployed from the dashboard. A Kubernetes manifests preview is a server-side dry run against the live objects, so a change made in the cluster by hand shows as pending too.
later.md lists everything that was left out of this version, and why.