Skip to main content

Multi-Instance Architecture

Qarion supports deploying multiple tenant instances, each backed by its own isolated database. This enables organizations to separate environments (e.g., production vs. staging), business units, or client tenants while managing them all from a single control-plane.

Key Concepts

Control Plane

The control plane is the central management layer. It stores the registry of all instances, handles authentication for platform administrators, and exposes the admin API. All instance lifecycle operations — provisioning, suspension, archival — are performed against the control-plane database.

Instances

An instance is an isolated Qarion deployment with its own PostgreSQL database. Each instance contains the full Qarion schema: organizations, spaces, users, products, quality checks, workflows, and more. Instances are identified by a unique slug (URL-safe, lowercase, e.g., acme-corp).

Instance Routing

When multi-instance mode is enabled, all instance-scoped API requests use the URL pattern /i/{slug}/…. The instance middleware extracts the slug, resolves the instance from the control-plane registry, validates that it is active, and transparently rewrites the path so downstream routers see standard paths.

For example, a request to /i/acme-corp/api/products is resolved to the acme-corp instance and forwarded as /api/products to that instance's database.

Instance Lifecycle

Instances progress through a simple lifecycle:

active → suspended → archived
↑ ↓
└─ activate
StatusBehavior
ActiveInstance is fully operational. API requests are routed to its database.
SuspendedRouting is disabled (returns HTTP 503). Data is preserved. Useful for maintenance or billing holds.
ArchivedInstance is marked as archived and its connection pool is disposed. Data remains in the database but the instance is no longer accessible.

Provisioning an Instance

Via the Admin Panel

Navigate to Administration → Instances and click New Instance. Fill in:

  • Slug — URL-safe identifier (2–63 characters, lowercase letters, numbers, and hyphens)
  • Display Name — Human-readable name shown in the UI
  • Database URL (optional) — Existing PostgreSQL connection URL. Leave empty to auto-provision.
  • Config (optional) — Per-instance configuration (JSON)

Auto-Provisioning

When no database URL is provided, Qarion automatically:

  1. Creates a dedicated PostgreSQL database on the same server as the control plane (e.g., qarion_acme_corp)
  2. Creates a dedicated database user with a 32-character random password
  3. Runs all Alembic migrations to initialize the schema
  4. Encrypts and stores the connection URL
tip

Auto-provisioning is the recommended approach for most deployments. It ensures consistent database naming and eliminates manual setup.

Manual Database

For advanced scenarios (external database servers, custom configurations), provide a full async PostgreSQL connection URL:

postgresql+asyncpg://user:password@host:5432/database_name

After creation, Qarion runs migrations on the provided database to ensure the schema is up to date.

Managing Instances

Health Monitoring

Check an instance's health from the Instances list by clicking the health indicator. The health check reports:

MetricDescription
ReachableWhether the instance database is accessible
DB SizeDatabase size in megabytes
User CountNumber of registered users
Product CountNumber of data products
Last ActivityTimestamp of the most recent activity
Migration VersionCurrent Alembic migration revision

Suspending and Activating

  • Suspend — Disables routing while preserving all data. The instance's connection pool is disposed to free resources.
  • Activate — Re-enables routing for a suspended instance.

Archiving

Archiving permanently disables an instance and disposes its connection pool. The instance record remains in the control plane for audit purposes but is no longer accessible via the API.

Running Migrations

If an instance falls behind on schema migrations (e.g., after a platform upgrade), use the Migrate action to bring it to the latest revision. For fresh databases, Qarion uses CREATE ALL + stamp head for fast initialization; for existing databases, it runs alembic upgrade head.

Instance Users

Each instance has its own user base, independent of the control-plane administrators.

Viewing Users

Navigate to Administration → Instances → {instance} → Users to see all users within that instance.

Adding Users

Click Add User and provide:

  • Email — Must be unique within the instance
  • First Name / Last Name
  • Password — Minimum 8 characters

Managing Users

For each user, you can toggle superadmin status or active status, and delete users entirely.

Security

Encrypted Database URLs

Instance connection URLs contain database credentials and are encrypted at rest using Fernet symmetric encryption (AES-128-CBC + HMAC-SHA256). The encryption key is resolved from:

  1. INSTANCE_ENCRYPTION_KEY environment variable (preferred — must be a valid Fernet key)
  2. Fallback: a deterministic key derived from SECRET_KEY via SHA-256

The db_url property on the Instance model transparently encrypts on write and decrypts on read, so application code always works with plain-text URLs while stored values remain opaque tokens.

Instance-Scoped Authentication

JWT tokens are scoped to their originating context (control plane vs. specific instance). This prevents cross-context token reuse — a token issued for one instance cannot be used to access another instance or the control plane.

Frontend Integration

The React frontend detects multi-instance mode automatically. When the app is loaded under /i/{slug}/…, the InstanceContext provider resolves the slug and fetches instance configuration. All navigation links and API calls are then scoped to the current instance.

For single-instance deployments (no /i/ prefix), the context returns null and the app operates in standard mode.

API Reference

All instance management endpoints require superadmin authentication and are prefixed with /admin/instances.

MethodEndpointDescription
GET/admin/instances/instancesList all instances
POST/admin/instances/instancesProvision a new instance
GET/admin/instances/instances/{slug}Get instance details
PATCH/admin/instances/instances/{slug}Update instance config
POST/admin/instances/instances/{slug}/suspendSuspend an instance
POST/admin/instances/instances/{slug}/activateRe-activate an instance
DELETE/admin/instances/instances/{slug}Archive an instance
GET/admin/instances/instances/{slug}/healthHealth check
POST/admin/instances/instances/{slug}/migrateRun migrations
GET/admin/instances/instances/{slug}/usersList instance users
POST/admin/instances/instances/{slug}/usersCreate instance user
PATCH/admin/instances/instances/{slug}/users/{id}Update instance user
DELETE/admin/instances/instances/{slug}/users/{id}Delete instance user

Learn More