Managing AI API Keys and Secrets From Local Development to Production
AI applications often hold several high-impact credentials: model-provider keys, vector database tokens, OAuth client secrets, webhook signing keys, and credentials an agent uses to call tools. Putting all of them in one .env file is convenient locally, but it is not a production security model.
This guide follows a secret from a developerโs machine through CI, deployment, runtime, rotation, and incident response.
Environment variables are process data
Every process receives a set of key-value pairs from its parent. A child gets a snapshot; later changes in the parent do not automatically update the running child.
export MODEL_PROVIDER_KEY="development-value"
node server.js
In Node.js the value appears in process.env.MODEL_PROVIDER_KEY; in Python it appears through os.environ. Environment variables are strings, so parse and validate booleans, numbers, URLs, and required values at startup.
They are not inherently encrypted. Depending on the platform and permissions, they may appear in process inspection, crash reports, debug output, deployment settings, or child processes.
A .env file is only a loader convention
The operating system does not automatically read .env. A framework or library loads the file into the process environment.
# .env.example โ names and safe placeholders only
MODEL_PROVIDER_KEY=
MODEL_PROVIDER_MODEL=
AI_GATEWAY_URL=http://localhost:3000
Commit .env.example, not .env. Add real secret files to ignore rules before creating them, and assume any secret ever committed to Git must be revokedโeven if the commit is later removed.
Use separate development credentials with low budgets and narrow access. A shared production key should never be required to run the project locally.
Validate configuration at startup
Fail closed when a required credential is absent:
const required = ['MODEL_PROVIDER_KEY', 'INTERNAL_APP_KEY'];
for (const name of required) {
if (!process.env[name]) {
throw new Error(`Missing required configuration: ${name}`);
}
}
Do not log the value while reporting the error. Validate incompatible combinations tooโfor example, a production environment using a development callback URL.
Keep provider keys on the server
Any value shipped to browser JavaScript or a mobile binary must be treated as public. Framework prefixes such as PUBLIC_, NEXT_PUBLIC_, or VITE_ commonly opt variables into client bundles.
The safe flow is:
Browser -> authenticated application backend -> AI gateway/provider
Your backend authenticates the user, enforces quotas and model policy, and retrieves the provider credential at runtime. The AI authentication guide explains how user identity differs from a backend credential.
CI should inject, not print, secrets
Store CI credentials in the platformโs protected secret facility. Restrict which branches, environments, and maintainers can use production values. Avoid passing secrets in command-line arguments, generated artifacts, cache keys, or test snapshots.
Forked pull requests should not receive deployment secrets by default. Prefer short-lived cloud identity or workload federation over a permanent cloud key where the platform supports it.
Containers do not make secrets safe
Never bake credentials into a Docker image through COPY, ARG, or a generated configuration layer. Image layers and registries can retain them.
Inject secrets at runtime. In Kubernetes, a Secret object improves separation from ordinary configuration but is not automatically a complete encrypted vault. Control RBAC, encryption at rest, namespace access, backups, and which pods can mount each secret.
Restart or reload workloads deliberately after rotation; a running process retains its old environment snapshot.
Serverless and hosting platforms
Use environment-scoped values for preview, staging, and production. Confirm whether a changed secret affects existing deployments or only new ones. Restrict dashboard access and audit who can reveal or modify values.
Preview deployments should use sandbox accounts and disposable keys. A public pull request must not be able to trigger an expensive model endpoint using a production budget.
Secret managers and workload identity
For production, a managed secret store can provide encryption, access policy, audit logs, and versioning. Applications can fetch a value at startup or through a controlled runtime client.
Where possible, use workload identity to authenticate the service to cloud resources without distributing a long-lived credential. You may still need provider API keys, but the identity can restrict which workload is allowed to retrieve them.
Separate identities by purpose
Do not use one unrestricted key everywhere. Separate credentials by:
- development, staging, and production;
- service or workload;
- model provider and account;
- customer or tenant where isolation requires it;
- background agent versus interactive application;
- read-only and write-capable tools.
An agent should not inherit every secret available to its host process. Give the tool executor only the credential needed for the approved action. OAuth grants should remain tied to the user and scope; read OAuth for agents and MCP servers.
Rotate without an outage
A practical rotation procedure is:
- Create a second credential with the intended limits.
- Deploy or reload consumers to use it.
- Verify traffic and error rates.
- Revoke the previous credential.
- Confirm it no longer appears in runtime configuration.
- Record the owner and next review date.
Some providers do not support overlapping credentials. In that case, plan a controlled maintenance window or route traffic through an AI gateway that centralizes credential changes.
Prevent leakage through logs and errors
Redact authorization headers, cookies, signed URLs, webhook signatures, and known secret field names. Avoid logging full request objects. Prompts and tool arguments may themselves contain credentials copied by a user.
Test redaction against success, validation failure, provider failure, timeout, and crash paths. Ensure monitoring alerts and support exports do not reintroduce the raw value.
If a secret leaks
Treat exposure as an incident:
- Revoke or rotate the credential immediately.
- Disable affected sessions or OAuth grants when relevant.
- Check provider usage, spend, tool actions, and audit logs.
- Contain the source: repository, log sink, artifact, browser bundle, or support ticket.
- Replace credentials in every environment that shared the value.
- Document impact and prevention measures.
Deleting a value from Git or a log does not undo exposure. Rotation is the recovery mechanism.
Production checklist
- No production credential is required for local development.
- Real
.envfiles and generated secret files are excluded from version control. - Browser bundles contain no provider or backend credentials.
- Preview, staging, and production use separate keys and budgets.
- CI and workloads receive the smallest necessary access.
- Agent tools do not inherit unrelated credentials.
- Logs, traces, errors, and support exports redact secrets.
- Every credential has an owner, rotation path, and revocation test.
- Leak response includes usage and cost review.
Secret handling connects the AI Security and AI Application Architecture layers. Environment variables remain a useful delivery mechanism, but identity, scoping, audit, rotation, and revocation are what make the system defensible.
Database clients such as Prisma
Tools such as Prisma often read DATABASE_URL while loading their configuration. An โenvironment variable not foundโ error can mean the value exists in one shell or deployment environment but not in the process running the CLI, migration or application.
Validate the effective environment in local development, CI, preview and production separately. Do not print the connection string to prove it exists. Check presence and expected format, ensure the correct environment file or platform secret is loaded, and give migration jobs credentials distinct from ordinary application traffic where practical.
For deployment migrations, fail before changing the schema if required configuration is absent. Never solve a missing production database variable by committing a populated .env file.