Learnings10 min read933 views

n8n 2.0: changes and context for the 2025 migration

The n8n 2.0 changes announced in December 2025: runner modes, node permissions and migration checks. Use the official guidance for your chosen version.

Written byIdir Ouhab

On this page

Look, I've been working with n8n deployments for over a year now, and when I saw the v2.0 announcement, my first thought wasn't "what cool features," it was "how many production workflows am I going to have to fix?"

Cover image for n8n 2.0: changes and context for the 2025 migration

And you know what? This release is genuinely good, but it's going to break your stuff if you don't prepare.

The December 2025 launch timeline

  • Beta: December 8, 2025
  • Stable: December 15, 2025
  • Recommendation at announcement: test before migrating

Version 1.x will get 3 months of security patches after v2 releases, and then that's it.

The Good Stuff

Autosave is coming. Yes, finally. After how many years of people losing work? Better late than never.

Also: updated canvas interface, new sidebar, and "surprises."

But let's be real, you're not here for UI updates.

The Stuff That Will Actually Break Your Workflows

Security: They're Locking Everything Down

1. Code Node Can No Longer Access Environment Variables

bash
N8N_BLOCK_ENV_ACCESS_IN_NODE=true  # This is now the default

Review Code nodes that rely on environment variables. Move sensitive values into n8n credentials and use nodes or integrations that support those credentials. Disabling the new restriction changes the security boundary; it should not be the default migration fix. 2.0 migration guidance.

2. Task runners are enabled by default

In n8n 2.0, Code-node executions use task runners by default. On a compatible 1.x release, N8N_RUNNERS_ENABLED=true lets you test this behavior before upgrading; the flag is no longer needed from 2.0.

JavaScript supports internal mode. Native Python requires external mode, and n8n recommends external runners with hardening for production or sensitive data. Enabling runners alone does not configure the external containers, their broker connection or their permissions. Runner setup.

3. Python Code Node: Complete Rewrite

They removed Pyodide (the browser-based Python). Now it's native Python only, task runners required, external mode mandatory.

What breaks:

  • _input variable (removed)
  • Dot notation access (removed)
  • Any Python Code node without proper task runner setup (broken)

This is better long-term, but the migration pain is real. Review every Python Code node you have.

4. ExecuteCommand and LocalFileTrigger: disabled by default

These nodes can execute system commands or watch local files, so n8n 2.0 disables them by default. Keep those exclusions unless a reviewed workflow needs a specific node.

NODES_EXCLUDE is the list of nodes not to load. An empty list enables all nodes; it is not a security setting to copy into every deployment. If you deliberately enable one node, remove only that node from your existing exclusion list and preserve the others. Node configuration.

5. OAuth Callbacks Need Authentication

bash
N8N_SKIP_AUTH_ON_OAUTH_CALLBACK=false  # New default

Before upgrading, test every OAuth integration. Slack, Google, whatever you've got connected—test it all.

6. File Operations Are Sandboxed

bash
N8N_RESTRICT_FILE_ACCESS_TO=~/.n8n-files  # New default

ReadWriteFile and ReadBinaryFiles nodes can only touch files in this directory now. Good for security, annoying if you've been reading files from random locations.

7. Configuration file permissions

n8n 2.0 enforces 0600 permissions on its settings file: only its owner can read or write it. Before upgrading, test with N8N_ENFORCE_SETTINGS_FILE_PERMISSIONS=true and check the file's ownership and permissions in the environment where n8n actually runs.

The documented exception is an environment that cannot support these permissions, where enforcement can be disabled. A Windows host is not by itself a reason to disable the check inside a Linux container. Check the actual filesystem and mount behavior. Migration guidance.

Database Changes (The Painful Ones)

MySQL/MariaDB: Removed

Gone. PostgreSQL or SQLite only. This was deprecated in v1.0—if you're still on MySQL, that migration warning dates back to July 2023.

Use the database migration tool or you're screwed.

SQLite: Legacy Driver Removed

Only the pooling driver remains. It uses WAL mode and is the new default; n8n reported up to a 10× speedup in its benchmarks.

bash
DB_SQLITE_POOL_SIZE=2  # Auto-configured

Most people won't notice this change. If you do, it's probably because something was broken before.

Binary Data: No More In-Memory Mode

The default mode that kept binary data in memory during execution? Gone.

