> ## Documentation Index
> Fetch the complete documentation index at: https://docs.frankenpress.com/llms.txt
> Use this file to discover all available pages before exploring further.

# Upgrading

> How to roll forward FrankenPress component versions

FrankenPress is built from four independent repos that move at their
own cadence. Upgrades happen at three levels — **runtime**, **mu-plugin**,
**site image** — and one operations-side level: **chart**.

## Component upgrade order

```text theme={null}
mu-plugin    →    runtime    →    your site image    →    charts
(release)            (bake new       (rebuild on top         (helm upgrade
                      mu-plugin tag)  of new runtime)         with new tag)
```

Most upgrades skip steps. A typical example:

| Change                                    | Upgrade path                                                                                   |
| ----------------------------------------- | ---------------------------------------------------------------------------------------------- |
| WP plugin / theme bump                    | site image only — `composer update`, tag, deploy                                               |
| WP core bump                              | site image — bump `roots/wordpress` constraint, tag, deploy                                    |
| FrankenPHP / Souin / Caddy module bump    | runtime → site image — bump `FRANKENPHP_VERSION` etc., tag runtime, rebuild site image, deploy |
| Cache invalidation logic change           | mu-plugin → runtime → site image                                                               |
| Helm chart change (new value, RBAC, etc.) | charts only — `helm upgrade --version <new>`                                                   |

## Site image upgrade

The most common case. In your `site-template` fork:

```bash theme={null}
git tag v1.2.0
git push origin v1.2.0
```

CI builds and pushes `ghcr.io/<your-org>/<your-site>:v1.2.0`. Then in
the cluster:

```bash theme={null}
helm upgrade mysite oci://ghcr.io/frankenpress/charts/site \
  --namespace mysite \
  --reuse-values \
  --set image.tag=v1.2.0
```

The chart re-rolls the Deployment, draining old pods according to your
`updateStrategy` (default rolling update with `maxSurge: 1`,
`maxUnavailable: 0`).

<Tip>
  The chart adds `checksum/config` and `checksum/secret` annotations
  to the pod template. If you change a `ConfigMap` or `Secret` value
  via `helm upgrade`, the pods will roll automatically — no manual
  pod restart needed.
</Tip>

## What the post-upgrade install Job does

Every `helm upgrade` (and the initial `helm install`) fires a
post-upgrade hook Job named `<release>-site-install`. The Job
runs two things, both idempotent:

1. `wp core is-installed` — short-circuits the install branch on every
   upgrade after the first.
2. `wp core update-db` — applies any pending schema migrations from
   the WP core version baked into the new image. No-op if already
   current.

Admin credential reconciliation moved out of the install Job in chart
**v0.4.0** to a Pod-lifecycle `sync-admin-credentials` initContainer —
self-driving on every Pod roll, not gated on `helm upgrade`. See
[Admin credential rotation](/operations/admin-credential-rotation).

Watch a single upgrade's Job complete:

```bash theme={null}
kubectl --namespace <ns> get jobs --watch
```

The Job uses `helm.sh/hook-delete-policy: hook-succeeded`, so the
pod and Job object disappear after a successful run — read logs from
the install pod *while it's running* (`kubectl logs -f
job/<release>-site-install`), or check `kubectl get events` for
historical confirmation.

If the Job fails, the upgrade fails too — the new Deployment still
rolls (Helm hooks are non-blocking by default), but you'll see a
loud error in `helm upgrade` output. Common cause: DB unreachable
from the install pod. The Job's `set -e` script bails fast and
`backoffLimit: 6` retries before failing the Job.

## Chart upgrade

```bash theme={null}
helm upgrade mysite oci://ghcr.io/frankenpress/charts/site \
  --version 0.13.2 \
  --namespace mysite \
  --reuse-values
```

`--reuse-values` keeps your existing overrides (image tag, S3 bucket,
etc.). Without it you'd need to repeat every `--set` from the original
install.

## Reading the release notes

Each repo publishes release notes on GitHub:

* [`runtime` releases](https://github.com/frankenpress/runtime/releases)
* [`mu-plugin` releases](https://github.com/frankenpress/mu-plugin/releases)
* [`charts` releases](https://github.com/frankenpress/charts/releases)
* (your `site-template` fork's releases for site-specific changes)

Breaking changes are flagged at the top of each release. Treat any
**major** version bump (e.g. `0.x → 1.0`, `1.x → 2.0`) as requiring a
read-through before upgrading.

## Backups before major upgrades

For production:

1. **Database snapshot** — your DB layer (RDS / MariaDB Operator backup CR / etc.) handles this. Verify the latest snapshot is recent before triggering the upgrade.
2. **No filesystem state to back up** — the site image is immutable, uploads live in S3. There's nothing local on the pod to lose.
3. **Image artefact is preserved** — GHCR retains all image tags by default; a rollback is `helm upgrade --set image.tag=<previous-tag>`.

## Rollback

```bash theme={null}
helm rollback mysite --namespace mysite
```

This reverts to the previous release revision. To rollback further:

```bash theme={null}
helm history mysite --namespace mysite
helm rollback mysite <revision> --namespace mysite
```

Database schema changes don't auto-rollback; if a release introduced a
WP DB migration that's incompatible with the previous code, you need a
DB restore alongside the helm rollback. WP core upgrades and most
plugin upgrades are forward-compatible, but verify per release.
