Ever shipped n8n with secrets accidentally exposed?
Environment variables are the guardrails that stop that, and this post walks through the ones you actually need for Docker and self-hosted deployments, shows which control auth, database, host and webhook URLs, queues and logging, where to set them (Docker Compose, systemd, Kubernetes, .env), and explains startup vs runtime resolution.
Read on to fix broken webhooks, protect credentials, and avoid the common gotchas that cause midnight incidents.
Complete Overview of n8n Environment Variables for Configuration

n8n environment variables control every part of how your deployment behaves. Database connections, authentication, webhook URLs, logging levels. These variables live outside workflow JSON, which keeps your credentials portable and stops you from accidentally leaking secrets when you export or share workflows. Most n8n installations load environment variables from Docker Compose files, systemd service units, Kubernetes manifests, or .env files, depending on how you deployed.
Configuration happens before n8n starts. Docker Compose reads variables from an environment: block or an external secure.env file. Systemd deployments need Environment= lines in /etc/systemd/system/n8n.service, then a daemon reload. Kubernetes uses ConfigMaps for regular settings and Secrets for credentials. Hostinger’s Docker Manager lets you edit the .yaml configuration right there and click Deploy to restart the container with your updates. Every method follows the same principle: define variables at the infrastructure layer so the n8n process can read them at startup.
Workflows access environment variables at runtime using n8n expressions like {{ $env.TELEGRAMCHATID }}. Variables don’t resolve during editing or preview. They only populate when you actually execute the workflow. This runtime behavior keeps secrets invisible in the workflow editor while letting nodes pull live values from the deployment environment. If you inspect a workflow JSON file, you’ll see the expression syntax but never the actual secret. That’s what solves the critical security problem of embedding API keys in exported workflows.
The most important n8n environment variable groups:
Authentication – N8NBASICAUTHACTIVE, N8NBASICAUTHUSER, N8NBASICAUTH_PASSWORD protect the web UI with HTTP basic auth
Database – DBTYPE, DBPOSTGRESDBHOST, DBPOSTGRESDBUSERNAME, DBPOSTGRESDB_PASSWORD switch from SQLite to PostgreSQL or MySQL
Host and Port – N8NHOST, N8NPORT, N8N_PROTOCOL define the address where n8n listens and generates URLs
Webhook – WEBHOOKURL, WEBHOOKTUNNEL_URL override callback and webhook base URLs when you’re behind reverse proxies
Queue Mode – EXECUTIONSMODE, QUEUEBULLREDISHOST enable distributed execution using Redis-backed queues
Logging – N8NLOGLEVEL, N8NLOGOUTPUT control verbosity and output format for troubleshooting and monitoring
Critical n8n Environment Variables Explained (Database, Auth, Host, Webhooks)

Authentication variables protect your n8n instance when it’s exposed to a network. Set N8NBASICAUTHACTIVE=true to enable HTTP basic authentication, then define N8NBASICAUTHUSER and N8NBASICAUTH_PASSWORD to create login credentials. You need this for any deployment reachable from outside your local machine, even if you’re using a reverse proxy with its own authentication layer. Basic auth acts as a first defense and stops unauthenticated access to the workflow editor and execution history.
Database configuration determines where n8n stores workflows, credentials, and execution logs. The default SQLite database works for single-user setups and development. But production deployments should use PostgreSQL or MySQL for reliability and concurrent access. Set DBTYPE to postgresdb or mysqldb, then provide connection details via DBPOSTGRESDBHOST, DBPOSTGRESDBPORT, DBPOSTGRESDBDATABASE, DBPOSTGRESDBUSER, and DBPOSTGRESDB_PASSWORD. n8n creates the schema automatically on first startup, so you only need to prepare an empty database and grant the user full access.
Host, port, and webhook variables control how n8n generates URLs for itself and for external services that call back into workflows. N8NHOST defines the public domain (like n8n.yourcompany.com), N8NPORT sets the listening port (default 5678), and N8NPROTOCOL specifies http or https. When n8n runs behind Nginx Proxy Manager or another reverse proxy, the process sees only the internal network address. Without WEBHOOKURL set to the public domain, n8n generates localhost callback URLs that break OAuth flows and external integrations. Setting WEBHOOK_URL=https://n8n.yourcompany.com fixes this by overriding the base URL used in all generated webhooks and redirects.
Variable precedence follows a consistent order across deployment types. Explicit environment variables override defaults. Variables defined directly in the service configuration (Docker Compose environment: block or systemd Environment= line) override values from external .env files. When you define the same variable in multiple places, the most specific source wins. For Docker Compose, that means inline environment: entries take priority over env_file: references. For systemd, it means the service unit’s Environment= directive beats system-wide /etc/environment entries. Always check loaded values using docker exec -it n8n printenv or systemctl show n8n to confirm which source took effect.
| Variable | Purpose | Example Value |
|---|---|---|
| WEBHOOK_URL | Override base URL for webhooks and OAuth callbacks when behind reverse proxy | https://n8n.example.com |
| DB_POSTGRESDB_HOST | PostgreSQL server hostname or IP address | postgres.internal.local |
| N8N_BASIC_AUTH_USER | Username for HTTP basic authentication on the web UI | admin |
| N8N_HOST | Public domain name used in generated URLs | n8n.yourcompany.com |
| N8N_LOG_LEVEL | Control verbosity of logs (error, warn, info, verbose, debug) | info |
Configuring n8n Environment Variables in Docker and Docker Compose

