Working with n8n Variables: A Comprehensive Guide
To properly use an n8n variable, you must first understand that n8n offers two distinct methods for storing and accessing data that function like variables: instance-level Custom Variables and execution-level Workflow Static Data. Custom Variables (`$vars`) are read-only, instance-wide values ideal for storing static configurations like API keys or environment-specific URLs. Conversely, Workflow Static Data (`getWorkflowStaticData()`) provides a read/write storage space that persists only for the duration of a single workflow execution, making it perfect for dynamic tasks like tracking items in a loop or passing data between complex branches.
Let’s talk about a topic that trips up even seasoned n8n users: variables. You’d think it would be simple, right? Just declare a variable and use it. But in the world of n8n, the answer is a classic “it depends.” The confusion often stems from not realizing there are different tools for different jobs. Getting this right is the difference between a brittle, confusing workflow and a robust, elegant automation. As an n8n professional, I’ve seen this firsthand countless times—it’s a foundational concept that unlocks truly powerful workflow design.
The Two Main “Variable” Types in n8n: What’s the Difference?
At its core, the choice comes down to one question: Is your data static or dynamic? Is it something you set once and use everywhere, or is it something that needs to change *during* a workflow run? Let’s break down the two main approaches.
Instance-Level Custom Variables (The `$vars` Object)
Think of Custom Variables as constants written on a giant whiteboard in your office. Everyone in your n8n instance can see them and use them, but no one can change them with an eraser (at least, not from within a workflow). This feature, primarily available on Self-hosted Enterprise and Pro/Enterprise Cloud plans, is a lifesaver for managing configurations.
Here’s the lowdown:
- Scope: Global. Available to every workflow in your n8n instance.
- Mutability: Read-only within a workflow. You must use the n8n UI to set or change their values.
- Access: Simple and clean, using the `$vars` object. For example:
{{ $vars.my_api_key }}
.
The killer use case? Managing environments. Let’s say you have a development, staging, and production setup. Instead of hardcoding the API base URL in every HTTP Request node, you can create a variable called api_base_url
. In your dev instance, you set its value to https://dev-api.mycompany.com
. When you move the workflow to production, you just update the variable’s value in the production instance’s UI to https://api.mycompany.com
. No workflow edits required. It’s clean, efficient, and far less error-prone.
Execution-Level Static Data (`getWorkflowStaticData`)
Now, here’s where it gets interesting. What if you need a variable that changes *during* a workflow? This is the problem that pops up constantly in community forums, especially with loops. For this, we turn to Workflow Static Data.
The name is a bit of a misnomer; it’s anything but static. I prefer to think of it as an “execution scratchpad.” It’s a temporary data store that is born when your workflow starts and dies when it finishes. Each execution gets its own, clean scratchpad.
Key characteristics:
- Scope: Execution-level. The data is only available within the single run of the workflow where it was created.
- Mutability: Read/write. You can create, update, and delete values from it using a Code node.
- Access: Accessed via the built-in
getWorkflowStaticData()
function within a Code node.
Real-World Example: Taming a Loop with Static Data
Let’s tackle a common scenario. You need to process a list of 100 customer records from a database and send each one to an external API. You want to keep a running count of how many succeeded and how many failed.
If you used a Set node inside the loop to increment a counter, you’d quickly find that its value isn’t easily accessible across different branches or after the loop finishes cleanly. It gets messy. Here’s the elegant solution using static data:
- Initialization (Before the Loop): Add a Code node to set up your scratchpad.
const staticData = getWorkflowStaticData();
// Initialize counters
staticData.successCount = 0;
staticData.failureCount = 0;
return []; // Pass along the original data to the loop
- Inside the Loop (After the API call): Use an IF node to check if the API call was successful.
- Increment Success: On the “true” path of the IF node, add a Code node to increment the success counter.
const staticData = getWorkflowStaticData();
staticData.successCount++;
return item;
- Increment Failure: On the “false” path, add a Code node to increment the failure counter.
const staticData = getWorkflowStaticData();
staticData.failureCount++;
return item;
- Final Report (After the Loop): Add a final Code node to retrieve the totals from your scratchpad and format a report.
const staticData = getWorkflowStaticData();
const report = {
message: "Processing complete!",
successful_records: staticData.successCount,
failed_records: staticData.failureCount
};
return report;
With this method, you have a reliable state that persists across all iterations and branches of your single workflow execution. It’s a game-changer.
Choosing Your n8n Variable Strategy
So, how do you decide what to use? Let this simple table be your guide.
Goal | Recommended Method | Best For… | Watch Out For… |
---|---|---|---|
Store a static value for all workflows (e.g., an API key or Base URL). | Custom Variables ($vars ) |
Managing configurations, environment settings, and preventing hardcoded secrets. | This is an Enterprise/Pro feature. The value is read-only in workflows. |
Track a changing value within a single workflow run (e.g., a loop counter). | Workflow Static Data | Loops, complex branching logic, and maintaining state during one execution. | Data is erased after the execution ends. It’s not for persistence between runs. |
Share a value between different workflow runs (e.g., last run timestamp). | External Storage (Database/Sheet) | Creating stateful workflows that need to remember past events. | Adds complexity and an external dependency (e.g., Redis, Postgres, Google Sheets). |
Let’s be honest, sometimes you need something that persists between runs but doesn’t require a full database setup. For self-hosted users, the community has developed nodes like @telepilotco/n8n-nodes-kv-storage
that provide a simple in-memory key-value store. It’s a fantastic middle ground, but be aware that since it’s in-memory, the data is typically lost if you restart your n8n instance.
Mastering these variable concepts is fundamental to leveling up your n8n skills. By understanding the distinction between instance-wide constants and execution-scoped data, you can build automations that are not only more powerful but also significantly easier to maintain and debug. Happy automating!