DPSMulti-Tenancy & Isolation

Multi-Tenancy & Isolation

DPS uses a database-per-tenant isolation model. Every customer Parse Server instance gets its own MongoDB database, its own Docker container, its own port, and its own credentials.

Hierarchy

Organizations
  └── Projects
        └── Apps  (parse-server | sellub | react-web | react-native | n8n)
              └── Deployments
              └── Metrics
              └── Activity log

A user belongs to one or more Organizations. Each Organization owns Projects, and each Project owns one or more Apps. Every app has its own deployments, metrics history, and audit trail.

Isolation strategy

DPS deliberately picks Option 1 — Database-per-Tenant over the cheaper Collection-per-Tenant model. Security and isolation trump resource efficiency.

Database-per-Tenant (chosen)Collection-per-Tenant (rejected)
Data isolationCompleteShared MongoDB, prefix-based
BackupsIndependentCoupled
ScalingIndependentCoupled
Resource overheadSlightly higherLower
ACL leakage riskNoneReal

Network isolation

Each tenant Parse Server runs in an isolated Docker container:

{
  network: "isolated_network_<instanceId>",
  ports: {
    internal: 1337,
    external: dynamicPort, // 8000-9000 range
  },
  environment: {
    PARSE_APP_ID: "unique_per_instance",
    PARSE_MASTER_KEY: "generated_secure_key",
    DATABASE_URI: "mongodb://localhost:27017/tenant_<instanceId>",
  },
  resources: {
    cpuLimit: tierConfig.cpuLimit,
    memoryLimit: tierConfig.memoryLimit,
  },
}

Access control layers

  1. Parse Server level
    • Unique appId and masterKey per instance.
    • No shared credentials.
    • Master-key IP restrictions.
  2. Database level
    • Separate database per tenant.
    • MongoDB authentication per instance.
    • Encrypted connections.
  3. Container level
    • Isolated Docker networks.
    • No inter-container communication.
    • Resource limits enforced (CPU + memory per tier).
  4. SSH deployment
    • Encrypted credential storage (AES-256-GCM in encryption.ts).
    • Key-based authentication only.
    • Audit log for every deploy.

Deployment strategies

              ┌────────────────────────┐
              │ Parse Server Manager   │
              │ (provision · health    │
              │  · billing · resource) │
              └────────────────────────┘

        ┌─────────────────┼─────────────────┐
        ▼                 ▼                 ▼
  ┌────────────┐   ┌────────────┐    ┌────────────┐
  │ DPS-Hosted │   │ SSH Deploy │    │ Kubernetes │
  │ (Docker)   │   │ (Client    │    │ (Future)   │
  │            │   │  VPS)      │    │            │
  └────────────┘   └────────────┘    └────────────┘
StrategyStatusUsed for
DPS-Hosted (Docker)90%Free / Developer / Pro tiers, default
SSH Deploy (client VPS)90%Pro / Enterprise customers running on their own infra
Kubernetes0%Future — Helm chart + operator on the roadmap

Tenant data model (DPS metadata)

Stored in the master DPS MongoDB; never in tenant DBs.

ParseServerInstance

{
  objectId, name, status, // "provisioning" | "running" | "stopped" | "error" | "migrating"
  project, organization, owner,
  tier, region, deploymentType,
  appId, masterKey, javascriptKey, serverURL, databaseURI,  // credentials encrypted at rest
  storageUsed, storageLimit, requestsThisMonth, requestLimit,
  containerID, dockerImage, port, internalPort,
  sshConfig?, // host, port, username, privateKey (encrypted), deployPath
  healthStatus, lastHealthCheck, uptime,
  billingStatus, nextBillingDate,
  version, environment, customDomain, sslEnabled,
}

ParseServerMetrics

Time-series metrics emitted by the running instance: CPU, memory, storage, network, RPM, response time, error rate, DB connections, query time.

ParseServerDeployment

Per-deployment audit row: status, type (create | update | restart | migrate | scale), trigger user, previous + new config, structured log array, timing, rollback availability.

Tenant resolution

Cloud-function callers always pass two identifiers that DPS uses to scope the request:

FieldExamplePurpose
appIdduabantiTop-level grouping (typically slug-equal to storeId).
storeIdduabantiSellub storeId for the channel; resolves Paystack subaccount.
referencePrefixDNTI≤ 8 chars; prepended to every Paystack reference for traceability.

For Vendure-flow checkouts a third identifier is added:

FieldExamplePurpose
channelToken(opaque)Looks up the VendureChannel row mapping to {appId, storeId, adminApiUrl, adminToken, label}.