--- title: "Managing Secrets and Environment Variables in n8n" canonical: "https://infisical.com/blog/n8n-secrets-management" published: "2026-09-17" tags: ["Technical"] source-index: https://infisical.com/llms.txt --- # Managing Secrets and Environment Variables in n8n When AI agents went mainstream, n8n went from a niche workflow automation tool to the backbone of many AI agents. n8n was perfectly positioned for the AI agent boom because it reads and writes data, makes API calls, and executes workflows across hundreds of services. This wealth of services also means that n8n requires a lot of API keys, auth tokens, and other credentials you'd rather not leak. This has always been true, but is becoming more important if you don't want to hand AI your secrets. This makes n8n secrets management trickier than ever. ## Why you need to manage secrets in n8n n8n's purpose is connecting to other systems, so a single instance accumulates credentials to many services. Let's say you have a basic n8n automation that gives the support team context about the customers whose tickets they're working on. That single workflow needs to access all sorts of infrastructure: * CRM * Data warehouse * Payment processor * Ticketing system * Internal APIs An instance may contain many workflows, and therefore a wealth of auth tokens, keys, and passwords. This has two consequences: 1. n8n is a security surface because it contains so many sensitive credentials, so you need to be intentional. 2. Workflows can break if a credential is rotated (invalidated and replaced with a new one) or if a connection expires, which disrupts day-to-day work and requires manual work to debug the issue. Good [secrets management](https://infisical.com/blog/secrets-management-complete-guide) (in n8n and elsewhere) exists to improve your security posture and minimize time spent managing credentials. To do this well, you first need to understand how n8n manages credentials. ## How does n8n handle credentials by default? A credential in n8n is its own object, separate from the workflows that use it. You pick the credential type for each service. That might be Stripe, Postgres, Google Sheets, or one of hundreds of others. You fill in the fields it asks for (either by pasting the key/token directly or making a direct connection to the app) and save it. n8n stores these values in its own encrypted database. Nodes then point at that credential inside n8n and don’t hold the values themselves. This lets one credential serve any number of workflows, but also means a single expiry or revocation breaks all of them at once. The database is encrypted with `N8N_ENCRYPTION_KEY`. If you never set that variable, n8n generates a random key on first launch. This works for individuals or small teams building building simple automations. It stops working well when: * More people can open the instance than you intended * When the values have to change. ## Who can read a credential you save in n8n? n8n doesn’t show a saved credential’s value in the editor. A colleague can select a shared credential in a node and run the workflow, but won't be able to view or edit the credential details. Instance owners and admins can view every credential on the instance. But credential sharing isn’t available for everyone: * On Cloud, it's available on every plan. * If you’re self-hosting, it requires a Business or Enterprise plan. * The Community edition has no sharing at all. n8n itself can also read your secrets. This may sound obvious (since it needs keys and tokens to authenticate the services it connects to), but creates a security consideration: if your n8n instance is compromised, every secret it can read may also be. Workflows can include a Code node that runs arbitrary JavaScript. While the setting `N8N_BLOCK_ENV_ACCESS_IN_NODE` (which lets nodes read the environment directly) sits at its default of `false`, any Code node any author writes can return the credentials. That includes: * The credential database password and every key or token stored in it. * The encryption key to n8n’s credential store (if you set `N8N_ENCRYPTION_KEY` directly). Technically, [n8n’s docs explain](https://docs.n8n.io/deploy/host-n8n/configure-n8n/set-up-task-runners) that the Code node’s task runners are “the only isolation layer between user-provided code and n8n.” n8n warns that without them "anyone who can edit a workflow could potentially read your database, encryption key, stored credentials, and environment variables." Anyone who can author a workflow can read your credentials, and that’s not ideal for most organizations. That’s why many teams use tools like n8n with a separate secrets manager to keep their credentials safe and their overhead low. ## What does a secrets manager do? A secrets manager is a separate system that holds credentials in an encrypted store, decides who can read them, and delivers them to applications like n8n and others. Storing secrets in a secrets manager enables a few things you can’t do when secrets live in n8n: * Managing secrets in one centralized store. The Stripe key n8n uses is probably also in a backend service and a CI pipeline. A secrets manager can deliver the same secret to each place. * One place to change a value, so rotating a key doesn't mean manually replacing it in five tools. * Access rules per identity, so the people and workloads who can read a credential (e.g. the Stripe key) can read only what they have access to, not every credential that exists. * A record of what read which secret and when, with granular audit logs. In a secrets manager like Infisical (which, like n8n itself, offers a [free open-source version](https://github.com/Infisical/infisical)), n8n gets a machine identity with its own permissions. Some secrets managers (including Infisical) also offer dynamic secrets, which are short-lived credentials the manager generates on request. There are multiple routes to get a secrets manager's values into an n8n credential field. ## Where should your credential values come from? n8n gives you several routes to get credentials into the right field when a workflow runs, but each works slightly differently.
| Where the value comes from | Mechanism | Availability | Rotation reaches a running instance | Lands in execution data | | :---- | :---- | :---- | :---- | :---- | | Typed into n8n and stored there | n8n's credential store | Any | By hand, per credential | No | | An environment variable on the n8n process | `$env` in a credential field | Self-hosted only | Only on restart | No | | Set once at startup, hidden from the editor | Credential overwrites | Self-hosted, no licence stated | On restart, or live via the endpoint | No | | Read from a secrets manager at execution time | External secrets, `{{ $secrets.infisical.NAME }}` | Enterprise, either | Within the poll interval | No | | Fetched by the workflow itself | HTTP Request against the secrets API | Any | Every execution fetches fresh | Yes, by default |
The first row is the default, and the second is common advice that is well-intentioned, but is not automatically secure and automated. The latter three are typical workflows for more advanced teams. ### Using an environment variable If you self-host n8n and can control its deployment, you can keep a secret out of n8n’s database by expressing credentials as an environment variable `={{ $env["STRIPE_API_KEY"] }}` and passing the real value to n8n as an environment variable. The credential field holds the expression instead of the key itself, and n8n resolves it at execution time by reading `STRIPE_API_KEY` from its own process environment. This moves the credential out of n8n’s encrypted database and into your unencrypted deployment config, which is a less secure place to keep it. It also means self-hosting, since the variable gets there by whatever launches n8n: * An `environment:` or `env_file:` block in your compose file * A `docker run -e` flag * A `systemd` unit * A Kubernetes Secret consumed with `envFrom` Adding credentials as environment variables in n8n keeps plaintext secrets out of n8n itself, but has two downsides: * Anyone on the host can read the plaintext secret in a `.env` file, so it can leak into git, logs, crash dumps, or elsewhere. * Rotated credentials still need to be replaced manually. You can resolve these issues by populating the `.env` with a secrets manager in the first place. In that case, managing your secrets in n8n becomes an extension of secrets management in [Docker](https://infisical.com/blog/docker-secrets-management), [Kubernetes](https://infisical.com/blog/kubernetes-secrets-management), or whatever deployment model you use. ### Credential overwrites, for anything shared Credential overwrites pass credentials as environment variables without either downside above. n8n injects them server-side rather than resolving an expression, so `N8N_BLOCK_ENV_ACCESS_IN_NODE` can stay `true` (which keeps n8n nodes from reading environment variables). The variable `CREDENTIALS_OVERWRITE_DATA_FILE` itself holds a path rather than values, so n8n reads the credentials off disk itself and secrets never enter the environment. You can keep individual values as separate secrets in your secrets manager and template that file from them at startup, which keeps them out of your repository. In practice, you set the shared fields of a credential *type*, not a credential, as minified JSON keyed by n8n's internal type name:
```json {"microsoftOutlookOAuth2Api":{"clientId":"CLIENT_ID","clientSecret":"CLIENT_SECRET"}} ```
n8n hides those fields from the editor. Users still create their own credential, get a "Managed OAuth2" option, and click connect without ever seeing the client secret. This is the ideal route to try on the Community edition because it requires no license. A limitation is that one value covers every credential of that type, which means you can’t handle multiple credentials for the same service in the workflow. ### How to pull external secrets into n8n from a secrets manager n8n’s enterprise edition (both self-hosted and managed) supports reading credentials from a secrets manager directly, with [Infisical as one of six supported providers](https://docs.n8n.io/administer/manage-credentials/use-external-secret-stores#infisical). It’s the easiest way to manage secrets safely and automate the workflows around them. Once you have Infisical set up, connect n8n to it with a machine identity and reference a secret in a credential field:
``` {{ $secrets.infisical.STRIPE_API_KEY }} ```
n8n then resolves the value when the workflow runs. When a secret rotates in the secrets manager, n8n picks up the new one on its next poll. You can’t pass secrets in non-credential fields. This is a good thing because it prevents insecure processes that leak plaintext secrets into configuration, AI prompts, or similar issues. Finally, you can also fetch secrets directly in workflows. ### How to fetch a secret inside the workflow This is a bit of an edge case: when a credential must live outside of a credential field (e.g. a URL, a query, a header, or Code node), an HTTP Request node can call the secrets API directly and hand the value to later nodes. To make this secure, create a Custom Auth credential for n8n to authenticate to Infisical. Custom Auth accepts `headers`, `qs`, and `body`, so the values live in the encrypted credential store and n8n adds them to the login request for you:
```json {"body": {"clientId": "<client-id>", "clientSecret": "<client-secret>"}} ```
An HTTP Request node using that credential posts to `/api/v1/auth/universal-auth/login` and gets back an `accessToken`. A second node uses it as a bearer token to fetch the secret, where `secretPath` is the folder it sits in:
``` GET https://app.infisical.com/api/v4/secrets/STRIPE_API_KEY ?projectId=<project-id>&environment=prod&secretPath=/n8n ```
The value arrives at `secret.secretValue` and passes onward as ordinary workflow data. This route means the only secret in the n8n credential store is a bootstrap credential that fetches the rest. If you use this method, modify the default log settings to keep plaintext secrets from leaking into execution history. `EXECUTIONS_DATA_SAVE_ON_SUCCESS` and `EXECUTIONS_DATA_SAVE_ON_ERROR` both default to `all`, so the node's response body is written to the database on every run, where anyone can read it. These methods work to better manage credentials in n8n, but they don’t solve the question of how to administrate n8n itself. ## How do you separate dev, staging, and production secrets in n8n? One n8n instance has one secret context, but you may not want a development instance to read production secrets. The best way to solve this is to separate n8n instances and use a secrets manager to give them separate machine identities, with access scoped to the exact credentials they need. Another issue is secret rotation: What happens if a credential is invalidated and replaced with a new one? ## How do you rotate a secret without breaking a running n8n workflow? Rotation reaches a running instance differently depending on which route you picked, which is worth knowing before you rotate something a scheduled workflow depends on. If you use n8n’s credential store, invalidating the credential it uses breaks every workflow using that credential ([dual-phase rotation](https://infisical.com/docs/documentation/platform/secret-rotation/overview) remedies this). The only way to rotate is to manually copy-paste the new secret wherever it was used. If you use a secrets manager, this can run automatically: * **External secrets** pick up the new value in the poll interval. The default is 300 seconds, but you can lower it if your workflows run more frequently. * **Environment variables** are read once at boot. n8n never re-reads its environment, so a rotated credential will require a restart to arrive. * **Credential overwrites** function as an environment variable, so they usually need a restart too. * **Workflow-level fetches** will always pull the latest secret the moment the API call arrives. Now that n8n powers many AI agent workflows, it’s important to keep AI agents from reading secrets. ## What changes when a model chooses the credential? An agent node treats credentials differently than a conventional one: * A classic workflow maps credentials statically. Reading the exported JSON tells you exactly which systems a run can touch. * Attach credentials to an agent as tools and it can reach any credential for any tool it has access to, resolved at runtime. The only way to prevent an AI from sending your sensitive credentials to LLM providers or leaking them in a prompt injection is to keep values out of workflow data entirely. If you use [Credential brokering](https://infisical.com/blog/credential-brokering-for-ai-agents), you never need to hand the agent a credential at all. Setting up Infisical’s [Agent Proxy](https://infisical.com/blog/agent-proxy) to do this lets n8n-powered agents use any service they need without being able to read credentials. ## Using Infisical as your secrets manager with n8n The reason n8n concentrates risk in the first place is that it touches every other system you run and has credentials for all of them. A secrets manager like [Infisical](https://infisical.com/blog/secrets-management-complete-guide) acts as one home for secrets values, access, and credential brokering for AI workflows. This way, you can keep credentials out of n8n entirely without breaking any workflows. Infisical is open source and self-hostable, so the instance holding your n8n credentials can run wherever n8n does. [Start with a free account](https://app.infisical.com) or [talk to an expert](https://infisical.com/talk-to-us).
/blog/n8n-secrets-management.md · raw source