# Previews

Deploy a branch as its own copy of the app — its own process, its own database, its own hostname — and remove it when the branch is done.

### Syntax

```
$ vela deploy -t preview
```

```
$ vela deploy -t preview:feature/maps
```

Bare `preview` previews the branch checked out in the working tree. `preview:<branch>` names one explicitly, which is what you need on a detached HEAD or in CI. Only `preview` takes a `:branch` suffix; `-t staging:main` is an error.

### What a preview is

A preview is a complete instance on the server, exactly like production or [staging](/deploy): its own Node process, its own PocketBase database and uploads, its own internally assigned port. Nothing is shared with production or with other previews, so a migration or a data change on a branch cannot touch the live site. Every command that takes a target accepts a preview:

```
$ vela status -t preview:feature/maps
$ vela logs -t preview:feature/maps
$ vela env set -t preview:feature/maps STRIPE_SECRET_KEY
```

### Hostnames

A preview gets a free hostname from velastack.dev, named after the project's subdomain and the branch:

```
<project>--<branch>.velastack.app
```

`feature/maps` becomes `feature-maps-1a2b3c`: lowercase, anything that isn't a letter or digit collapsed to a hyphen, and a short hash of the full branch name whenever that changed anything, so `feature/maps` and `feature-maps` can never share an instance. A branch that already is lowercase letters, digits and hyphens keeps its name as it is. The name is printed at the end of the deploy and goes live within a minute. See [Domains](/deploy/domains) for how these names are chosen and served.

For that the project needs to be [linked](/link) and the CLI logged in, with [`vela login`](/account/login) or `VELA_API_KEY` in the environment. A preview that nothing routes to stops before it builds:

```
Nothing routes to this preview.

Previews get a free velastack.app hostname from velastack.dev: `vela link` the
project and `vela login` (or set VELA_API_KEY). Or pass `--domain <host>`
with DNS of your own pointing at root@example.com.
```

Previews never inherit production's domain — that is production's alone. To serve previews on your own domain as well, add a preview base such as `preview.example.com` on the project's Domains page; each branch is then also served at `<branch>.preview.example.com`.

### Server

Previews of a project all run on one server. The first `-t preview` asks which, and records it under `preview` in `.vela/project.json`, next to `production` and any named targets. Every branch after that lands on the same machine, so commit the file as usual. Each preview is a full instance, so a server hosts as many as it has memory for.

[`vela targets`](/targets) shows that shared binding as one `preview` row rather than a row per branch. To see what is actually running, ask the server:

```
$ vela status --all
```

### Removing a preview

```
$ vela destroy deployment -t preview:feature/maps
```

This stops the services, deletes the releases and retires the hostname, which stops answering at the edge. The database and uploads stay on the server unless you pass `--purge`, which snapshots them into the server's trash for two weeks before deleting; a preview is the one target `--yes` is enough to purge. A pull request closed through the action purges. See [`vela destroy deployment`](/destroy/deployment).

Previews are not pruned by age. Remove them as branches are finished, or let a pull request do it for you.

### From pull requests

[`velastack/action`](/helpers/github-action) deploys `preview:<branch>` for every pull request, keeps one comment on the pull request up to date with the URL, and removes the preview, data included, when the pull request closes:

```yaml
on:
  push:
    branches: [main]
  pull_request:
    types: [opened, synchronize, reopened, closed]

permissions:
  contents: read
  pull-requests: write

jobs:
  deploy:
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v6
      - uses: velastack/action@v1
        with:
          server: root@your-server
          ssh-key: ${{ secrets.SSH_PRIVATE_KEY }}
          api-key: ${{ secrets.VELA_API_KEY }}
          domain: example.com
```

The action derives the target from the event: a push to `main` deploys production, a pull request deploys its branch. `VELA_API_KEY` is what gives previews their hostname; create one at [velastack.dev/api-keys/new](https://velastack.dev/api-keys/new). Pull requests from Dependabot or from forks run without secrets, so they get no preview; the [action page](/helpers/github-action) shows how to skip them, and how to keep the cleanup running when a branch's tests fail.

### On velastack.dev

Every preview shows up on the project's Deployments page with its branch, and on the Domains page under Previews, with the hostnames it is served on. Destroyed previews drop off the Domains page and stay in the deploy history.