
What actually changes in n8n 3.0?
n8n 3.0 is a cleanup release: it removes about two dozen legacy nodes, requires Docker for self-hosting, and changes several defaults that can break working workflows without any warning in the editor. n8n has scheduled it for October 2026. As of today (October 2, 2026) it isn't on npm yet; the latest stable tag is 2.41.6. That gap is the window to audit every client instance you run.
We ran the audit on our own two instances this week: 198 workflows, 2,329 nodes. The removed nodes turned out to be the small problem. Only three workflows used one, and only one of those is active. The defaults are where our time will go, because nothing in the editor flags them.
The official list is on n8n's v3.0 breaking changes page. Below is the order we're working through it, with what our scan found at each step.
Is your instance running on npm or Docker?
Start here, because it's the only change that stops an instance from booting at all. n8n 3.0 requires a Docker-based deployment and no longer supports installs started with npm or npx n8n.
For an agency this usually means the oldest client box: a VPS someone set up two years ago with npm install -g n8n and a PM2 process. Before the version upgrade:
- Move that instance to Docker on the version it already runs, with the same database and encryption key.
- Mount the n8n data folder as a volume and keep the
N8N_ENCRYPTION_KEYidentical, or every saved credential becomes unreadable. - Run it for a few days. Then upgrade.
Doing the runtime move and the major version jump in the same maintenance window makes any failure impossible to attribute. If you don't already have a tested way back, read how we roll back a self-hosted n8n upgrade before starting.
Which removed nodes are in your workflows?
Don't open workflows one by one. Pull them through the public API and match node types. This is the script we used against both instances:
import json, urllib.request
REMOVED = {
"n8n-nodes-base.function", "n8n-nodes-base.functionItem",
"n8n-nodes-base.itemLists", "n8n-nodes-base.cron", "n8n-nodes-base.interval",
"n8n-nodes-base.htmlExtract", "n8n-nodes-base.iCal",
"n8n-nodes-base.moveBinaryData", "n8n-nodes-base.readBinaryFile",
"n8n-nodes-base.readBinaryFiles", "n8n-nodes-base.writeBinaryFile",
"n8n-nodes-base.readPDF", "n8n-nodes-base.aiTransform",
"n8n-nodes-base.workflowTrigger", "n8n-nodes-base.serpApi",
"n8n-nodes-base.orbit", "n8n-nodes-base.openAi",
"@n8n/n8n-nodes-langchain.code", "@n8n/n8n-nodes-langchain.toolHttpRequest",
"@n8n/n8n-nodes-langchain.openAiAssistant",
"@n8n/n8n-nodes-langchain.manualChatTrigger",
"@n8n/n8n-nodes-langchain.memoryMotorhead",
"@n8n/n8n-nodes-langchain.memoryZep",
"@n8n/n8n-nodes-langchain.memoryChatRetriever",
}
def workflows(base, key):
cursor = None
while True:
url = f"{base}/api/v1/workflows?limit=100" + (f"&cursor={cursor}" if cursor else "")
req = urllib.request.Request(url, headers={"X-N8N-API-KEY": key})
page = json.load(urllib.request.urlopen(req))
yield from page["data"]
cursor = page.get("nextCursor")
if not cursor:
return
for wf in workflows("https://n8n.example.com", "YOUR_API_KEY"):
hits = [n["name"] for n in wf["nodes"] if n["type"] in REMOVED]
if hits:
print("ACTIVE" if wf["active"] else "inactive", wf["name"], hits)
The list isn't exhaustive (the vector store Insert/Load pairs and the document loaders are also on n8n's page), but it covers the nodes agencies actually built with. Our result:
- Legacy OpenAI node: 2 instances
- Cron node: 1
- Legacy HTTP Request Tool: 1
- Function, Function Item, Item Lists: 0
Zero Function nodes surprised us, since they were the default way to write JavaScript for years. Ours had all been replaced during earlier rebuilds. Instances that were built once and never touched again are a different story, which is exactly why you scan each client instance instead of assuming.
The replacements:
- Function / Function Item → Code node ("Run Once for All Items" or "Run Once for Each Item")
- Item Lists → Split Out, Aggregate, Sort, Limit, Remove Duplicates or Summarize
- Cron / Interval → Schedule Trigger
- HTML Extract → HTML node, Extract HTML Content operation
- Read PDF → Extract from File, Extract From PDF
- Legacy OpenAI / OpenAI Assistant → the current OpenAI node
- Legacy HTTP Request Tool → HTTP Request node attached to the AI Agent as a tool
- AI Transform → n8n converts it to a Code node on upgrade automatically
- SerpApi, Orbit, Workflow Trigger → no direct replacement; rebuild with HTTP Request or the Execute Workflow Trigger
Swap these on 2.x, where the old and new nodes both exist and you can compare outputs side by side.
Why will Code nodes start timing out after 3.0?
The default N8N_RUNNERS_TASK_TIMEOUT drops from 300 seconds to 60. Any Code node that takes longer than a minute gets killed.
This is the change we expect to cause the most "it worked yesterday" tickets. Our two instances have 370 Code nodes between them. Most finish in milliseconds, but the ones that don't are the important ones: nightly report builders that loop over thousands of rows, enrichment steps that await a slow API inside a loop, CSV reshaping on a client's full export.
Two ways to handle it:
- Pin the old value with
N8N_RUNNERS_TASK_TIMEOUT=300in the environment. Fast and safe for the upgrade itself. - Fix the slow nodes, which is the better long-term answer. Move the HTTP calls out of the Code node into an HTTP Request node with batching, and split heavy transforms with Loop Over Items.
We're doing option 1 on upgrade day and option 2 over the following month. If your Code nodes already misbehave under task runners, our task runner troubleshooting guide covers the other common failures.
Will n8n 3.0 block calls to Tailscale or internal IPs?
Only if you've turned SSRF protection on, but if you have, it will. SSRF protection is off by default (N8N_SSRF_PROTECTION_ENABLED=false). When it's on and N8N_SSRF_BLOCKED_IP_RANGES includes default, n8n 3.0 adds the shared address space 100.64.0.0/10 and IPv6 transition ranges to the block list.
100.64.0.0/10 is the range Tailscale hands out. Our internal instance has four workflows that call devices by their Tailscale IP: a local GPU box for video jobs and a few home-automation endpoints. SSRF protection is off on that instance today, so 3.0 won't touch them. But enabling SSRF protection is standard hardening advice (we've given it ourselves in our n8n security patching guide), and the combination of "turned on SSRF last month" plus "upgraded to 3.0" is how a working workflow starts failing with a blocked-request error.
If you rely on CGNAT or Tailscale addresses, allow them explicitly:
N8N_SSRF_PROTECTION_ENABLED=true
N8N_SSRF_BLOCKED_IP_RANGES=default
N8N_SSRF_ALLOWED_IP_RANGES=100.96.12.0/24
# or, narrower and easier to read:
N8N_SSRF_ALLOWED_HOSTNAMES=*.your-tailnet.ts.net
Allowed hostnames are checked first and always win over a block, per n8n's SSRF protection docs. Allow the narrowest range that works. Allowing all of 100.64.0.0/10 defeats the reason you turned the protection on.
What else changes quietly?
These won't break most instances, but each one has broken at least one kind of setup we manage:
- Community packages.
N8N_UNVERIFIED_PACKAGES_ENABLEDnow defaults tofalse. We counted 161 community nodes across nine packages on our two instances (most of them our own HYROS node). Check which of yours are verified, and test how the installed unverified ones behave on a staging copy before production finds out for you. - Binary data storage.
N8N_DEFAULT_BINARY_DATA_MODE=defaultis no longer valid and switches to filesystem, and the~/.n8n/binaryDatafolder is renamed to~/.n8n/storage. If you back up or mount that path by name, update the backup job. - Compression limits. The Compression node's decompressed size cap drops from 2 GiB to 256 MiB and the zip entry cap from 5,000 to 1,000. Clients who unzip large media or export archives will hit this.
- Removed environment variables.
OFFLOAD_MANUAL_EXECUTIONS_TO_WORKERSandN8N_PRE_EXECUTE_ERROR_CREATES_EXECUTIONare gone, andN8N_DB_PING_TIMEOUTbecomesDB_PING_TIMEOUT_MS. Grep your compose files. - Editor features. Import from URL, the Code node's Ask AI tab,
$getPairedItem,$evaluateExpression()in JavaScript Code nodes, and the Local File and URL sources on Execute Sub-workflow are removed. Chat Hub is off by default.
How should an agency roll this out across client instances?
One instance at a time, internal first, with a restored clone as the test bed. Our order:
- Inventory. Every instance, its version, how it's deployed, who owns the server. If you host clients on separate instances, as we recommend in our multi-tenant n8n guide, this list already exists.
- Scan. Run the script above on each one and save the output with the date.
- Fix on 2.x. Replace removed nodes, move npm installs to Docker, and set
N8N_RUNNERS_TASK_TIMEOUTexplicitly. Ship those changes on the version that's running now. - Clone and upgrade. Restore last night's database and storage backup into a staging container, upgrade it to 3.0, and run every active workflow once with test data.
- Upgrade production during the client's quiet hours, with the rollback steps written down and the previous image tag noted.
Don't take the 3.0 image on the day it ships. Major releases usually get a quick patch release, and nothing in 3.0 is a feature your clients are waiting for. Our plan is to upgrade our internal instance first, wait for the first 3.0.x patch, then move client instances in order of how simple they are.
The upgrade itself is a five-minute container swap. The audit is the real work, and you can do all of it now, on 2.x, before 3.0 exists.
Want us to audit your n8n instances?
We build and maintain n8n for agencies and service businesses, and we're running this exact checklist across every instance we manage this month. If you'd rather not find out about the 60-second timeout from a client, send us your instance details and we'll scan it, list what breaks and fix it on your current version.
Get a Free Automation Audit, or see how we handle n8n workflow automation day to day.
