# Preview environments

A preview is a throwaway copy of a project at its own addresses: to try a pull request, or any change, without touching the real thing.

## What's copied

- Every service, with its settings, variables and secrets.
- Services built from code start with the code the original runs (or, if nothing of it is live, its newest push), built again with the copy's own variables. A static site's files are built the same way, so its pages point at the copy's services, not the original's.
- Volumes: new and **empty** (see [data to try it with](#data-to-try-it-with)).
- Release commands: the copy runs them too, so its database gets the original's migrations.
- Custom domains: not copied.
- Each service runs on one server, without autoscaling.

## Data to try it with

A preview's databases start empty. Give the project a seed command in [alf.toml](/docs/alf-toml#previews):

```toml
[previews]
seed = "npm run db:seed"
```

Every new preview runs it once, after its app is first live (and its migrations have run): the app's own seed script, with made-up data rather than anybody's real data. `alf previews` shows each preview's seed: waiting for its app, running, done, or failed with its exit code (a failure is also sent where deploy failures go).

## Create one

In a project, **+ Create → Preview environment**. Optionally give it a **name** (like `pr-12`), and choose how long to keep it: 6 hours to a week. Coding agents can create previews too.

Then push code to it like to any project: its services have their own **Deploy new code**, and the deploy command takes `--preview <name>`.

## A preview per pull request

In CI, `--preview <name>` does both steps: it deploys to the project's preview of that name, and creates the preview the first time. `--end-preview <name>` ends it when the pull request closes. The [workflow for GitHub Actions](/docs/deploying-code#a-preview-for-every-pull-request) is ready to copy, and [`alf ci github`](/docs/cli#deploying-from-github) writes it for you, with a step that comments the preview's addresses on the pull request: one comment, updated with each push, and saying when the preview ended. It uses the job's own token, so there's nothing to install on GitHub. (`deploy.sh` hands the addresses to the steps after it as the output `urls`, one "service address" per line, for a workflow of your own.)

## Finding your previews

- The sidebar shows each project's previews beneath it.
- In a project, its name has a menu to go through it and its previews; **All previews** at its foot lists every one, under the project each copies, with when they go, **Keep 24 h longer** and **End**.
- A preview's page says it's a preview, links to the original, and lets you keep it longer or end it.

Anyone in the organization can end a preview. When its time is up, it's deleted with its services and their data.

## Environments

A preview is a copy that expires, because CI makes one per pull request and nobody would clear
them up. An **environment** is the same copy without the expiry: a staging you deploy to first,
beside the production everyone uses. Nothing deletes it.

Make one from a project's **Environments** page, in its sidebar. It gets the same services, settings,
variables and secrets, its own addresses, and volumes that start empty. Custom domains stay with
the original — point your own at the environment if you want one.

### Databases and buckets

A copy — preview or environment — has everything the original has, its own of each:

- **Databases** are copied as databases: the same engine, the same settings and credentials (so an
  app that wrote its database URL out in full still reaches it), and **no data**. An environment's
  database is backed up on the original's schedule; a preview's, which is thrown away, isn't.
- **Buckets** are made again under the same name, each with a key of its own, and **empty**.
- An app's references follow: `${{postgres.DATABASE_URL}}` and `${{uploads.S3_BUCKET}}` find the
  copy's database and bucket, never the original's. Production's data stays in production.

To give a copy some data, restore a [snapshot](/docs/backups) you downloaded, or seed it the way
you would a new database.

### Releases

**Record what's running** writes down the version every service in an environment is on, under a
name. Open a release to see exactly what it holds, and **Promote** puts those same versions into
another environment. **Forget it** throws the record away; whatever was promoted from it carries
on running.

Only the versions move. The other environment's variables, secrets, domains and volumes are its
own and aren't touched — that's the point, so production keeps pointing at the production
database. A promotion is refused unless every service in the release exists there, so it can't
half happen and leave a mixture running.

A service built from pushed code is **built again** from the same commit rather than running the
image that was tried, because an image belongs to the service that built it. That's the same
source, not the same binary; the answer says which services were rebuilt. If that matters, build
in CI and deploy a registry image instead.
