Skip to content

Drift detection

Check a workspace on a schedule for infrastructure changes made outside StackShip, get an email when it drifts, and revert the drift with one approved run.

Updated View as Markdown

Drift is a difference between your real infrastructure and the workspace’s state: someone changed a resource in the cloud console, a script edited a tag, or a resource was deleted by hand. Drift detection checks for it on a schedule, so you find out without anyone pushing a commit.

What a check compares

A drift check is a refresh-only plan. It reads every resource in the workspace’s state from your cloud provider and compares what it finds with what the state says.

  • It doesn’t compare your configuration with your infrastructure, so a newer module version, a data source such as “latest AMI”, or timestamp() never shows up as drift.
  • It never applies, never writes state, and never locks state.
  • It never posts a GitHub check or pull request comment.

The check appears in the run list with the kind Drift check. Its run page shows each resource that drifted, with the attribute-level differences, in the same plan view as any other run. A check that finishes reads Plan completed, whether or not it found drift.

Turn on drift detection

  1. Open the workspace and select the Drift tab.
  2. Choose a Frequency. For Daily and Weekly, choose the Hour (UTC). For Weekly, also choose the Weekday.
  3. Select Save.

When you first turn drift detection on, the first check starts within about 5 minutes. The tab then shows when the next check is due.

Saving the same settings again doesn’t move the next check. Changing the frequency, hour, or weekday moves the next check to the new time; it doesn’t start one right away.

To stop checking, select Turn off. The workspace’s drift status is hidden until you turn drift detection on again. Past drift checks stay in the run history.

Frequency and timing

Frequency When checks run (UTC)
Every 6 hours 00:00, 06:00, 12:00, and 18:00
Every 12 hours 00:00 and 12:00
Daily The hour you choose
Weekly The hour you choose, on the weekday you choose

All times are UTC. Each workspace adds its own fixed minute, from 0 to 59, so a check starts up to 59 minutes after the hour shown, at the same minute every time. This spreads checks across your organization rather than starting them together. The most frequent schedule is every 6 hours. A new schedule defaults to Daily.

What a check uses

Configuration. A check uses the configuration of the workspace’s latest successful plan-and-apply run. If the workspace has never applied through StackShip (for example, you moved its state in with tofu state push), it uses the configuration of its latest plan that finished. Speculative plans from pull requests are never used. Until the workspace has one of these, a scheduled check is skipped and waits for its next scheduled time.

Variables and credentials. A check gets the same workspace variables, secrets, and cloud credentials as a plan-only run in the workspace. If the workspace has no cloud credentials for plans, every check fails; see cloud access.

Checks never block your runs

A drift check doesn’t count toward the workspace’s or organization’s active runs, doesn’t lock state, and doesn’t hold the workspace. A push, a tofu CLI run, or an API run starts exactly as it would with no check running.

When the workspace is busy with another run, StackShip skips the scheduled check and tries again about 15 minutes later, rather than making your run wait. Only a limited number of drift checks run at once, both in your organization and in each execution region; a check over either limit also tries again about 15 minutes later.

Drift status

While drift detection is on, the workspace shows a drift status on its overview and in the workspace list.

Status Meaning
No drift The latest check found no differences, or the current state was written by a StackShip apply, which reads real infrastructure as it runs
Drift detected The latest check found differences. The badge shows how many resources would be added, changed, and destroyed to bring state back in line, such as +0 ~2 −1
Check failed The latest check stopped before its plan finished, for example because the workspace has no cloud credentials. Open the check to read the error
Checking A check is running
Not checked yet No check has finished against the current state yet

A check only counts against the state it read. When an apply or a tofu state push writes new state, the status starts over: No drift after a StackShip apply, Not checked yet after a state push, until the next check finishes.

Why a check didn’t run

When a scheduled check is skipped, Not checked yet and the Drift tab show why:

Reason What it means
Runs once the workspace has a plan or apply There’s no configuration to check yet. The check waits for its next scheduled time. Push to the tracked branch or run tofu plan
Waiting for the active run to finish Another run, state upload, or recovery is in progress. The check tries again in about 15 minutes
Waiting for capacity Your organization, or the workspace’s execution region, is already running as many drift checks as it can at once. The check tries again in about 15 minutes
Runs are paused StackShip isn’t starting new runs right now. The check tries again in about 15 minutes
Cloud credentials need attention A credential profile on this workspace isn’t verified, or isn’t allowed to plan. Verify the profile or allow it to plan; the check tries again at its next scheduled time

Email when drift appears

When a workspace drifts, StackShip emails every owner and admin in your organization with a verified email address. Deactivated users get no email. The subject is Drift detected in followed by the workspace name. The email says how many resources changed and has a Review drift link to the check. It never includes resource addresses or attribute values.

Each person gets one email per drifted state. While the workspace stays drifted against the same state, later checks send nothing. Once an apply or state push changes the state, new drift sends a new email. There’s no way to turn these emails off yet.

Revert drift

When the status is Drift detected, the workspace overview shows Revert drift. It starts a normal plan-and-apply run from the same configuration the check used (not the latest commit on the tracked branch) and opens it.

The plan shows what the apply would change to undo the drift. Review it, and approve it like any other run; see run approvals. If the workspace is busy with another run, Revert drift shows why the run couldn’t start.

To keep a change someone made outside StackShip instead, update your configuration to match and push it.

Permissions

Role Can do
Member See drift status and the Drift tab settings; select Revert drift if they can start runs
Admin or owner Everything a member can, plus turn drift detection on, change its schedule, and turn it off. Approve the revert run

Changes to a schedule are recorded in the audit log as workspace.schedule.updated and workspace.schedule.deleted.

Manage drift detection with the API

Request Description
GET /v1/workspaces/{workspace_id}/schedule Read the schedule, with next_run_at and last_skip_reason. Returns 404 when drift detection is off
PUT /v1/workspaces/{workspace_id}/schedule Turn drift detection on or change it. Send cadence (every_6_hours, every_12_hours, daily, or weekly), hour_utc (0–23, for daily and weekly), and weekday (0–6, Sunday is 0, for weekly). Leave the others null. Requires an Idempotency-Key
DELETE /v1/workspaces/{workspace_id}/schedule Turn drift detection off. Requires an Idempotency-Key
GET /v1/runs?kind=drift_check List drift checks. An unknown kind returns 400 RUN_LIST_KIND_INVALID
curl -X PUT https://api.stackship.run/v1/workspaces/ws_9x2p/schedule \
  -H "Authorization: Bearer $STACKSHIP_TOKEN" \
  -H "Idempotency-Key: $(uuidgen)" \
  -H "Content-Type: application/json" \
  -d '{"cadence": "daily", "hour_utc": 6, "weekday": null}'

Every workspace object includes drift: the status, checked_at, run_id of the check, its configuration_version_id, the add, change, and destroy counts, and skip_reason. It’s null while drift detection is off. To revert drift through the API, send POST /v1/runs with kind: "plan_and_apply" and that configuration_version_id.

You can’t start a drift check with POST /v1/runs; only a schedule starts them. AI agents can read and set schedules too; see MCP server.

Next steps

Navigation

Type to search…

↑↓ navigate↵ selectEsc close