Skip to main content
Namespaces let you run multiple isolated environments in the same database. For example, you can use one database for both staging and production by giving each environment a different namespace. Workflows in one namespace are completely invisible to another.

How Namespaces Work

Every workflow run and step attempt is tagged with a namespaceId. Workers and clients only see data in their namespace.
import { BackendPostgres } from "openworkflow/postgres";

// Production namespace
const prodBackend = await BackendPostgres.connect(url, {
  namespaceId: "production",
});

// Staging namespace (same database)
const stagingBackend = await BackendPostgres.connect(url, {
  namespaceId: "staging",
});

Default Namespace

If you don’t specify a namespaceId, the default is "default":
// These are equivalent
const backend = await BackendPostgres.connect(url);
const backend = await BackendPostgres.connect(url, { namespaceId: "default" });

Use Cases

Environment Isolation

Run development, staging, and production in the same database:
import { defineConfig } from "@openworkflow/cli";
import { BackendPostgres } from "openworkflow/postgres";

const namespace = process.env.OPENWORKFLOW_NAMESPACE_ID || "development";

export default defineConfig({
  backend: await BackendPostgres.connect(
    process.env.OPENWORKFLOW_POSTGRES_URL!,
    {
      namespaceId: namespace,
    },
  ),
});
Set the namespace per environment:
# Development
OPENWORKFLOW_NAMESPACE_ID=development npm run worker
# Staging
OPENWORKFLOW_NAMESPACE_ID=staging npm run worker
# Production
OPENWORKFLOW_NAMESPACE_ID=production npm run worker

Multi-Tenancy

Isolate workflows per tenant:
async function getBackendForTenant(tenantId: string) {
  return BackendPostgres.connect(process.env.OPENWORKFLOW_POSTGRES_URL!, {
    namespaceId: `tenant-${tenantId}`,
  });
}

Testing

Use separate namespaces for test runs:
// In your test setup
const testNamespace = `test-${Date.now()}`;

const backend = await BackendPostgres.connect(testUrl, {
  namespaceId: testNamespace,
});

SQLite Namespaces

Namespaces work the same way with SQLite:
import { BackendSqlite } from "openworkflow/sqlite";

const backend = BackendSqlite.connect("./backend.db", {
  namespaceId: "development",
});

Data Visibility

Each namespace is completely isolated:
NamespaceCan See
productionOnly production workflows
stagingOnly staging workflows
defaultOnly default workflows
A worker with namespaceId: "production" will never pick up work from the staging namespace.

Dashboard

The dashboard shows workflows for the namespace configured in your config file. To view a different namespace, update the config and restart the dashboard.

Best Practices

  1. Use environment variables - Don’t hardcode namespace IDs
  2. Consistent naming - Use clear names like production, staging, development
  3. One namespace per worker pool - Don’t mix namespaces in the same worker process
  4. Clean up test namespaces - Periodically remove old test data