Docker Compose injects environment variables into the n8n container at startup using the environment: block in your docker-compose.yml file. You can define variables inline or reference an external file with envfile:. Inline definitions look like environment: – WEBHOOKURL=https://n8n.example.com and are visible in the Compose file itself. Using env_file: – ./secure.env keeps secrets in a separate file that you exclude from version control, which is safer for production. Both methods work identically. n8n reads the variables from the container’s environment at startup, and precedence follows Docker’s standard rules.
YAML formatting is strict. Indentation must use spaces, never tabs, and the environment: block must align with other service-level keys like image: and ports:. Each variable entry starts with a hyphen and a space, like – N8NBASICAUTH_ACTIVE=true. Missing or inconsistent indentation breaks the Compose file and prevents the container from starting. If you’re editing configuration in Hostinger’s Docker Manager or another web-based editor, preserve existing spacing exactly and add new variables following the same pattern. After editing, click Deploy or run docker-compose up -d to restart the container with updated environment variables.
To define and validate environment variables in Docker Compose:
- Open your docker-compose.yml file and locate the n8n service block
- Add or update the environment: section with your variables, like environment: – WEBHOOKURL=https://n8n.example.com – DBTYPE=postgresdb – DBPOSTGRESDBHOST=postgres
- Save the file and run docker-compose down && docker-compose up -d to restart the n8n container
- Verify loaded variables using docker exec -it n8n printenv | grep WEBHOOK_URL (replace n8n with your container name if different)
- Check the n8n web UI or execute a test workflow to confirm variables resolve correctly at runtime
Setting n8n Environment Variables on Bare-Metal and Systemd Services

Systemd-based n8n installations, common in Proxmox LXC containers and Debian servers using Community Scripts, require editing the service unit file at /etc/systemd/system/n8n.service. This file defines how systemd starts and manages the n8n process. Environment variables go inside the [Service] section using the syntax Environment=”VARIABLENAME=value”. Each variable needs its own Environment= line. Like Environment=”WEBHOOKURL=https://n8n.internal.example” sets the webhook base URL. Quotes around the value are optional for simple strings but required if the value contains spaces or special characters.
After editing the service unit, systemd must reload its configuration to recognize the changes. Run systemctl daemon-reload to refresh systemd’s cache, then systemctl restart n8n.service to apply the new environment. Or reboot the entire container or server to guarantee a clean start with all changes active. The first method is faster, but rebooting eliminates edge cases where partial updates leave stale state.
To add and apply environment variables in a systemd n8n service:
- Open /etc/systemd/system/n8n.service in a text editor with root privileges (like sudo nano /etc/systemd/system/n8n.service)
- Locate the [Service] section and add Environment=”VARIABLENAME=value” lines below existing directives. For example, Environment=”WEBHOOKURL=https://n8n.example.com”
- Save the file, then run systemctl daemon-reload to reload the unit configuration
- Restart n8n with systemctl restart n8n.service or reboot the container to apply changes, then verify using systemctl status n8n.service and checking the process environment
Using Environment Variables Directly Inside n8n Workflows

Workflows access environment variables using n8n expression syntax: {{ $env.VARIABLE_NAME }}. This expression resolves to the variable’s value only when the workflow executes, not during editing or preview. You can use environment variable expressions in any node field that accepts expressions. Text inputs, headers, authentication tokens, API endpoints, and Set node mappings. The most common pattern is storing API keys and chat IDs as environment variables, then referencing them in HTTP Request nodes or messaging integrations like Telegram.
The Set node with Manual Mapping mode offers the cleanest way to expose environment variables for debugging or passing them to downstream nodes. Add a Set node to your workflow, switch to Manual Mapping, and create output keys for each variable you want to surface. For each key, click the field’s gear icon to enable expression mode, then enter {{ $env.TELEGRAMCHATID }} or whichever variable you need. When you execute the workflow, the Set node outputs reveal the actual values. This helps verify that n8n loaded the variables correctly and that your expressions match the exact variable names defined in Docker Compose or systemd.
Runtime resolution is intentional. It prevents accidental exposure of secrets in the workflow editor and makes sure the same workflow JSON works across development, staging, and production environments without modification. If you export a workflow that uses {{ $env.APIKEY }}, the exported JSON contains the expression, not the key itself. When you import that workflow into another n8n instance, it automatically pulls the APIKEY value from the new environment’s variables. This makes workflows portable and secure, as long as every environment defines the required variables.
Common workflow use cases for environment variables:
Storing API keys for third-party services (Stripe, SendGrid, Slack) in HTTP Request authorization headers
Defining webhook callback URLs dynamically when the same workflow runs in local, staging, and production environments
Passing database connection strings or credentials to Execute Command or Postgres nodes
Injecting Telegram bot tokens and chat IDs without hardcoding them in workflow JSON
Configuring environment-specific endpoints, like {{ $env.APIBASEURL }}/users vs {{ $env.APIBASEURL }}/orders depending on the deployment
Environment Variable Management for Docker, Kubernetes, and CI/CD

