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 logA 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 isolation | Complete | Shared MongoDB, prefix-based |
| Backups | Independent | Coupled |
| Scaling | Independent | Coupled |
| Resource overhead | Slightly higher | Lower |
| ACL leakage risk | None | Real |
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
- Parse Server level
- Unique
appIdandmasterKeyper instance. - No shared credentials.
- Master-key IP restrictions.
- Unique
- Database level
- Separate database per tenant.
- MongoDB authentication per instance.
- Encrypted connections.
- Container level
- Isolated Docker networks.
- No inter-container communication.
- Resource limits enforced (CPU + memory per tier).
- SSH deployment
- Encrypted credential storage (AES-256-GCM in
encryption.ts). - Key-based authentication only.
- Audit log for every deploy.
- Encrypted credential storage (AES-256-GCM in
Deployment strategies
┌────────────────────────┐
│ Parse Server Manager │
│ (provision · health │
│ · billing · resource) │
└────────────────────────┘
│
┌─────────────────┼─────────────────┐
▼ ▼ ▼
┌────────────┐ ┌────────────┐ ┌────────────┐
│ DPS-Hosted │ │ SSH Deploy │ │ Kubernetes │
│ (Docker) │ │ (Client │ │ (Future) │
│ │ │ VPS) │ │ │
└────────────┘ └────────────┘ └────────────┘| Strategy | Status | Used for |
|---|---|---|
| DPS-Hosted (Docker) | 90% | Free / Developer / Pro tiers, default |
| SSH Deploy (client VPS) | 90% | Pro / Enterprise customers running on their own infra |
| Kubernetes | 0% | 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:
| Field | Example | Purpose |
|---|---|---|
appId | duabanti | Top-level grouping (typically slug-equal to storeId). |
storeId | duabanti | Sellub storeId for the channel; resolves Paystack subaccount. |
referencePrefix | DNTI | ≤ 8 chars; prepended to every Paystack reference for traceability. |
For Vendure-flow checkouts a third identifier is added:
| Field | Example | Purpose |
|---|---|---|
channelToken | (opaque) | Looks up the VendureChannel row mapping to {appId, storeId, adminApiUrl, adminToken, label}. |