Deployment topologies
The deployment shapes Hubzoid supports, where each one keeps its data, and how to choose between SQLite and PostgreSQL for your hubs.
Every Hubzoid deployment is built from the same three processes. The shapes below differ in how many hubs share them and where the data lives. Pick a shape before you provision a server, because moving between SQLite and PostgreSQL later is a manual step.
When to use this
Read this page before your first deployment past a laptop, and again when you add a second hub, move to PostgreSQL or put Hubzoid on a container platform. For the step-by-step setup of each shape, follow the links in Next steps.
The building blocks
| Process | Listens on | What it does |
|---|---|---|
| Edge | The public port, PORT (default 3080), on --host (default 127.0.0.1) | The single front door. Forwards /artifacts, /portal, /mcp and /webhooks paths to a bridge or inbound server and everything else to the chat app. |
| Chat app (Open WebUI) | A loopback port, PORT + 40000 unless HUBZOID_OWUI_PORT is set | Accounts, sign-in, chats and the web chat interface. |
| Bridge | 127.0.0.1:BRIDGE_PORT (default 8000) | One per hub. Runs the agent, its tools, workflows and schedules, the Console API and the hosted MCP server. |
The bridge never listens on a public interface. Its /v1, /uploads and /healthz routes are reachable only from the same machine, and /v1 also requires a key from BRIDGE_API_KEYS. See the security model for the full list of public and loopback paths.
- Browser
- TLS proxy
- Edge
- Chat app
- Bridge
- Model provider
Supported shapes
| Shape | How you run it | Chat app and sign-in | Use it when |
|---|---|---|---|
| One hub | hubzoid run <hub> | Its own chat app, users in <hub>/.openwebui-data/webui.db | One team or one agent. The simplest production setup. |
| Separate hubs on one machine | One hubzoid run per hub, each with a unique PORT and BRIDGE_PORT | One chat app per hub, separate user lists and URLs | Hubs that must not share accounts, sign-in or a URL. |
| Multi-hub gateway | hubzoid gateway <hub> <hub> ... | One shared chat app, one sign-in, per-team visibility | Several teams on one machine who should sign in once and share one Console. |
| Container | The Hubzoid image, which runs hubzoid run /hub | As for one hub | pip install is impractical on the host, or your platform runs containers. |
- TLS proxy
- Edge
- Shared chat app
- Bridge for each hub
- Shared operational store
The gateway is lighter than one hubzoid run per hub because it starts a single chat app process. It shares one login surface by design. When hubs need separate login realms, run them as separate hubs instead. The gateway page covers its flags and URL layout.
Rules for every shape
- One bridge per hub. Two processes serving the same hub at once is not a supported configuration. On a container platform, run each hub as its own service with a desired count of 1 and no autoscaling.
- SQLite stays on a local disk. Do not put SQLite files on a network filesystem such as NFS or EFS. File locking over a network filesystem corrupts SQLite databases.
- One SQLite workflow database per hub. In a gateway each hub keeps its own
.hubzoid/dbos.db. The gateway refuses to start when a multi-hub deployment pointsHUBZOID_DBOS_DBat a SQLite file. - Unique hub folder names. A hub's folder name is its access domain. Within one deployment, names must differ even when compared case-insensitively.
- SQLite 3.42 or newer on Python 3.12. The workflow engine needs it for scheduled tasks and workflows.
hubzoid doctorreports it asdeps.sqlite. Python builds from python.org or Homebrew, Debian 13 and Ubuntu 24.04 ship a new enough SQLite. PostgreSQL avoids the requirement.
Where data lives
Hubzoid keeps its state in three stores, and the chat app keeps a fourth.
| Store | What it holds | One hub (SQLite) | Gateway (SQLite) |
|---|---|---|---|
| Operational store | Grants, identities, access changes, tool decisions, usage, workflow state and catalog | <hub>/.hubzoid/hub.db | <data-dir>/hubzoid-operational.db, shared by every hub |
| Workflow engine | Run history and checkpoints for schedules and workflows | <hub>/.hubzoid/dbos.db | <hub>/.hubzoid/dbos.db for each hub |
| Hub database | WhatsApp and Telegram conversation history | <hub>/.hubzoid/hub.db | <hub>/.hubzoid/hub.db for each hub |
| Chat app | Accounts, sessions, chats, uploaded files, model visibility | <hub>/.openwebui-data/webui.db | <data-dir>/webui.db |
In a standalone hub the operational store and the hub database share one SQLite file. Each store keeps its own schema version table, so they never collide.
Hubzoid resolves each store's URL in a fixed order:
| Store | Resolution order |
|---|---|
| Operational store | The registered gateway manifest, then HUBZOID_OPERATIONAL_DB, then DATABASE_URL, then the hub's own SQLite file |
| Workflow engine | The hub's entry in the gateway manifest, then HUBZOID_DBOS_DB, then DATABASE_URL when it is PostgreSQL, then <hub>/.hubzoid/dbos.db |
| Hub database | DATABASE_URL, then <hub>/.hubzoid/hub.db |
Inside a gateway the manifest (<data-dir>/deployment.json) is authoritative. A bridge or operator command whose HUBZOID_OPERATIONAL_DB or HUBZOID_DBOS_DB disagrees with it stops with an error instead of writing to a second store.
SQLite or PostgreSQL
SQLite is the default and needs no setup. It suits one machine with one bridge per hub, and hubzoid backup copies it consistently while the hub runs.
Choose PostgreSQL when you want a managed database server with its own backups and point-in-time recovery. Install the driver extra and use the postgresql+psycopg:// URL form, because Hubzoid installs the psycopg 3 driver only:
pip install "hubzoid[postgres]"Hubzoid upgrades its own tables at start. The workflow engine keeps its tables in the dbos schema. Hubzoid does not copy an existing SQLite deployment into PostgreSQL, so start a PostgreSQL deployment fresh or move the data with your own tooling.
Two ways to point Hubzoid at PostgreSQL
| Set | Moves to PostgreSQL | Stays in SQLite | Use it for |
|---|---|---|---|
HUBZOID_OPERATIONAL_DB and HUBZOID_DBOS_DB | Operational store, workflow engine | Each hub's .hubzoid/hub.db, the chat app's webui.db | Gateways, and any hub that serves the hosted MCP server or still uses group-based access |
DATABASE_URL | Operational store, workflow engine, hub database, and the chat app, which reads the same variable | Nothing | A single hub that does not serve MCP and manages access with grants in the Console |
For a gateway, set the variables in the environment the hubzoid gateway process starts with:
HUBZOID_OPERATIONAL_DB=postgresql+psycopg://hubzoid:<password>@db.internal:5432/hubzoid
HUBZOID_DBOS_DB=postgresql+psycopg://hubzoid:<password>@db.internal:5432/hubzoidBoth variables can name the same database. Hubzoid's tables use an hz_ prefix and the workflow engine uses its own schema.
How sharing one database behaves
The choice between the two forms follows from what each store is keyed on:
- The operational store is shared by design. Every row carries its hub, so one store serves every hub in a gateway. An organization grant made in one hub is visible to all of them, and one Console covers the deployment.
- The workflow engine can share PostgreSQL. Each hub runs under its own application name and queues, so several hubs can use one PostgreSQL system database. On SQLite each hub keeps its own file.
- The hub database is not keyed by hub. It stores WhatsApp and Telegram history per sender (
whatsapp-<phone>,telegram-<id>). Hubs that share oneDATABASE_URLshare this table, so a person who writes to two hubs over the same channel would get one merged history. Keep one hub database per hub, which theHUBZOID_OPERATIONAL_DBandHUBZOID_DBOS_DBform does automatically. - The chat app follows
DATABASE_URL. Hubzoid reads two things directly from the chat app's SQLite file: the per-user API keys that authenticate the hosted MCP server and the chat app groups used by hubs that have not moved to managed grants. Both need the chat app on SQLite, which is whyDATABASE_URLsuits only the single-hub case in the table above.
Back up PostgreSQL separately
hubzoid backup saves SQLite files and names every PostgreSQL database it skipped. Dump each distinct PostgreSQL database with pg_dump. The backup page has the full procedure.
Choosing a shape
| Situation | Choose |
|---|---|
| One team, one agent, one server | One hub on SQLite, behind a reverse proxy |
| Several teams on one server who should sign in once | A gateway on SQLite |
| Hubs that must keep separate accounts and URLs | Separate hubzoid run processes, one per hub |
| You need a managed database or point-in-time recovery | Either shape with PostgreSQL through HUBZOID_OPERATIONAL_DB and HUBZOID_DBOS_DB |
pip install fails on the host, or you run containers | The Docker image, one container per hub |
Check any shape with hubzoid doctor <hub>. It reads the deployment without changing it and fails on misconfigured or unreachable databases (db.operational, db.hub).
Next steps
People and activity
The People page and account statuses, a user's details with their role, password and access by agent, approving sign-ups, deleting users, and how to use the two Activity records for audits.
Single server
Run one or more hubs on a Linux server with systemd, put a reverse proxy in front for TLS, and set the values a public deployment needs.
