# Upgrades

> Update a self-hosted pod with synap update, choose a release channel, turn on automatic updates, and read the outcome.

URL: https://docs.synap.live/docs/run/upgrades · Last reviewed against the code: 2026-10-20

Pods are updated from published releases. Each release has a signed manifest that pins every image by digest, so an
update installs exactly what was tested.

## Update now

```bash
cd /opt/synap/deploy
sudo synap update                     # the default channel: SYNAP_UPDATE_CHANNEL, else stable
sudo synap update --release stable    # the latest tagged release
sudo synap update --release fast      # the latest successful build of main
sudo synap update --release v1.1.3    # one specific release
```

| Option | Meaning |
|---|---|
| `--release <ref>` | A release ID (`vX.Y.Z` or `main-<sha>`) or a channel (`stable`, `fast`). |
| `--force` | Re-apply the release the pod is already on. |
| `--from-source` | Build the checkout as images and apply it like a release. For development on a clone. |

An update never falls back to building images on the server: without `--from-source` it only pulls the release's
images, and a failed pull stops the update before anything changes.

Options from older versions are still accepted, so scripts keep working: `--build`, `-b`, `--local` and `-l` mean
`--from-source`; `--version <ref>`, `-v <ref>` and a bare `<ref>` mean `--release <ref>`; `latest` means the default
channel; `--from-image` is ignored.

## What an update does

Every update runs the same steps:

1. Takes the update lock, so two updates never run at once.
2. Fetches the release manifest and checks its signature (see [Verifying releases](/docs/run/install#verifying-releases)).
3. Checks free disk space (at least 2 GB by default) and pulls the images by digest. A failed pull stops here, before
   anything changed.
4. Takes a verified `pre-update` dump of every database.
5. Runs database migrations.
6. Starts the new backend as a canary and checks it.
7. Swaps the backend, brings up every other service and verifies that they are healthy.
8. Records the outcome.

If any step fails, the update **rolls back** to the previous images, and restores the `pre-update` dump when the
migrations had already changed the database.

## The outcome

Every update writes its result to `deploy/state/last-update.json`. After a rollback (`rolled_back`,
`rollback_failed` or `failed`), every pod admin gets an in-app notification.

## Automatic updates

Automatic updates on a self-hosted pod run from systemd. Install the units, with automatic updates on:

```bash
sudo synap units install --enable-auto-update
sudo synap units status
```

This installs:

- `synap-heal.timer`: every 2 minutes, restarts this pod's unhealthy containers, within a budget (never PostgreSQL).
- `synap-update.path`: applies update requests written to `deploy/state/requests/update.json`.
- `synap-compose.path` (releases after v1.1.3): applies service requests from the pod agent, written to
  `deploy/state/requests/compose.json`.
- `synap-update.timer` (only with `--enable-auto-update`): once a night, from 03:00, applies the latest release of the
  pod's channel within the update window.

The automatic run reads `deploy/state/update-policy.json`: `auto` (`false` turns it off), `pinned` (`true` holds the
pod on its release), `channel` and `window` (default `03:00-05:00`). Automatic updates always require a valid
release signature (`SYNAP_RELEASE_VERIFY=enforce`).

Without systemd, `synap units install` prints a cron line for the heal task instead; updates are then manual.

## Release channels

| Channel | What it follows |
|---|---|
| `stable` | Tagged releases (`v*`). The default. |
| `fast` | Every successful build of the `main` branch. |

Set the default channel for `synap update` with `sudo synap config set SYNAP_UPDATE_CHANNEL=fast`.

## Before a release with upgrade notes

Some releases change something you operate, for example a new setting or a new outbound connection. These are listed
in the "Upgrade notes" of `deploy/README.md` in the
[backend repository](https://github.com/Synap-core/backend/blob/main/deploy/README.md), newest first. Read them
before moving across several releases at once.
