---
title: Deployments and previews
description: From a push to a running service, rolling back, promoting, and an environment per pull request.
order: 3
---

## A deployment

A push to a branch an environment follows starts a build for every service that builds
from the repository. **Deployments** lists them, and each one shows its steps:

| step | what happens |
| --- | --- |
| clone | the commit is fetched through the GitHub App |
| checks | ops waits for the commit's GitHub checks to pass |
| build | the image is built from the service's Dockerfile |
| scan | the image is scanned for known vulnerabilities |
| push | the image is stored in ops's registry |
| sign | ops signs the image; only signed images are deployed |
| deploy | the service is updated to the new image |
| health | ops watches the rollout until it is healthy |
| docs | in production, when the project file names a `docs` folder, it is published; see [Publish docs](../publish/docs.md) |

The `docs` step runs after the service is healthy, and a broken docs folder fails only that
step: the deployment stands and you get a notification.

Builds run one at a time. The commit on GitHub shows a deployment status and a check run,
so the pull request says what is live.

**Which checks count.** By default a build waits for the checks the branch protection
requires, or, where GitHub cannot say, for every check on the commit. List them yourself
with `checks` in [the project file](project-file.md); `checks: []` waits for none. A check
that fails refuses the build, and one that never reports times out after two hours.

**Healthy** means every replica is running and stays running for the service's health
window, 120 seconds unless you set `deploy.health_window_secs`. Give the image a
`HEALTHCHECK` so a container that starts but cannot serve counts as unhealthy.

## Rollback

A rollout that does not come up healthy is rolled back to the newest healthy image
automatically, and you get a notification. To go back by hand:

```sh
ops rollback web                          # the newest healthy image before this one
ops rollback web --digest sha256:4f1c...  # a specific image
```

A deployment's page has **Roll back now** while its rollout is unhealthy.

## Deploying by hand

`ops deploy` puts a build that already exists on a service, without a push:

```sh
ops deploy web --commit 7c1d0e2            # the newest build of that commit
ops deploy web --build 412                 # a build by its id
ops deploy web --digest sha256:4f1c...     # an image by its digest
```

## Promote

Promote moves the exact images of one environment into another, with no rebuild. It is
how a release goes from staging to production when production should run what staging
was tested with:

```sh
ops promote --from staging --to production --dry-run   # what would change
ops promote --from staging --to production
```

The console has the same under **Settings**, **Promote**.

## Previews

When a project has a staging or development environment, every pull request gets a preview
environment of its own, `preview-<n>`, with its own hostname. It is built from the pull
request's branch and updated on each push to it. Ops posts one comment on the pull request
with the address, the database, the build and the expiry, and keeps that comment current.

- A preview copies its services and domain from the environment that follows the pull
  request's base branch, with one replica each.
- Its variables are copied from staging, else development, and never from production.
- A database that keeps a template gets a fresh copy of that template in the preview; a
  database without one is not in the preview. A SQLite database starts empty. Keep only
  seed data in a template, see [Databases](../resources/databases.md).
- A pull request from a fork never gets a preview.
- A preview is removed when the pull request closes, or three days after its last push.

The number of previews and how long they last are set in **Settings**, **Previews**. When
the limit is reached, the pull request says so instead of getting one.