Kubernetes deployments distinguish between ConfigMaps and Secrets when injecting environment variables into n8n pods. ConfigMaps hold non-sensitive configuration like N8NHOST, N8NPORT, and N8NLOGLEVEL. Secrets store base64-encoded credentials such as DBPOSTGRESDBPASSWORD and N8NENCRYPTIONKEY. Both resources are mounted into the container’s environment using envFrom: in the pod spec, but Secrets receive additional protections like restricted RBAC access and optional encryption at rest. Helm charts for n8n typically define a values.yaml file where you declare variables once, and the chart templates populate both ConfigMaps and Secrets automatically based on naming conventions or explicit annotations.
Secret rotation in Kubernetes requires updating the Secret object and restarting affected pods. If you change DBPOSTGRESDBPASSWORD in the Secret but don’t restart the n8n pod, the running process still uses the old password and database connections fail. Kubernetes doesn’t automatically propagate Secret updates to running containers. Use kubectl rollout restart deployment/n8n or configure a sidecar that watches for Secret changes and triggers restarts. For high-availability setups, consider external secret managers like HashiCorp Vault or AWS Secrets Manager, which integrate with Kubernetes via the External Secrets Operator and allow centralized rotation policies.
CI/CD pipelines inject environment variables during n8n deployment by passing them to Docker build, docker-compose, or kubectl commands. GitHub Actions workflows use secrets stored in repository settings and reference them as ${{ secrets.N8NWEBHOOKURL }} in job steps. GitLab CI reads variables from project or group settings and exposes them as shell environment variables during script execution. Terraform and Ansible playbooks accept variables from encrypted vaults or remote state stores, then template them into Docker Compose files or Kubernetes manifests before applying. The key is to keep secrets out of the pipeline definition file itself and pull them from a secure store at runtime.
Multi-environment handling demands clear precedence and naming conventions. Prefix variables with the environment name (PRODWEBHOOKURL, STAGINGWEBHOOKURL) or use separate configuration files (prod.env, staging.env) loaded conditionally by deployment scripts. Kubernetes namespaces naturally segment environments, so you can define different Secrets in the prod and staging namespaces with identical keys but different values. Helm lets you override values.yaml settings per release using –set or additional values files, like helm install n8n-prod ./n8n-chart -f prod-values.yaml. This pattern scales to any number of environments without duplicating manifests.
Secure Handling of Sensitive n8n Environment Variables and Secrets

Hardcoding API keys, database passwords, or OAuth tokens inside workflow nodes creates a critical security problem. When you export a workflow to share with a teammate or back it up, the exported JSON contains every value you typed into text fields. Anyone with access to that file gains access to the credentials. The same risk applies to version control. Committing workflows with embedded secrets to Git exposes them to everyone with repository access, and they remain in the commit history even after you remove them from the latest version.
Environment variables solve this problem by keeping secrets outside workflow JSON. When a node references {{ $env.STRIPEAPIKEY }}, the exported workflow contains the expression, not the key itself. The actual key lives in Docker Compose, systemd, or Kubernetes configuration, which you protect using file permissions, secret management systems, and access control policies. This separation lets you safely share workflows, store them in version control, and rotate credentials without touching workflow definitions.
Best practices for securing sensitive n8n environment variables:
Store all credentials and tokens as environment variables instead of hardcoding them in nodes. Use {{ $env.VARIABLE_NAME }} expressions exclusively.
Protect .env and secure.env files with restrictive permissions (chmod 600) and exclude them from version control using .gitignore.
Use dedicated secret management tools (HashiCorp Vault, AWS Secrets Manager, Azure Key Vault) for production deployments instead of plain text files.
Rotate secrets regularly and update only the environment configuration, not the workflows themselves, to apply new credentials.
Enable audit logging to track who accesses environment variable configuration and when changes occur.
Restrict SSH and console access to the server or container running n8n, and use role-based access control (RBAC) for Kubernetes secrets.
Troubleshooting n8n Environment Variable Issues

