Skip to content

Commit ec69953

Browse files
committed
Multi-tenant applications
1 parent 7162a27 commit ec69953

File tree

2 files changed

+60
-1
lines changed

2 files changed

+60
-1
lines changed

docs/deploy-environment-variables.mdx

Lines changed: 52 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -360,4 +360,55 @@ This will read your .env.production file using dotenvx and sync the variables to
360360

361361
- Trigger.dev does not automatically detect .env.production or dotenvx files
362362
- You can paste them manually into the dashboard
363-
- Or sync them automatically using a build extension
363+
- Or sync them automatically using a build extension
364+
365+
## Multi-tenant applications
366+
367+
If you're building a multi-tenant application where each tenant needs different environment variables (like tenant-specific API keys or database credentials), you don't need a separate project for each tenant. Instead, use a single project and load tenant-specific secrets at runtime.
368+
369+
<Note>
370+
This is different from [syncing environment variables at deploy time](#sync-env-vars-from-another-service).
371+
Here, secrets are loaded dynamically during task execution, not synced to Trigger.dev's environment variables.
372+
</Note>
373+
374+
### Recommended approach
375+
376+
Use a secrets service (Infisical, AWS Secrets Manager, HashiCorp Vault, etc.) to store tenant-specific secrets, then retrieve them at the start of each task run based on the tenant identifier in your payload or context.
377+
378+
**Important:** Never pass secrets in the task payload, as payloads are logged and visible in the dashboard.
379+
380+
### Example implementation
381+
382+
```ts
383+
import { task } from "@trigger.dev/sdk";
384+
import { SecretsManagerClient, GetSecretValueCommand } from "@aws-sdk/client-secrets-manager";
385+
386+
export const processTenantData = task({
387+
id: "process-tenant-data",
388+
run: async (payload: { tenantId: string; data: unknown }) => {
389+
// Retrieve tenant-specific secret at runtime
390+
const client = new SecretsManagerClient({ region: "us-east-1" });
391+
const response = await client.send(
392+
new GetSecretValueCommand({
393+
SecretId: `tenants/${payload.tenantId}/supabase-key`,
394+
})
395+
);
396+
397+
const supabaseKey = JSON.parse(response.SecretString!).SUPABASE_SERVICE_KEY;
398+
399+
// Your task logic using the tenant-specific secret
400+
// ...
401+
},
402+
});
403+
```
404+
405+
You can use any secrets service - see the [sync env vars section](#sync-env-vars-from-another-service) for an example with Infisical.
406+
407+
### Benefits
408+
409+
- **Single codebase** - Deploy once, works for all tenants
410+
- **Secure** - Secrets never appear in payloads or logs
411+
- **Scalable** - No project limit constraints
412+
- **Flexible** - Easy to add new tenants without redeploying
413+
414+
This approach allows you to support unlimited tenants with a single Trigger.dev project, avoiding the [project limit](/limits#projects) while maintaining security and separation of tenant data.

docs/limits.mdx

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -55,6 +55,14 @@ If you add them [dynamically using code](/management/schedules/create) make sure
5555

5656
If you're creating schedules for your user you will definitely need to request more schedules from us.
5757

58+
## Projects
59+
60+
| Pricing tier | Limit |
61+
| :----------- | :----------------- |
62+
| All tiers | 10 per organization |
63+
64+
Each project receives its own concurrency allocation. If you need to support multiple tenants with the same codebase but different environment variables, see the [Multi-tenant applications](/deploy-environment-variables#multi-tenant-applications) section for a recommended workaround.
65+
5866
## Preview branches
5967

6068
| Pricing tier | Limit |

0 commit comments

Comments
 (0)