Your options:

  • filesystem (default for single instance)
  • database (default for queue mode)
  • s3 (if you're fancy)

Make sure you have disk space. If you're processing large files and suddenly run out of space, this is why.

Behavior Changes (The Subtle Ones)

Subworkflow + Wait Node = Fixed

Before: Parent workflow received input from Wait nodes in child workflows (which made no sense)

Now: Parent workflow receives output from the end of the child workflow (which is correct)

If you have workflows calling subworkflows with Wait nodes, review them. The behavior change is correct, but your logic might depend on the previous broken behavior.

Configuration Hell

dotenv Update

Your .env file parsing is different now.

Changes:

  • Backticks need quotes now
  • An unquoted # starts a comment; wrap values containing # in quotes
  • Multi-line values work now

Review your .env files. Especially if you have weird characters in passwords or tokens.

Removed stuff:

  • QUEUE_WORKER_MAX_STALLED_COUNT
  • CLI option n8n --tunnel (use ngrok or Cloudflare Tunnel)
  • update:workflow --all --active=true (good riddance, this was dangerous)

Removed nodes:

  • Spontit
  • crowd.dev
  • Kitemaker

Docker architecture for the 2.0 migration

The earlier examples mixed floating latest tags, example credentials and settings that re-enabled disabled nodes. They have been removed. This section explains the December 2025 migration; it is not a complete deployment recipe and has not been validated end to end.

Internal and external runners

ModeArrangementScope
Internaln8n starts the JavaScript runner as a child processIsolated testing without sensitive data
ExternalA separate n8nio/runners container connects to the broker in n8nRequired for native Python; recommended with hardening for production

From 2.0, the main n8n image no longer contains the external runner. The separate official runners image includes JavaScript and Python. Extra packages may require extending that image and configuring explicit allowlists; installing a package does not automatically make it available to Code nodes.

The broker connection

The direction matters: runner → broker in n8n, using the broker's private address on port 5679 by default. A single-instance deployment does not need Redis just because it uses external runners; queue mode has a separate Redis requirement.

Settingn8n containerRunner container
N8N_RUNNERS_MODEexternal—
N8N_RUNNERS_BROKER_LISTEN_ADDRESS0.0.0.0 when accepting connections from the private container network—
N8N_RUNNERS_TASK_BROKER_URI—Its broker address, such as http://n8n:5679
N8N_RUNNERS_AUTH_TOKENSecurely generated shared tokenThe same token

These are connection settings, not a Compose file. Keep the broker private, pin compatible n8n and runner images to the same version, and configure authentication, storage and proxying for your own environment. 1.111.0 is the documented minimum for this sidecar arrangement, not a current release recommendation. In queue mode, each worker needs a runner; main also needs one if it executes manual workflows.

Keep the default exclusions for ExecuteCommand and LocalFileTrigger. Review any custom exclusion list instead of replacing it with an empty list.

Implementation and validation

Use the official runner setup, runner variables and hardening guidance for the selected release. Check image compatibility, private broker reachability, matching authentication tokens, permissions and the dependencies used by your workflows.

Then test real JavaScript/Python Code nodes, OAuth callbacks, file operations, persistence and restart recovery in a separate environment. Container startup alone does not validate a migration. External containers improve separation; they do not guarantee protection against every malicious script, resource-exhaustion event or configuration error.

How Not to Screw This Up

Step 1: Check the migration report

Settings → Migration Report (you need admin access)

Available since v1.121.0. Run it now.

Step 2: Decide your runner mode

Using native Python Code nodes? → External mode is required. Production or sensitive data? → External mode plus hardening and validation. Isolated JavaScript testing without sensitive data? → Internal mode can be an option.

Step 3: If going external, generate token

bash
openssl rand -hex 32

Edit .env:

bash
N8N_RUNNERS_AUTH_TOKEN=your-token-here

Set permissions:

bash
chmod 600 .env

Step 4: test the complete setup in staging

On a compatible 1.x version, enable task runners and test the new OAuth and file-permission defaults before migrating. On 2.0, runners are already enabled; verify the actual runner mode, broker connection and workflow behavior. Keep environment access blocked and the default node exclusions in place. A few environment variables do not replace testing the complete deployment.

Step 5: Review every Code node

Especially:

  • Anything using process.env
  • All Python Code nodes
  • File operations

Step 6: Test OAuth

All integrations. Manually. No exceptions.

Step 7: Back up everything

Database, workflows, credentials, configs, complete .n8n directory.

Step 8: Help find bugs

Report bugs either in an issue on the repository or in the n8n community forum.

My Take

This is the most significant n8n release since 1.0. The security improvements are necessary.

For isolated testing, internal mode is simpler. For production or sensitive data, use the external runner setup and hardening guidance, and verify the whole workflow before migrating.

External mode is required for native Python and recommended with hardening for production. With queue workers, give each worker the runner setup it needs.

If you're running n8n in production (like I do every day), take this seriously. Budget real time for testing and migration. Don't just do docker pull and hope for the best.

The release adds useful security defaults and removes legacy components. Whether your migration is reliable depends on the configuration, workflows and recovery checks you actually test.

Resources

About the author

Idir Ouhab

AI Deployment Engineer at OpenAI, trainer and host of Prompt&Play. I write about what I learn taking AI into production.

Your next step

Putting this into production?

Review the architecture, integrations, and risks of your AI system before the next step.

Share

Topics

  • n8n
  • workflow automation
  • software update
  • breaking changes
  • version 2.0
  • automation tools
Always active

Remembers your language and cookie choices. A separate session cookie keeps administrators signed in. These are not used for advertising.

Your choices are valid for 180 days in this browser. Optional purposes start switched off. If browser storage is unavailable, your choice lasts for this page only.

You can withdraw permission here at any time. If optional content has already loaded, the page reloads to stop it; unsent form changes may be lost.

How cookies and storage are used