Verification always starts by checking what the n8n process actually received. Run docker exec -it n8n printenv to list all environment variables inside the container. Pipe the output through grep to find specific variables: docker exec -it n8n printenv | grep WEBHOOK_URL. If the variable appears with the correct value, n8n loaded it successfully. If it’s missing or shows a different value than expected, the problem is in your Docker Compose file, systemd unit, or Kubernetes manifest, not in n8n itself.
Systemd deployments require extra steps. Run systemctl status n8n.service to confirm the service started without errors. Check the service’s environment using systemctl show n8n.service –property=Environment, which displays all Environment= lines from the unit file. If the variable is defined but n8n still doesn’t see it, verify the service reloaded using systemctl daemon-reload and restarted using systemctl restart n8n.service or a full container reboot. Stale systemd state is a common cause of environment variables not propagating to the running process.
| Issue | Root Cause | Fix | Validation |
|---|---|---|---|
| Workflow expression {{ $env.VAR }} returns empty string | Variable not defined in deployment config or typo in variable name | Add variable to docker-compose.yml or systemd unit, verify exact spelling matches expression | docker exec -it n8n printenv | grep VAR |
| OAuth callback uses localhost instead of public domain | WEBHOOK_URL not set, n8n generates URLs using internal container address | Set WEBHOOK_URL=https://your-public-domain.com and restart container | Check generated webhook URL in workflow execution logs |
| Database connection fails after adding DB_POSTGRESDB_PASSWORD | Container not restarted after environment change, or wrong password in variable | Run docker-compose down && docker-compose up -d or systemctl restart n8n.service | Check n8n startup logs for successful database connection message |
| Systemd service shows Environment= line but printenv doesn’t list variable | systemd configuration not reloaded, or service never restarted after edit | Run systemctl daemon-reload && systemctl restart n8n.service | systemctl show n8n.service –property=Environment |
Final Words
You now have a practical map of n8n environment variables: where to set them (Docker Compose, systemd, secure.env), what the key groups do (auth, DB, host, webhooks, queue, logging), and how they resolve at runtime.
The guide walked through Docker and systemd setup, runtime access via {{ $env.VAR }}, multi-platform patterns (K8s, CI/CD), and secure handling plus troubleshooting. Small fixes like YAML indentation and WEBHOOK_URL updates save hours.
Keep a concise env strategy, rotate secrets, and validate with docker exec or systemctl status. With those checks, n8n environment variables become a reliable, low-friction part of your deployments, and you’re set.
FAQ
Q: What are n8n environment variables and what do they control?
A: n8n environment variables are key-value settings that control runtime behavior—database connections, UI auth, host/port, webhook URLs, queue mode, and logging. They decide how n8n runs and connects to services.
Q: Where can I set n8n environment variables?
A: You can set n8n environment variables in Docker Compose (environment or env_file), systemd unit files (/etc/systemd/system/n8n.service), Hostinger Docker Manager, or secure.env files for secrets.
Q: How do I use environment variables inside n8n workflows?
A: Environment variables in n8n workflows are accessed with expressions like {{ $env.VARIABLE_NAME }}, and they resolve only at workflow execution time, not in the editor preview.
Q: Which environment variables are most critical to configure?
A: The most critical n8n variable groups are authentication (N8NBASICAUTH), database (DBTYPE, DB), host/port (N8NHOST, N8NPORT), webhook (WEBHOOKURL, WEBHOOKTUNNELURL), queue mode, and logging settings.
Q: How do I define env vars in Docker Compose correctly?
A: Define env vars in the Compose environment: block or use env_file (secure.env), keep YAML indentation exact, deploy with docker-compose up -d, then verify with docker exec
Q: How do I set environment variables for a systemd n8n service?
A: For systemd, add Environment=”KEY=value” lines to /etc/systemd/system/n8n.service, then run systemctl daemon-reload and systemctl restart n8n to apply changes and check status with systemctl status n8n.
Q: How should I handle sensitive n8n environment variables and secrets?
A: Keep sensitive variables out of workflow JSON by using secure.env or secret stores, avoid hardcoding credentials, use CI/CD or secret managers, restrict file access, and rotate credentials regularly.
Q: What should I change if n8n webhook callback URLs are incorrect behind a proxy?
A: Set WEBHOOKURL or WEBHOOKTUNNEL_URL to the externally reachable base URL so n8n generates correct callbacks when behind a reverse proxy or NAT, and ensure proxy headers forward host info.
Q: How do I troubleshoot environment variable issues in n8n?
A: Troubleshoot by running docker exec
Q: How does precedence work when the same variable is defined in multiple places?
A: When variables are defined multiple places, the service-level or container-provided value wins: explicit Compose environment overrides env_file, container env overrides image defaults, and systemd governs the systemd service.
