Hubzoid
Deploy and operate

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

ProcessListens onWhat it does
EdgeThe 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 setAccounts, sign-in, chats and the web chat interface.
Bridge127.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.

  1. Browser
  2. TLS proxy
  3. Edge
  4. Chat app
  5. Bridge
  6. Model provider
A chat message on a single hub. The chat app calls the hub's bridge over loopback, and the bridge calls the model you configured.

Supported shapes

ShapeHow you run itChat app and sign-inUse it when
One hubhubzoid run <hub>Its own chat app, users in <hub>/.openwebui-data/webui.dbOne team or one agent. The simplest production setup.
Separate hubs on one machineOne hubzoid run per hub, each with a unique PORT and BRIDGE_PORTOne chat app per hub, separate user lists and URLsHubs that must not share accounts, sign-in or a URL.
Multi-hub gatewayhubzoid gateway <hub> <hub> ...One shared chat app, one sign-in, per-team visibilitySeveral teams on one machine who should sign in once and share one Console.
ContainerThe Hubzoid image, which runs hubzoid run /hubAs for one hubpip install is impractical on the host, or your platform runs containers.
  1. TLS proxy
  2. Edge
  3. Shared chat app
  4. Bridge for each hub
  5. Shared operational store
A gateway. One chat app reaches every hub's bridge on loopback, and all bridges read and write one 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 points HUBZOID_DBOS_DB at 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 doctor reports it as deps.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.

StoreWhat it holdsOne hub (SQLite)Gateway (SQLite)
Operational storeGrants, identities, access changes, tool decisions, usage, workflow state and catalog<hub>/.hubzoid/hub.db<data-dir>/hubzoid-operational.db, shared by every hub
Workflow engineRun history and checkpoints for schedules and workflows<hub>/.hubzoid/dbos.db<hub>/.hubzoid/dbos.db for each hub
Hub databaseWhatsApp and Telegram conversation history<hub>/.hubzoid/hub.db<hub>/.hubzoid/hub.db for each hub
Chat appAccounts, 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:

StoreResolution order
Operational storeThe registered gateway manifest, then HUBZOID_OPERATIONAL_DB, then DATABASE_URL, then the hub's own SQLite file
Workflow engineThe hub's entry in the gateway manifest, then HUBZOID_DBOS_DB, then DATABASE_URL when it is PostgreSQL, then <hub>/.hubzoid/dbos.db
Hub databaseDATABASE_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

SetMoves to PostgreSQLStays in SQLiteUse it for
HUBZOID_OPERATIONAL_DB and HUBZOID_DBOS_DBOperational store, workflow engineEach hub's .hubzoid/hub.db, the chat app's webui.dbGateways, and any hub that serves the hosted MCP server or still uses group-based access
DATABASE_URLOperational store, workflow engine, hub database, and the chat app, which reads the same variableNothingA 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:

gateway environment
HUBZOID_OPERATIONAL_DB=postgresql+psycopg://hubzoid:<password>@db.internal:5432/hubzoid
HUBZOID_DBOS_DB=postgresql+psycopg://hubzoid:<password>@db.internal:5432/hubzoid

Both 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 one DATABASE_URL share 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 the HUBZOID_OPERATIONAL_DB and HUBZOID_DBOS_DB form 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 why DATABASE_URL suits 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

SituationChoose
One team, one agent, one serverOne hub on SQLite, behind a reverse proxy
Several teams on one server who should sign in onceA gateway on SQLite
Hubs that must keep separate accounts and URLsSeparate hubzoid run processes, one per hub
You need a managed database or point-in-time recoveryEither shape with PostgreSQL through HUBZOID_OPERATIONAL_DB and HUBZOID_DBOS_DB
pip install fails on the host, or you run containersThe 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

Read this page as Markdown

Choose which cookies Hubzoid can use. You can change this at any time from Cookie settings in the footer. Read the Cookie Policy for details.