---
title: "Local stack"
description: "Run the wervt services with Docker Compose."
canonical_url: "https://wervt.app/self-hosting/local-stack"
---
# Local stack

> Run the wervt services with Docker Compose.

```bash
docker compose up -d
```

<table>
<thead>
  <tr>
    <th>
      Port
    </th>
    
    <th>
      Service
    </th>
  </tr>
</thead>

<tbody>
  <tr>
    <td>
      <code>
        9000
      </code>
    </td>
    
    <td>
      Runtime: <code>
        http://<app>.wervt.localhost:9000
      </code>
    </td>
  </tr>
  
  <tr>
    <td>
      <code>
        5432
      </code>
    </td>
    
    <td>
      Postgres (<code>
        postgres
      </code>
      
       / <code>
        postgres
      </code>
      
      , database <code>
        wervt
      </code>
      
      )
    </td>
  </tr>
  
  <tr>
    <td>
      <code>
        3030
      </code>
    </td>
    
    <td>
      Electric, for <code>
        nuxt dev
      </code>
    </td>
  </tr>
  
  <tr>
    <td>
      <code>
        9100
      </code>
    </td>
    
    <td>
      Control plane
    </td>
  </tr>
  
  <tr>
    <td>
      <code>
        9428
      </code>
    </td>
    
    <td>
      VictoriaLogs, with its own UI at <code>
        /select/vmui
      </code>
    </td>
  </tr>
  
  <tr>
    <td>
      <code>
        3900
      </code>
    </td>
    
    <td>
      Garage S3 API (apps' buckets)
    </td>
  </tr>
  
  <tr>
    <td>
      <code>
        3903
      </code>
    </td>
    
    <td>
      Garage admin API, on <code>
        127.0.0.1
      </code>
      
       only, for <code>
        nuxt dev
      </code>
    </td>
  </tr>
</tbody>
</table>

## Secrets

<table>
<thead>
  <tr>
    <th>
      Variable
    </th>
    
    <th>
      Default
    </th>
    
    <th>
      
    </th>
  </tr>
</thead>

<tbody>
  <tr>
    <td>
      <code>
        WERVT_DB_SECRET
      </code>
    </td>
    
    <td>
      <code>
        dev-secret
      </code>
    </td>
    
    <td>
      Derives role passwords and app tokens
    </td>
  </tr>
  
  <tr>
    <td>
      <code>
        WERVT_ELECTRIC_SECRET
      </code>
    </td>
    
    <td>
      <code>
        dev-electric-secret
      </code>
    </td>
    
    <td>
      Electric API secret
    </td>
  </tr>
  
  <tr>
    <td>
      <code>
        WERVT_PG_PASSWORD
      </code>
    </td>
    
    <td>
      <code>
        postgres
      </code>
    </td>
    
    <td>
      Postgres superuser password
    </td>
  </tr>
  
  <tr>
    <td>
      <code>
        WERVT_DEPLOY_TOKEN
      </code>
    </td>
    
    <td>
      <code>
        dev-deploy-token
      </code>
    </td>
    
    <td>
      Control plane token for everything
    </td>
  </tr>
  
  <tr>
    <td>
      <code>
        WERVT_READ_TOKEN
      </code>
    </td>
    
    <td>
      <code>
        dev-read-token
      </code>
    </td>
    
    <td>
      Control plane token for app info and logs
    </td>
  </tr>
  
  <tr>
    <td>
      <code>
        WERVT_GARAGE_ADMIN_TOKEN
      </code>
    </td>
    
    <td>
      <code>
        dev-garage-admin
      </code>
    </td>
    
    <td>
      Garage admin API token
    </td>
  </tr>
  
  <tr>
    <td>
      <code>
        WERVT_GARAGE_RPC_SECRET
      </code>
    </td>
    
    <td>
      zeros
    </td>
    
    <td>
      Garage RPC secret (64 hex characters)
    </td>
  </tr>
</tbody>
</table>

Set them in a `.env` next to `compose.yml` for anything beyond local development.

## Deploying an app

```bash
pnpm exec wervt login --url http://localhost:9100 --token dev-deploy-token
pnpm app:deploy todos   # = wervt deploy todos, run from the repo root
```

`wervt deploy` builds the app and uploads `.output/server`, `.output/public` and `drizzle/` to the
control plane, which:

1. extracts the bundle into `.releases/<app>/<release>`,
2. provisions the app's role and schema and runs its migrations as that role,
3. points the `<app>` symlink at the new release with an atomic rename.

The runtime resolves the symlink per request, so the next request starts a fresh isolate on the new
release while the old one idles out. No restart needed.

```bash
pnpm exec wervt apps               # list apps and their current release
pnpm exec wervt rollback todos     # back to the previous release
pnpm exec wervt releases todos     # its releases, newest first
pnpm exec wervt activate todos <id> # switch to one of them
pnpm exec wervt logs todos          # its logs from the last hour
```

<warning>

Rollback doesn't revert migrations. The previous build has to work with the current schema.

</warning>


## Sitemap

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