API Key Management#
Dokploy's API key system is built on the better-auth library's @better-auth/api-key plugin. Keys are scoped to a user + organization pair — the referenceId on the apikey table points to the owning user , and the organization ID is stored as a JSON string in the metadata column . Every authenticated tRPC call can use either a session cookie or an x-api-key header.
Plugin Registration#
The apiKey plugin is registered in packages/server/src/lib/auth.ts with enableMetadata: true and references: "user". The resulting auth export exposes createApiKey and the internal verifyApiKey callable .
Database Schema#
The apikey table is defined in packages/server/src/db/schema/account.ts. Key columns:
| Column | Purpose |
|---|---|
referenceId | FK → user.id — the owning user |
metadata | JSON string — holds { organizationId } |
key | The raw key value (hashed by better-auth) |
prefix / start | Display-safe fragments shown in the UI |
expiresAt | Optional TTL |
rateLimitEnabled, rateLimitTimeWindow, rateLimitMax | Per-key rate limiting |
remaining, refillAmount, refillInterval | Request-count limiting with refill |
enabled | Soft enable/disable toggle |
Zod Validation Schema (tRPC)#
The input schema apiCreateApiKey is defined at the top of apps/dokploy/server/api/routers/user.ts:
name— required, min length 1prefix— optional stringexpiresIn— optional number (seconds)metadata.organizationId— required string; used to scope the key to an orgrateLimitEnabled,rateLimitTimeWindow,rateLimitMax— optional rate-limit configremaining,refillAmount,refillInterval— optional request-count limiting
tRPC Procedures#
Both procedures live in userRouter in apps/dokploy/server/api/routers/user.ts:
user.createApiKey — protectedProcedure:
- Verifies the caller is a member of
metadata.organizationId. - Calls the
createApiKeyservice function which delegates toauth.createApiKey(...)and then writes themetadataJSON back to the DB. - Emits an audit log entry.
user.deleteApiKey — protectedProcedure:
- Looks up the key by ID.
- Guards that
referenceId === ctx.user.id(only the key owner can delete it). - Hard-deletes the row and emits an audit log entry.
The user.get query also returns a user's keys with id, name, prefix, enabled, expiresAt, and createdAt columns — this is the data source for the profile/settings UI.
Request-Time Validation (validateRequest)#
The validateRequest function in auth.ts handles both auth methods:
- API key path — If an
x-api-keyheader is present, it callsapi.verifyApiKey({ body: { key } }). - Org resolution — After a valid key, it reads
metadata.organizationIdfrom the DB record and looks up the matchingmemberrow to build a mock session withrole,ownerId, andenableEnterpriseFeaturesset correctly . - Session cookie path — Falls back to
api.getSession(...)for cookie-based auth .
Invalid or missing organizationId in metadata short-circuits to { session: null, user: null } .
Key Entry Points#
| File | Purpose |
|---|---|
packages/server/src/lib/auth.ts | Plugin registration, validateRequest, auth.createApiKey export |
packages/server/src/db/schema/account.ts | apikey table + apikeyRelations |
packages/server/src/services/user.ts | createApiKey service (wraps auth.createApiKey + metadata write) |
apps/dokploy/server/api/routers/user.ts | apiCreateApiKey Zod schema, createApiKey & deleteApiKey tRPC procedures |