# Upgrading Anaphora

Source: https://docs.anaphora.it/getting-started/upgrading

# Upgrading Anaphora

Every Anaphora image carries `anaphora-upgrade.sh`, a script that upgrades a Docker Compose installation to that image.
It tests the new version on a copy of your database before it changes anything, and it can roll back.

:::info Why a script?
A new version can change the database (a migration). The previous version refuses to start on a database that a newer
version changed. So a rollback needs the database from before the upgrade. The script keeps that copy for you.
:::

## Requirements

- A Docker Compose installation, as in [Installation](./installation.md#docker-compose)
- The image named as `beshultd/anaphora:${ANAPHORA_TAG}` in `docker-compose.yaml`. If your file names a version
  directly, the script prints the one line to change.
- A host folder mounted on `/usr/src/app/storage`. The script cannot back up a database that lives inside the container
  or in a named volume.
- Docker Compose v2 and `python3` on the host

## Get the script

Take the script from the version you upgrade to. Run this in the folder of your `docker-compose.yaml`, and replace
`<version>` with the new version:

```bash
docker run --rm --entrypoint cat beshultd/anaphora:<version> /usr/local/bin/anaphora-upgrade.sh > anaphora-upgrade.sh
```

## Upgrade step by step

1. **Check.** Try the new version on a copy of your database. Anaphora keeps running. The copy has no network and sends
   nothing.

   ```bash
   bash anaphora-upgrade.sh check <version>
   ```

   The script shows what it found (service, image, folders), the database changes that the new version applies, the
   jobs it reads, and what the log said. If it says that the check failed, or that it cannot read some jobs, do not
   upgrade. Send the log file that it names to support.

2. **Dry run.** See every step and command, without running any:

   ```bash
   bash anaphora-upgrade.sh apply <version> --dry-run
   ```

3. **Apply.** The script checks again and asks before it stops Anaphora: type `yes` and press Enter. Then it copies
   `storage/` to `anaphora-backups/<date>/`, sets `ANAPHORA_TAG` in `.env` (the old file stays as `.env.bak`), starts
   the new version and waits until it answers. If the new version does not start, the script puts the backup back and
   starts the old version.

   ```bash
   bash anaphora-upgrade.sh apply <version>
   ```

   Add `--yes` to stop Anaphora without a question, for example in an unattended run.

   When the new version runs, the script shows the log lines that need your attention, for example
   `OIDC issuer discovery failed:`. Its last line is `Upgrade complete: Anaphora runs <image>.`

4. **Roll back** (if necessary). The script prints the command at the end of `apply`, for example:

   ```bash
   bash anaphora-upgrade.sh rollback anaphora-backups/20260924-071500
   ```

Every docker command is printed before it runs, and the whole session goes to a log file.

### Script options

Set these in the environment of the script:

| Variable              | Default                         | Description                                      |
|-----------------------|---------------------------------|--------------------------------------------------|
| `START_TIMEOUT_S`     | `600`                           | How long to wait for a version to start          |
| `BACKUP_ROOT`         | `./anaphora-backups`            | Where `apply` puts its backups                   |
| `LOG_FILE`            | `./anaphora-upgrade-<date>.log` | The log of the run                               |
| `ANAPHORA_IMAGE_REPO` | `beshultd/anaphora`             | The image repository, for a mirror or a registry |

:::warning Reports are not in the backup
The script copies the database, not the reports folder (`content/`). A rollback in the first hour keeps every report.
After that, the hourly clean-up can remove the reports of runs that are past their retention.
:::

## Automatic database backups

Anaphora also backs up its database by itself. Before it applies a migration, it writes a copy to `storage/backups/`,
encrypted with the same `DB_ENCRYPTION_KEY`. It keeps the three newest copies. The log names the file and says how to
restore it:

1. Stop Anaphora.
2. Restore the copy as `storage/authfish.sqlite`, and remove `authfish.sqlite-wal` and `authfish.sqlite-shm`.
3. Start the previous version.

If Anaphora cannot write the copy, it applies no migration, and the database stays as it was.

## After the upgrade from 0.16.0

0.17.0 changes the database. `AddApiKeys` adds a table. `SamlClockSkewChecked` and `OidcSettingsExplicit` come with
Authfish, the sign-in service, and change the saved SAML and OIDC settings. Some licence, LDAP and sign-in changes
change what an existing install does, or stop a login or a logout that worked before:

- **A licence that ends.** An install that ran on PRO or Enterprise lets only its administrators in when it goes to
  Free. The other users are signed out. Renew the licence, or give the System role to the users who must keep working.
- **Roles after Free.** An install that ran on Free before 0.17.0 has every local user saved as System. After the move
  to PRO or Enterprise, set the role of each user in **Settings**.
- **LDAP settings that cannot work.** Anaphora refuses them at start, and LDAP login stops: a user filter without
  `{{username}}`, a filter that does not parse, a group filter with no user in it, a CA certificate that Node cannot
  read. Check the LDAP settings before you upgrade.
- **Regex space permissions.** A permission saved before 0.17.0 keeps matching any part of the name, and the log names
  it at each start. After you change its mode, do not roll back to an older Anaphora: older versions drop the mode. See
  [How a permission matches](../administration/spaces.md#how-a-permission-matches).

- **OIDC with a private CA.** Anaphora now checks the TLS certificate of an `https` issuer at every address, also on an IPv4
  address, on `localhost`, on a `*.localhost` name or on `[::1]`. If OIDC from the
  environment uses such an issuer with a certificate from a private CA or a self-signed one, OIDC sign-in stops, and
  the log says `OIDC issuer discovery failed:`. Local users can still sign in. See
  [The TLS certificate of the issuer](../administration/authentication/oidc.md#the-tls-certificate-of-the-issuer).
  OIDC that you set up in the settings page needs no action: the upgrade turns on **Tls insecure skip verify** in
  **Settings** > **System** > **Auth** > **OIDC** where the old rule skipped the check, and the start log warns about
  it. Set **Tls CA cert** on the same page instead.
- **SAML Audience.** SAML checks the Audience of each assertion. An identity provider that sends another Audience, or
  none, fails every login. Set `extraConfig.audience` to the value that the provider sends.
- **SAML login from the portal of the identity provider.** Such a login is refused. Set
  `extraConfig.validateInResponseTo: never` to accept it again.
- **SAML logout from the identity provider.** The provider must sign it. An unsigned one leaves the Anaphora session
  open. In Keycloak, turn on **Sign documents** in the client.
- **SAML clock skew.** A saved **Accepted clock skew ms** (in **Settings** > **System** > **Auth** > **SAML**) of `-1`
  (no time check, the old default) becomes `0`. If the clock of the server is behind the clock of the provider, the
  login fails with "SAML assertion not yet valid". Sync the clocks, or set **Accepted clock skew ms**, for example to
  `60000`.
- **Logins in progress.** A SAML login that started before the upgrade fails once. The user signs in again.
- **Extra config.** The **Extra config** in **Settings** > **System** > **Auth** > **SAML** and **OIDC** can no longer
  change a setting that has a field of its own, for example the SAML `issuer` or the OIDC `client_secret`. The upgrade
  moves such a value to its field.
- **More roles.** A user whose provider sends exactly one group now gets that role. A SAML provider that sends one
  attribute per role now gives every role, not only the first. An LDAP group with more than one name gives one role
  per name. Check the [space permissions](../administration/spaces.md#how-a-permission-matches) that match these
  roles, mainly the admin ones.
- **Username.** A SAML assertion or an OIDC profile without the username attribute is refused.
- **OIDC logout.** The logout goes to the logout endpoint that the provider publishes. See
  [Logout](../administration/authentication/oidc.md#logout).
- **Health monitors.** A monitor can read the job names with an observer key, instead of the password of a system
  user. See [Self-monitoring](../administration/self-monitoring.md#observer-keys).

## After the upgrade from 0.15 or older

Version 0.16.0 changes the database. After the upgrade from an older version:

- Every user is signed out once and logs in again.
- The log can say that some local users "still carry the legacy sha512 password hash". Their passwords still work.
  Save their passwords again under **Settings** to store the new kind of hash.
- Jobs that keep their runs forever still keep them. To free disk space, set a retention on the job. The hourly
  clean-up then removes the older runs and their report files.
- A job from a version older than 0.9.5 that never had a retention setting now keeps its runs for six months.
- The hourly clean-up also removes report folders that no run owns. It removes only folders older than one day, and it
  removes nothing while the database holds no run.
- Report links are private. Links that Anaphora sent before the upgrade keep working until their runs expire or are
  deleted. See [Reports](../data-retention/reports.md).
- The test route `/guest/api/test/webhook` is removed. If a webhook delivery interface points at it, point it at a real
  receiver.
- A report template with no page gets one default page. A template row that holds more cells than its column count
  loses the extra cells. This change cannot be reversed.
- The first health check rates the last five runs of each job with the new rules for failed deliveries. It can turn a
  job yellow or red, and send the health mail once, for a delivery that failed days ago. See
  [Self-monitoring](../administration/self-monitoring.md).
- If accounts that you do not trust can sign in, change the session secret and the identity-provider secrets.

## Next steps

- [Installation](./installation.md): environment variables and Docker Compose
- [Backup](../administration/backup.md): export data and back up volumes
