---
title: Updating and running
description: Ship new versions, keep your instance's files safe, and run Sendoff locally.
sidebar:
  icon: refresh-cw
---

Your copy of Sendoff is a git clone of the repo, made by `bun create sendoff`, and your settings live in its database, not in code. So updating is just moving to a newer version and redeploying.

## Updating

From your Sendoff folder:

```bash
bun run update
```

It fetches Sendoff's releases and shows the release notes for every version since yours. If you say yes, it moves your folder to the latest release, installs dependencies and runs `bun run deploy`. That applies any new database migrations first, then deploys the Worker with your instance's config, and the Pages project in front of it if [your DNS isn't on Cloudflare](/docs/reference/setup#domains-outside-cloudflare). It doesn't touch your secrets, DNS or SES setup.

Type the `run`: `bun update` on its own is Bun's command for updating dependencies.

If your folder has uncommitted changes, or commits of its own, it stops without changing anything and says what to do. If your folder doesn't have `bun run update` yet, run `git pull` once to get it.

### Running the latest code

To run what's on `main`, including changes that aren't released yet:

```bash
git pull
bun install
bun run deploy
```

Unreleased changes don't have release notes yet, so `bun run update` is the safer way to update.

## What's changed

Each version of the Worker has release notes on GitHub, under [Releases](https://github.com/haydenbleasel/sendoff/releases) (the ones tagged `app@`), and in `apps/app/CHANGELOG.md` in your folder. `bun run update` shows you the ones you're about to get. If a version needs more than deploying, like [running setup again](#running-setup-again), its notes say so.

To hear about new versions, open the [Sendoff repo](https://github.com/haydenbleasel/sendoff) on GitHub and choose **Watch › Custom › Releases**.

## Your instance's files

Setup records your instance in `apps/app/.sendoff/`, which git ignores:

- `instance.json`: your Cloudflare account, hostname, and the IDs of the database, KV namespace, queue, Turnstile widget, SES resources and Pages project (if you have one)
- `wrangler.jsonc`: the deploy config generated from it
- `pages/`: the Pages project's code and config, generated from it, if you have one

`bun run deploy` needs them. If you lose them, or move to another machine, run `bun run setup` again. It finds the database, queue and SES resources that already exist instead of creating new ones, and keeps the Worker's `TOKEN_SECRET`. It can't look up the Turnstile widget or the Worker's AWS key without these files, though, so it makes new ones. Delete the old widget in the Cloudflare dashboard afterwards. Setup lists the `sendoff` IAM user's old key, with when it was last used, for you to delete: IAM allows a user only two keys, so setup stops if both are taken. It doesn't delete the key itself, since another Sendoff instance in the same AWS account could be using it.

The repo's own `apps/app/wrangler.jsonc` stays generic, so pulling updates never conflicts with your instance.

## Running setup again

Setup is safe to re-run at any time. It checks every step and skips what's already done. Run it to:

- **Finish an interrupted setup**, such as when DNS took too long
- **Replace the MCP token**, by choosing **create a new one** when it asks
- **Recreate the Worker's AWS key**, if you deleted it
- **Turn on delivery reports**, when `doctor` warns that SES doesn't send them (instances set up before Sendoff counted deliveries)

It never rotates `TOKEN_SECRET`, which signs the unsubscribe and tracked links in every email you've ever sent. Changing it would break all of them.

## Running locally

```bash
cd apps/app
cp .dev.vars.example .dev.vars
bun run db:migrate
bun run dev
```

That serves the Worker at `http://localhost:8787` with a local database and Turnstile's test keys. Fill in real AWS keys in `.dev.vars` to send test emails. Open `/preview/sample` to see the layout with sample content.

Other commands, from the repo root:

| Command | What it does |
| --- | --- |
| `bun run test` | Runs the tests inside workerd, the Workers runtime |
| `bun run types` | Typechecks |
| `bun run check` | Lints and checks formatting |
| `cd apps/app && bun run bench` | Measures render CPU time against the Workers limit |
