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
| Status | Behavior |
|---|---|
| Active | Instance is fully operational. API requests are routed to its database. |
| Suspended | Routing is disabled (returns HTTP 503). Data is preserved. Useful for maintenance or billing holds. |
| Archived | Instance 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:
- Creates a dedicated PostgreSQL database on the same server as the control plane (e.g.,
qarion_acme_corp) - Creates a dedicated database user with a 32-character random password
- Runs all Alembic migrations to initialize the schema
- Encrypts and stores the connection URL
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:
| Metric | Description |
|---|---|
| Reachable | Whether the instance database is accessible |
| DB Size | Database size in megabytes |
| User Count | Number of registered users |
| Product Count | Number of data products |
| Last Activity | Timestamp of the most recent activity |
| Migration Version | Current 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:
INSTANCE_ENCRYPTION_KEYenvironment variable (preferred — must be a valid Fernet key)- Fallback: a deterministic key derived from
SECRET_KEYvia 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.
| Method | Endpoint | Description |
|---|---|---|
GET | /admin/instances/instances | List all instances |
POST | /admin/instances/instances | Provision a new instance |
GET | /admin/instances/instances/{slug} | Get instance details |
PATCH | /admin/instances/instances/{slug} | Update instance config |
POST | /admin/instances/instances/{slug}/suspend | Suspend an instance |
POST | /admin/instances/instances/{slug}/activate | Re-activate an instance |
DELETE | /admin/instances/instances/{slug} | Archive an instance |
GET | /admin/instances/instances/{slug}/health | Health check |
POST | /admin/instances/instances/{slug}/migrate | Run migrations |
GET | /admin/instances/instances/{slug}/users | List instance users |
POST | /admin/instances/instances/{slug}/users | Create instance user |
PATCH | /admin/instances/instances/{slug}/users/{id} | Update instance user |
DELETE | /admin/instances/instances/{slug}/users/{id} | Delete instance user |
Learn More
- User Management — Managing users and roles
- Permissions — Role-based access control