---
title: "Deploys"
description: "Deploy from your machine or from GitHub Actions with a per-app token, and roll back."
canonical_url: "https://wervt.app/guides/deploys"
---
# Deploys

> Deploy from your machine or from GitHub Actions with a per-app token, and roll back.

`wervt deploy` builds the app, uploads it to the control plane, migrates its database and
activates the new release. Each release records what was deployed: the commit, branch, commit
message and who deployed it, from git or from GitHub Actions. The dashboard's **Deployments** page
and `wervt releases <app>` show them.

```bash
pnpm exec wervt deploy                 # from the app's directory
pnpm exec wervt releases my-app        # newest first, with commit and author
pnpm exec wervt rollback my-app        # the release before the active one
pnpm exec wervt activate my-app <id>   # any kept release
```

The last 5 releases are kept. Rolling back doesn't revert migrations: an older build must work with
the current schema.

## A release that doesn't start

After activating a release, the deploy starts it and sends it one request, the same way the app
starts for its first visitor. If it doesn't start (an error while the server loads, a missing
module, no answer within 60 seconds, or a 5xx answer), the app goes back to the release before,
and the deploy fails with the error:

```text
▲  Release 20261006213058-7dc906 didn't start
│
│  InvalidWorkerResponse: event loop error: Error: boom at startup
│
●  my-app is back on 20261006212843-58e25c.
```

`wervt deploy` exits with 1, so a GitHub Actions run fails too. The failed release stays in the
list, marked **Didn't start** on the **Deployments** page with its error. Its migrations stay
applied, like with a rollback. A first deploy has nothing to go back to, so it stays active and the
deploy fails.

The check covers starting, not every page: a release that starts but breaks one route still goes
live.

## From GitHub Actions

Apps created with `create-wervt` come with `.github/workflows/deploy.yml`, which deploys on every
push to `main`. It needs a **deploy token**: it can deploy its app and read its info, and nothing
else (no env, access, database, logs or other apps).

1. Create a token, in the dashboard (app > **Deployments** > **Deploy tokens**) or with the CLI:```bash
wervt tokens my-app --create github
```

<br />

The token is shown once. wervt only stores its hash.
2. In the GitHub repository, under **Settings > Secrets and variables > Actions**, add the token as
the secret `WERVT_TOKEN` and the control plane URL as the variable `WERVT_URL`.
3. Push to `main`. The run shows up as a `production` deployment on GitHub, linked to the app, and
the release links back to the run.

```bash
wervt tokens my-app                    # list: id, name, created, last used
wervt tokens my-app --revoke <id>
```

For an app created before the workflow existed, copy `.github/workflows/deploy.yml` from a new app
(`pnpm create wervt tmp --no-install`), add `@wervt/cli` as a dev dependency and set
`packageManager` in `package.json` (the workflow installs that pnpm version).

## Tokens

<table>
<thead>
  <tr>
    <th>
      Token
    </th>
    
    <th>
      Can
    </th>
    
    <th>
      For
    </th>
  </tr>
</thead>

<tbody>
  <tr>
    <td>
      Admin
    </td>
    
    <td>
      everything
    </td>
    
    <td>
      the dashboard
    </td>
  </tr>
  
  <tr>
    <td>
      CLI session
    </td>
    
    <td>
      everything, revocable per machine
    </td>
    
    <td>
      you, via <code>
        wervt login
      </code>
    </td>
  </tr>
  
  <tr>
    <td>
      Read
    </td>
    
    <td>
      app info, releases and logs of every app
    </td>
    
    <td>
      agents debugging an issue
    </td>
  </tr>
  
  <tr>
    <td>
      Deploy (app)
    </td>
    
    <td>
      deploy one app and read its info
    </td>
    
    <td>
      CI
    </td>
  </tr>
</tbody>
</table>

## Signing in the CLI

```bash
wervt login --url https://api.example.com
```

The CLI shows a one-time code and opens the dashboard's **CLI** page. Enter the code there and
approve: the machine gets its own admin session, saved in `~/.config/wervt/config.json`. The
dashboard lists signed-in machines and signs them out. Codes expire after 10 minutes, and the code
is typed in rather than sent in a link, so a login can't be approved by clicking a link someone else
sent. `wervt login --url … --token …` saves a token directly (a read token, or for scripts).


## Sitemap

See the full [sitemap](https://wervt.app/sitemap.md) for all pages.
