Skip to main content
iii 0.23 removes the legacy worker lifecycle. There is no compatibility period or automatic migrator: iii worker, the worker::* functions, engine fallback through iii.toml, and engine-managed project workers are gone. A managed project now has one worker-compose.yaml. Its engine: section configures the engine; containers: declares project workers. If a direct config.yaml still declares a project worker, startup and reload stop with UNSUPPORTED_CONFIG_WORKERS and list every entry to migrate.

Hand the migration to a coding agent

The steps below are mechanical, so a coding agent with shell access to the project can carry them out. Copy this prompt into the agent, then review the resulting worker-compose.yaml before starting it.
Prompt

Back up the project

Keep a copy of:
  • config.yaml and any path passed with --config
  • an existing worker-compose.yaml
  • the configuration worker’s storage directory, ./config/ by default
  • state, queue, and stream data paths
  • iii.lock, if the project used the removed installer
Rollback requires those files and the previous iii binary. The 0.23 engine does not accept the old project-worker declarations.

Build the new engine section

Move these five engine-owned worker configs from list entries in config.yaml to the direct engine.workers map:
iii-stream is deprecated (iii-stream) and will be removed in an upcoming release. Behavior is unchanged for now, so move its configuration as shown. Plan the migration with Migrate from iii-stream and pubsub.
The map value is the worker config itself; do not keep the old nested config: key. Multiple instances use a #instance suffix, such as iii-worker-manager#rbac.
worker-compose.yaml
engine.url defaults to ws://127.0.0.1:49134. Compose writes the engine-only representation to <project-dir>/.iii/compose/<daemon-namespace>/engine-config.yaml by default. When III_COMPOSE_STATE_DIR is set, the path is $III_COMPOSE_STATE_DIR/<project-slug>/<daemon-namespace>/engine-config.yaml. Compose writes the file with owner-only permissions and starts the engine from it. Do not edit that generated file. Remove explicit iii-engine-functions, iii-telemetry, and iii-observability entries. The engine injects them automatically, and declaring one under engine.workers fails.

Move project workers to containers

pubsub is deprecated (pubsub) and will be removed in an upcoming release. Behavior is unchanged for now. See Migrate from iii-stream and pubsub.
Copy each old project worker’s config: value to config_override, preserve its storage paths, and pin the package version.
worker-compose.yaml
Translate iii-exec commands into pre_run, run, and post_run. Custom local workers use path://; registry workers use package:// plus a version. You can also add a registry worker after the managed daemon is running:
compose::add writes containers: and restarts affected containers. It never edits engine: or restarts the engine. A registry root of kind engine is rejected and points to engine.workers.<name> when the worker is configurable.

Preserve stored configuration

Standalone packages use unprefixed configuration ids. A value stored as iii-state is not copied to state automatically.
  1. Before upgrading, read the old value with configuration::get.
  2. Put it under config_override, or write it to the new id with configuration::set.
  3. Verify the new worker before removing the old stored entry.
Do not rename only the YAML file: filesystem-backed entries also contain id and schema metadata.

Start the result

For a managed engine, the file owns both lifecycles:
The explicit namespace (dev) addresses compose::* and overrides the file’s namespace: for its containers. Changing engine: while the daemon runs returns ENGINE_RESTART_REQUIRED; stop and restart that Compose invocation. A second file with engine: returns ENGINE_ALREADY_OWNED. For an engine supervised separately by systemd or Kubernetes, keep its five allowed workers in the list-shaped config.yaml, start it directly, omit engine: from the Compose file, and supply the existing URL:
Run those commands under separate supervisors. Explicit --engine has highest priority and may override an engine.url in the Compose file. Without either value, Compose uses III_URL, then defaults to ws://127.0.0.1:49134. Verify the migration:

Common errors

  • UNSUPPORTED_CONFIG_WORKERS: direct config.yaml still contains project workers.
  • UNSUPPORTED_ENGINE_WORKER: engine.workers contains a project or unknown worker.
  • ENGINE_WORKER_IS_INJECTED: remove an automatically supplied internal worker.
  • ENGINE_RESTART_REQUIRED: restart Compose to apply a changed engine section.
  • ENGINE_ALREADY_OWNED: start the second managed file in a separate Compose invocation.
  • ENGINE_SECTION_REQUIRES_MANAGED_START: an external daemon was asked to load a managed file.
  • ENGINE_WORKER_IS_BUILTIN: compose::add was given an engine package as a root.
  • CONTAINER_NAME_TAKEN: another live process owns the same (namespace, worker name).