Deployment Checks let you gate a deployment with validations that run either before the build pipeline starts or after every server has received its files. They are designed for the three questions you ask most often around a deploy:

- **Before the build runs**: is it safe to deploy right now? (No uncommitted changes on the target server, no maintenance flag set, no in-flight migration, etc.)
- **After the deploy finishes**: did it actually work? (Site returns 200, healthcheck endpoint reports healthy, smoke command succeeds.)
- **Is the code itself safe to ship?**: are there known vulnerabilities in the dependency tree, or any leaked secrets in the repository?

> Deployment Checks are currently in beta. Enable beta features on your account to see the "Deployment Checks" entry in the project sidebar.

## How a check fits into a deployment

Every check has a **stage** and a **type**.

**Stages**:

- **Pre-build** – runs at the very start of a deployment, before the build pipeline begins. A failing pre-build check **aborts the deployment cleanly**, before any build resources are spent. This is the right place to detect things like uncommitted changes the customer made directly on the server, or a maintenance flag that says "don't deploy right now".
- **Post-deploy** – runs after every server has received its files, before the deployment is marked complete. A failing post-deploy check **marks the deployment as failed** and fires the usual failure notifications (email, Slack, webhook). The files stay on the server – Deployment Checks do not roll the deploy back, they alert you so you can react.

**Types**:

- **SSH** – runs an arbitrary command on one or more SSH-capable servers in the project. A non-zero exit aborts (pre-build) or fails (post-deploy) the deployment. Output is captured into the deploy log in a console-style block.
- **HTTP** – sends a `GET` or `HEAD` request to a URL and checks the response status (and, optionally, that the body contains a specific substring). Use this for maintenance-flag endpoints, public healthchecks, or any URL-based signal.
- **Vulnerability scan** – runs a security scanner (**Trivy** for dependency CVEs, **TruffleHog** for leaked secrets) against your source on the build server. A finding at or above the configured severity threshold aborts (pre-build) or fails (post-deploy) the deployment. See [Vulnerability scan checks](https://www.deployhq.com/support/deployments/deployment-checks/vulnerability-scan-checks) for setup, scanner options, severity thresholds, and the findings table.

SSH and HTTP checks run from the deployment worker. The SSH type only applies to projects with at least one SSH-capable server; HTTP checks work for any project type – including FTP, ElasticBeanstalk, Heroku, and the cloud-storage protocols – because nothing runs on the target server. Vulnerability scans run on the DeployHQ build server against the checked-out source, so they need a build pipeline to be configured on the project.

## Worked examples

### Detecting drift before deploying

A common case for Drupal, WordPress and similar CMS sites: an operator edits a file directly on the server (a quick config tweak, a hotfix on a plugin) and forgets to push it back into git. The next deploy would overwrite their change.

A pre-build SSH check that runs against the existing release directory will catch this:

```
git diff --quiet && git diff --cached --quiet || exit 1
```

If the working tree on the server is clean, both `git diff` calls exit 0 and the deploy proceeds. If anything is uncommitted, the check exits non-zero and the deploy is aborted before the build kicks off – no wasted build minutes, no overwritten changes.

### Maintenance-mode flag

If you publish a `/maintenance.json` endpoint that returns 200 when maintenance is on, a pre-build HTTP check can refuse to deploy on top of it:

- **Method**: GET
- **URL**: `https://example.com/maintenance.json`
- **Expected status**: 404

If the maintenance flag is up, the endpoint returns 200, the expected 404 doesn't match, and the deploy aborts. When maintenance is taken down, the endpoint 404s, the check passes, and deploys resume.

### Post-deploy smoke test

A post-deploy HTTP check that hits your homepage with an expected `200` is a cheap canary against a broken deploy. Pair it with a body match (e.g. `"healthy"` from a `/healthcheck` endpoint) if your app exposes a richer signal.

## Server scoping

Both SSH and HTTP checks can run on every applicable server in the project, on a specific subset, or on the servers in a server group. The scoping uses the same picker as pre/post-deployment commands, so it should already feel familiar.

For HTTP checks specifically, scoping to a server is what lets the URL field expand template variables against that server's context. For example, a check with the URL `https://%environment%.example.com/health` scoped to a staging server and a production server will hit `https://staging.example.com/health` and `https://production.example.com/health` respectively – one HTTP request per server, one result per server in the deploy log.

A few things to know:

- An HTTP check left with the default project-wide scope runs once per deploy, exactly like before. Template variables that depend on a server (`%environment%`, `%release_path%`, `%current_path%`) won't expand without a server scope.
- If you create an SSH check on a project that has no SSH-capable servers (for example, an FTP-only or Heroku-only project), the check will be marked as **skipped** on every deploy – it shows up in the deploy log as a neutral, grey note rather than as a green pass or a red failure. The same applies to HTTP checks scoped to identifiers that no longer exist in the project.
- The pre/post-deployment commands you have today still work exactly as before. Deployment Checks sit *outside* the file transfer phase – pre-build runs before the build server is touched, and post-deploy runs after the last server's transfer completes.

Vulnerability scans don't use the server picker — they run once per deploy against the checked-out source. See [Vulnerability scan checks](https://www.deployhq.com/support/deployments/deployment-checks/vulnerability-scan-checks) for the full configuration surface.

## Template variables

Both SSH commands and HTTP URLs support the same template variables as pre/post-deployment commands. The variables most useful for checks are:

- `%current_path%` – the active release directory on the target server
- `%release_path%` – the directory of the release being deployed
- `%branch%` – the branch being deployed
- `%startrev%` / `%endrev%` – the SHAs that bound the deploy

For example, `cd %current_path% && ./scripts/config-check.sh` runs a config-validation script that lives in your repository.

## Timeouts and output

Every check has a per-check timeout. SSH and HTTP checks default to 30 seconds; vulnerability scans default to 300 seconds because scanners need time to walk the dependency graph. Every check type shares the same hard cap of 10 minutes. A check that doesn't finish in time is treated as a failed check.

Output is bounded:

- SSH check output is capped at 1MB per execution. Anything beyond that is truncated and the deploy log notes the truncation.
- HTTP check responses capture up to 64KB of the response body.
- Vulnerability scan output is capped at 1MB, with the findings table capped at 50 rows. See [Vulnerability scan checks](https://www.deployhq.com/support/deployments/deployment-checks/vulnerability-scan-checks) for the findings layout.

## What fires when a check fails

**Pre-build failure**:

- The deployment is aborted before any of your build commands run. For SSH/HTTP checks the build server isn't touched at all; for vulnerability scans the build server checks out the source to run the scanner, but your build pipeline never executes.
- The deployment is marked as failed.
- Existing failure notifications fire (any integration with "send on failure" enabled).

**Post-deploy failure**:

- All transfers have already completed by this point. Files stay on the server.
- The deployment is marked as failed.
- Existing failure notifications fire.

If you only want to be notified about *check* failures (and not all deployment failures), the simplest pattern today is a dedicated integration scoped to failure notifications. Per-check-type notification events are on the roadmap.

## API

Deployment Checks are managed through the standard project API alongside build commands and pre/post-deployment commands. Each project exposes `/projects/:project_id/deployment_checks` for full CRUD. See the API reference for the request and response shapes.

## Related

- [Vulnerability scan checks](https://www.deployhq.com/support/deployments/deployment-checks/vulnerability-scan-checks) — Trivy and TruffleHog setup, severity thresholds, scan target, and the findings table.
