Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
53 changes: 52 additions & 1 deletion docs/deploy-environment-variables.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -360,4 +360,55 @@ This will read your .env.production file using dotenvx and sync the variables to

- Trigger.dev does not automatically detect .env.production or dotenvx files
- You can paste them manually into the dashboard
- Or sync them automatically using a build extension
- Or sync them automatically using a build extension

## Multi-tenant applications

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.

<Note>
This is different from [syncing environment variables at deploy time](#sync-env-vars-from-another-service).
Here, secrets are loaded dynamically during task execution, not synced to Trigger.dev's environment variables.
</Note>

### Recommended approach

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.

**Important:** Never pass secrets in the task payload, as payloads are logged and visible in the dashboard.

### Example implementation

```ts
import { task } from "@trigger.dev/sdk";
import { SecretsManagerClient, GetSecretValueCommand } from "@aws-sdk/client-secrets-manager";

export const processTenantData = task({
id: "process-tenant-data",
run: async (payload: { tenantId: string; data: unknown }) => {
// Retrieve tenant-specific secret at runtime
const client = new SecretsManagerClient({ region: "us-east-1" });
const response = await client.send(
new GetSecretValueCommand({
SecretId: `tenants/${payload.tenantId}/supabase-key`,
})
);

const supabaseKey = JSON.parse(response.SecretString!).SUPABASE_SERVICE_KEY;

// Your task logic using the tenant-specific secret
// ...
},
});
```

You can use any secrets service - see the [sync env vars section](#sync-env-vars-from-another-service) for an example with Infisical.

### Benefits

- **Single codebase** - Deploy once, works for all tenants
- **Secure** - Secrets never appear in payloads or logs
- **Scalable** - No project limit constraints
- **Flexible** - Easy to add new tenants without redeploying

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.
8 changes: 8 additions & 0 deletions docs/limits.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -55,6 +55,14 @@ If you add them [dynamically using code](/management/schedules/create) make sure

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

## Projects

| Pricing tier | Limit |
| :----------- | :----------------- |
| All tiers | 10 per organization |

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.

## Preview branches

| Pricing tier | Limit |
Expand Down
10 changes: 10 additions & 0 deletions docs/troubleshooting.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -177,6 +177,16 @@ Make sure that you always use `await` when you call `trigger`, `triggerAndWait`,

View the [rate limits](/limits) page for more information.

### Runs waiting in queue due to concurrency limits

If runs are staying in the `QUEUED` state for extended periods, check your concurrency usage in the dashboard. Review how many runs are `EXECUTING` or `DEQUEUED` (these count against limits) and check if any runs are stuck in `EXECUTING` state, as they may be blocking new runs.

**Solutions:**

- **Increase concurrency limits** - If you're on a paid plan, increase your environment concurrency limit via the dashboard
- **Review queue concurrency limits** - Check if individual queues have restrictive `concurrencyLimit` settings
- **Check for stuck runs** - See if stalled runs are blocking new executions

### `Crypto is not defined`

This can happen in different situations, for example when using plain strings as idempotency keys. Support for `Crypto` without a special flag was added in Node `v19.0.0`. You will have to upgrade Node - we recommend even-numbered major releases, e.g. `v20` or `v22`. Alternatively, you can switch from plain strings to the `idempotencyKeys.create` SDK function. [Read the guide](/idempotency).
Expand Down