Documentation / Exploitation / Guides pratiques / Upgrade the platform

Upgrade the platform

Meridian has two independently versioned things to upgrade: the platform (the four core libraries baked into the meridian-api and meridian-config-plane images) and plugins (published separately and resolved by the running host at boot). This recipe covers both, and — this is the part that bites if done out of order — the sequencing between them.

Before starting, make sure you already have a registry set up (Set up a plugin registry) and know how your instance declares its plugins (Declare and install plugins).

Before you start

  • Images never bundle business plugins. Dockerfile installs only the workspace and the core plugins (external-plugins/, copied into the image); every plugin under plugins: in the instance manifest is resolved against the registry (or a path override) at boot, in ensureInstancePlugins (apps/api/src/registry.ts). A plugin that is listed but cannot be resolved — no registry configured, version not found, download failed — fails the boot loudly, the same as an invalid manifest. Practically: publish a plugin version before you deploy or restart anything that will require it. Doing it the other way round turns a routine restart into an outage.
  • Version resolution is real semver, via the npm semver package: the registry picks the highest published version satisfying the manifest's range (maxSatisfying, libs/plugin-cli/src/registry/registry.ts) and the host checks a plugin's sdk: range against the running SDK version (satisfies, apps/api/src/plugins.ts). An incompatible sdk: range is a soft failure: the plugin is skipped and logged, the rest of the host still boots. An unresolvable registry entry is a hard failure: the boot aborts. Keep the two apart when you plan a rollout.
  • A run's workflow spec and behavior code are journaled when the run starts; changing plugin code or workflow YAML never affects a run already in flight, only new runs. See Why durable execution.

1. Publish new plugin versions first

Do this regardless of whether the platform itself is also changing — it's the step that must land before anything else touches the affected environment.

  1. From the plugin's own repo, build the deliverable dist/ (esbuild bundles every behavior/adapter/source module the manifest references, inlining its runtime dependencies; only node:* and @meridian/* stay external):

    meridian-plugin build .
    
  2. Publish it to the target registry (local directory or gs:// bucket):

    meridian-plugin publish . --registry gs://meridian-plugins
    # or: export MERIDIAN_REGISTRY=gs://meridian-plugins && meridian-plugin publish .
    

    A published version is immutablepublishPlugin refuses to overwrite an existing @publisher/name/<version> (libs/plugin-cli/src/registry/registry.ts). If you need to ship a fix, bump the version in plugin.yaml and publish again; there is no in-place replace.

  3. Confirm it landed before moving on:

    meridian-plugin versions @posos/demo --registry gs://meridian-plugins
    

See Build and bundle and Publish to the registry for the authoring side of this in full, and Bundling and distribution for why the artifact is self-contained.

2. Point instances at the new plugin version

This is safe to do any time after step 1 — the registry now has what any instance will ask for.

  • If the instance manifest's range already covers the new version (e.g. "@posos/demo": "^0.1.0" and you published 0.1.2), no manifest change is needed: the next endpoint/proxy restart resolves the new highest match automatically.
  • If you need the new version specifically, tighten or bump the range in the manifest and restart endpoint + proxy so ensureInstancePlugins re-resolves it. See Declare and install plugins and the instance manifest reference for the plugins: block.
  • A stale local cache never blocks an upgrade to a newer version — only the requested version is looked up, downloaded once into MERIDIAN_PLUGIN_CACHE, and reused afterwards.

3. Upgrade the platform libraries (lockstep)

Do this when @meridian/shared, engine-core, llm-client, or plugin-cli change — they version together as a fixed group (.changeset/config.json, fixed), so plugin authors only ever have to check one number.

  1. Any PR touching one of the four libraries includes a changeset:

    pnpm changeset
    
  2. On merge to main, the release workflow (changesets/action, .github/workflows/release.yml) opens or updates a "Version PR". Merging that PR runs:

    pnpm run version-packages   # changeset version && lockfile refresh
    

    which bumps all four package.json versions identically. Note that PLUGIN_CONTRACT_VERSION (libs/shared/src/version.ts) — the constant the host compares every plugin's sdk: range against — is decoupled from the package version and only changes on a real break of the plugin-facing surface. Publishing these packages to an external npm registry is deliberately disabled for now (release.yml): they're consumed in-workspace, so "upgrading the platform" means building new images from the merged main, not installing a new SDK release.

  3. Build the images from that commit. endpoint and proxy are the same meridian-api image, started with different commands (pnpm --filter @meridian/api run restate vs run server — see Dockerfile); ui is the separate meridian-config-plane image (Dockerfile.ui). For a compose deployment:

    pnpm run build:plugins   # external plugins mounted via PLUGINS_PATH, not baked in
    docker compose --profile stack up -d --build
    

For the full changeset mechanics (drafting a changeset, reviewing the Version PR), see Release and version.

4. Roll out in order

Once the images (if any) are built and the plugin versions you need are published, restart in this order — it's the order that keeps in-flight runs safe and keeps every component talking to a component it can still reach:

  1. Endpoint — restart it first. It re-registers with Restate on boot; any run already in progress resumes exactly where the journal left it, which is the durability guarantee Restate gives you (no drain step needed). See Why durable execution.
  2. Proxy — restart next.
  3. UI — restart last; it is stateless and only serves the console.
  4. Restate itself upgrades on its own schedule and procedure, independent of the four services above — keep the restate-data volume across the upgrade so the journal survives.

Test the exact platform + plugin version combination in pre-production first; the same advice applies to plugin swaps and to a platform bump alike.

5. Verify

  • GET /healthz and GET /readyz on the proxy — /readyz returns 503 with a { checks } detail if the Restate ingress or the run index isn't reachable yet after the restart.
  • Check the endpoint's plugin-load logs (component: "plugins") for the plugin you just published — it should show as loaded, with no SDK incompatibility warning for the new platform version.
  • Start one new event and confirm the run picks up the new plugin version/behavior, while runs already in flight before the restart keep behaving as journaled.

Roll back

  • Plugin roll back is just re-pointing the manifest's range at the older version (or leaving a path override in place) and restarting endpoint + proxy — published versions are immutable, so the old version is still sitting in the registry untouched.
  • Platform roll back is redeploying the previous image tag. If you also need to roll back the SDK version a plugin was checked against, remember the check is a soft failure: a plugin whose sdk: range no longer matches is dropped with a warning, not a hard boot failure — so a partial rollback degrades gracefully rather than blocking restart.
75 documents8 sectionssource : /docs · généré au build