---
title: Upgrading
description: Upgrade Hubzoid from 0.9.x to 1.0.1 and apply later releases safely, with what changes, the steps, database migrations and the way back.
canonical_url: https://hubzoid.com/docs/deploy/upgrading
last_updated: 2026-09-27
---

# Upgrading

Upgrade Hubzoid from 0.9.x to 1.0.1 and apply later releases safely, with what changes, the steps, database migrations and the way back.

Hub files need no changes between releases. What changes is the software around them: Hubzoid's own tables, the workflow engine and the chat app. Each upgrades itself at the first start of the new version, so an upgrade is a short maintenance window with a backup taken just before that start.

## When to use this

Follow the general procedure for every release. Read the [0.9.x to 1.0.1](https://hubzoid.com/docs/deploy/upgrading#from-09x-to-101) section once if your deployment runs a 0.9 release, because 1.0.1 changes security defaults and moves schedules onto the workflow engine. The full list of changes is in the [changelog](https://hubzoid.com/docs/reference/changelog).

## The general procedure

Drain runs → Stop → Install → Back up → Check → Start → Verify

Back up after installing and before the first start. Nothing is upgraded until that start.

1. **Drain scheduled work**

   Runs belong to the code that started them. After an upgrade, runs that were queued or interrupted under the old version are cancelled at the next start so they cannot block the queue, and show as `CANCELLED`. Queued markdown tasks that had not started are queued again under the new code. To avoid losing work, wait until `hubzoid schedule status <hub>` shows no run `PENDING` or `ENQUEUED`, ideally between slots.

2. **Stop everything**

   Stop the hub, or the gateway and every bridge. Upgrade the whole deployment together. Running an old and a new version side by side against the same databases is not supported.

3. **Install the new version**

   In the same environment, either bump the pin in your hubs' `requirements.txt` and reinstall, or upgrade directly:

   ```bash
   pip install -U hubzoid                 # or: pip install -U "hubzoid[postgres]"
   ```

4. **Back up**

   ```bash
   hubzoid backup ./my-hub --out pre-upgrade.tar.gz
   ```

   A backup never creates or upgrades a table, so this archive is the deployment exactly as the old version left it. For PostgreSQL, also run `pg_dump` for each database. See [backup and restore](https://hubzoid.com/docs/deploy/backup-and-restore#postgresql).

5. **Check**

   ```bash
   hubzoid doctor ./my-hub
   ```

   Fix every `fail`. Before the first start, `db.operational` and `db.hub` report `info` or `warn` because their schemas have not been upgraded yet.

6. **Start**

   Start as before. At the first start, Hubzoid, the workflow engine and the chat app each upgrade their own tables.

7. **Verify**

   Sign in as an ordinary user and chat with each agent, download a file an agent makes, open the Console (Agents, each agent's Runs and schedules tab, Activity), and run `hubzoid doctor` again. If a cancelled scheduled run is still needed, start it with `hubzoid schedule run <hub> <name>`.

## How database upgrades work

Hubzoid versions its own tables with Alembic in two stores, each with its own version table so they never collide with each other, with the chat app's migrations or with the workflow engine's:

| Store | Version table | Holds |
| --- | --- | --- |
| Operational | `hz_alembic_operational` | Access, identities, audit, workflow catalog and state, usage, tool decisions |
| Hub | `hz_alembic_hub` | WhatsApp and Telegram conversation history |

Each bridge upgrades both stores before it serves anything. Bridges that start together take a lock first (a PostgreSQL advisory lock, or a file lock next to a SQLite database), so only one runs a migration at a time. Deployments from before versioning are handled by a baseline revision that creates any missing table and verifies the columns of existing ones. A table that does not match is refused rather than silently stamped.

A database already upgraded by a newer Hubzoid is refused, and the bridge stops with a message naming the store. `hubzoid doctor` reports this as a `fail` on `db.operational` or `db.hub`. The fix is to install that newer version or restore a backup. This is also why going back to an older release always means restoring the backup taken before the upgrade.

## From 0.9.x to 1.0.1

1.0.1 changes security defaults, moves markdown schedules onto the workflow engine and versions Hubzoid's tables. Hub files (`AGENTS.md`, skills, `schedule/*.md`, `workflows/`) need no changes.

| Change | What you do |
| --- | --- |
| The public bridge key `dev` is refused for downloads, and `hubzoid doctor` fails when `BRIDGE_API_KEYS` is unset. | Before restarting, set `BRIDGE_API_KEYS` to a long random value in each hub's `.env` (`openssl rand -hex 32`). The gateway hands each hub's key to the chat app at start. A hub with `OWUI_NATIVE_MCP=true` also needs the new key entered in the chat app's admin settings, under Connections. |
| Download links for files the agent made are signed. Links from earlier versions stop working. | Nothing. The files are still there, so ask the agent for the link again. `HUBZOID_ARTIFACT_LINK_TTL` adds an optional expiry. |
| Administrators can no longer open or export other people's chats in the chat app. | To allow it, set `ENABLE_ADMIN_CHAT_ACCESS=true` and `ENABLE_ADMIN_EXPORT=true`. |
| The chat app keeps its own branding unless the hub has files in `branding/`. | Nothing, unless you relied on the Hubzoid logo there. |
| OpenAI Agents SDK traces are no longer sent to OpenAI. | To keep them, set `HUBZOID_OPENAI_TRACING=true`. |
| Markdown tasks run on the hub's workflow engine. Each hub gets `.hubzoid/dbos.db`, and runs appear in the Console. | Nothing. Timing, catch-up and commit behaviour are unchanged. Let running tasks finish before you stop the hub. |
| On Python 3.12 the workflow engine needs SQLite 3.42 or newer. | Check `deps.sqlite` in `hubzoid doctor`. The Docker image moves to Debian 13 for this reason. |
| Hubzoid's tables are versioned and upgraded at the first start. A database written by a newer Hubzoid is refused. | Take the backup in the procedure above. Going back to 0.9.x means restoring it. |
| Tool decisions are stored in the database instead of `logs/access-*.jsonl`. | Nothing. The old files are imported once and left in place. |
| Usage is recorded per chat turn and workflow model call, with no message content. The Console opens on Agents, with usage totals above the agent cards. | Nothing. Counting starts at the upgrade. |
| In code workflows, `hub.call_llm` is one model call without tools. Earlier it ran the full agent. | Use `hub.call_agent` where a workflow needs tools. |
| Agent calls in code workflows are not retried unless `agent_max_attempts` is set. Runs left over from older workflow code are cancelled at start. | Drain queued runs before upgrading. |
| The public port rejects `.` and `..` path segments and drops client-sent `X-Hubzoid-*` and `X-OpenWebUI-*` headers. | Nothing, unless a client relied on sending those. |

Then follow the general procedure: set the bridge keys, stop, `pip install -U hubzoid`, back up, check, start and verify. At the first start Hubzoid upgrades its tables and imports the old decision logs.

## Upgrading a container

Back up, stop the container, build or pull the new image, and start again. With the Compose files, which build `hubzoid:local` from the checkout by default:

```bash
docker compose -f docker/docker-compose.yml exec hubzoid hubzoid backup /hub --out /hub/pre-upgrade.tar.gz
docker compose -f docker/docker-compose.yml down
git checkout v1.0.1
HUB_PATH=$PWD/my-hub docker compose -f docker/docker-compose.yml up -d --build
```

To run a published image instead, set `HUBZOID_IMAGE=ghcr.io/hubzoid/hubzoid:1.0.1`.

The container upgrades its tables at its first start, the same as a native install. See [Docker](https://hubzoid.com/docs/deploy/docker).

## Upgrading a gateway

One `hubzoid backup` of any hub in a gateway covers the gateway's data directory and every hub. Stop the gateway and every bridge before installing, including bridges run as their own services with `--no-bridges`, and start the gateway before external bridges so they find its manifest.

Moving hubs from chat app groups to managed grants is a separate maintenance window with its own preview, cutover and rollback. Schedule it after the upgrade is verified. See [access administration](https://hubzoid.com/docs/deploy/access-administration#move-a-hub-to-managed-grants).

## Going back

Stop everything. Restore with the new version still installed, because releases before 1.0.1 have no `restore` command, then reinstall the previous version and start:

```bash
hubzoid restore pre-upgrade.tar.gz
pip install "hubzoid==<previous version>"
```

Whatever the restore replaces is kept beside it as `<name>.pre-restore-<time>`. For PostgreSQL, restore each dump with `pg_restore` before restoring the archive.

## Next steps

- [Backup and restore](https://hubzoid.com/docs/deploy/backup-and-restore): What the pre-upgrade archive holds and how to restore it.
- [Changelog](https://hubzoid.com/docs/reference/changelog): Every change in each release.
- [Doctor reference](https://hubzoid.com/docs/reference/doctor): Every check and what its status means.
