Skip to content

Run many sites from one deployment

Fleet mode serves many small docs sites from one web service, one worker and one Postgres database. Each site keeps its own schema, settings, readers and secrets.

Most deployments run one site. Providers that host many small sites, such as free or trial plans, can run them as a fleet instead of giving each one its own services.

How sites stay apart

  • Each site's tables live in their own Postgres schema, site_<slug>. Its connections can only see that schema.
  • Requests are routed by host name. Each site runs as its own app, configured from its own settings, so it has its own session secret, admin token, caches and public URL.
  • Notifications that clear caches carry the schema in their channel name, so one site's changes never touch another's.
  • Each site's images, themes and PDFs are stored under a prefix of their own.

Per-reader filtering works exactly as on a single site.

Running a fleet

Run opend-server fleet serve as the web service and opend-server fleet worker as the worker, with opend-server fleet migrate before each deploy so an upgrade's database changes reach every site's schema first, both with the fleet variables in Environment variable reference. Point every site's host names (and editor host names) at the web service, typically with wildcard DNS records.

Sites are managed through the fleet admin API, on OPEND_FLEET_HOST with the bearer token OPEND_FLEET_TOKEN:

RouteDoes
POST /fleet/api/sitesCreates a site from slug, hosts, editorHosts and env: the settings a single-site deployment would get as environment variables (PUBLIC_URL, SESSION_SECRET, OPEND_ADMIN_TOKEN and so on). The settings must form a valid configuration. The fleet supplies the database, storage and address variables itself.
GET /fleet/api/sites, GET /fleet/api/sites/:slugLists sites, with the names of their settings but never the values.
PATCH /fleet/api/sites/:slugChanges hosts or settings. The site restarts on its next request.
POST /fleet/api/sites/:slug/suspend, /resumePauses a site (readers see that it is paused) or brings it back.
DELETE /fleet/api/sites/:slugDeletes the site, its schema and its stored files.
PUT /fleet/api/forwards/:hostForwards a host to another server, with {"upstream": "https://…"}: see below.
GET /fleet/api/forwards, DELETE /fleet/api/forwards/:hostLists forwards, or removes one.

Each site then has its own admin API at its own host, with its own admin token, for registering the docs repository, readers and orgs (Admin API reference).

Forwarding hosts to dedicated deployments

A provider that points a wildcard domain at the fleet cannot also give a dedicated deployment a name under that domain directly. A forward lets the fleet answer for such a host and pass every request on to the deployment's own address, unchanged apart from Host and the X-Forwarded-* headers. Redirects to the upstream's address are rewritten to the public host. The upstream must be an https origin (plain http only to a loopback or .internal host), and its PUBLIC_URL is the forwarded host. A host is either a site's or a forward's, never both. Give a site its own domain to connect without the fleet in between.

Moving a site to its own deployment

Export the site with GET /admin/api/export on the site's own host. Start a dedicated deployment with the same SESSION_SECRET (or OPEND_SECRET_KEY) and import the export with POST /admin/api/import. Then request builds. Point the host names at the new deployment, and delete the site from the fleet.