Operating n8n Cloud, self-hosting, scaling, team security, and the platform surface.
A Signal Through field guide. Independent, plain-English documentation; not affiliated with or endorsed by n8n GmbH. Approximately 22995 words.
In this volume:
Choosing n8n Cloud — the hosted, subscription version of n8n run by the vendor — spares you servers, databases, backups, and upgrade scripts. What it does not spare you is ownership. Somebody in your organization still has to know what the plan actually buys, when the platform will throttle or queue your work, which version of the software you are running and when it changes, where your data physically lives, and what to do when a workflow needs something Cloud will not allow. That person is the Cloud operator, and this chapter is their manual.
Chapter 2 (The n8n Product Family and How It Is Sold) covered the plan ladder — which tiers exist and roughly how they are positioned. This chapter assumes that and goes one level deeper: not which plan to buy but how the machinery behaves once you are on one, and how to tell when it is time to leave. If you are already committed to running your own servers, most of this chapter is context rather than instruction, and you can move to Chapter 32 (Self-Hosting from Zero).
n8n Cloud gives you two distinct places to work, and keeping them straight prevents a lot of confusion.
The first is the editor — the workspace you spend
your days in, with the canvas, the workflow list, credentials, and
executions. It lives at your instance's own web address, a subdomain the
platform assigns when you sign up (something like
your-team.app.n8n.cloud). Everything Chapters 4 through 30
describe happens here.
The second is the Cloud admin dashboard — a separate
management surface where the instance itself is administered.
This is where you manage the subscription, change the running n8n
version and release channel, set the instance timezone, and restart the
workspace if it gets into a bad state. You reach it by signing in to
your n8n Cloud account (the account that owns the subscription, not just
any workspace member) and opening the workspace's Manage
panel. Only the instance owner — the account that created and pays for
the workspace — has full access to this dashboard; ordinary members of
the workspace never see it.
Think of it as the difference between living in an apartment and holding the lease. The editor is the apartment. The admin dashboard is the lease, the thermostat for the whole building, and the superintendent's phone number, all in one place.
Tip: Decide early who in your team holds the owner account, and make sure that person's login is recoverable (password manager, two-factor backup codes). If the owner leaves the company and nobody can reach the admin dashboard, you can still build workflows — but you cannot upgrade, downgrade, change versions, or manage billing until you untangle account ownership with support.
Every Cloud plan is a bundle of allowances. Three of them do almost all the work of defining what your money buys: the execution quota, the concurrency cap, and the execution-history retention window. (A fourth — a cap on how many workflows could be active at once — mattered for years, but n8n has removed it; current Cloud plans allow unlimited active workflows, so breadth no longer costs you anything in allowance.) Understanding how each behaves at its limit matters more than memorizing the numbers, which change with pricing revisions anyway.
n8n Cloud meters usage in executions — one complete run of one workflow, from trigger to finish, counted as a single unit no matter how many nodes it passes through or how many items it processes. (Chapter 3 defined executions; Chapter 21 covers them as a system of record.) This is the single most operator-relevant fact about n8n's pricing, because it is the opposite of how many competing platforms charge. Per-task platforms bill every step; n8n bills the run. A forty-node workflow that enriches, deduplicates, scores, routes, and logs a lead consumes exactly one execution — the same as a two-node workflow that forwards an email.
Each plan includes a monthly execution allowance, larger on higher tiers. As an operator you should know three things about it:
Tip: Sub-workflow calls are effectively free on Cloud. When a parent workflow calls a child via the Execute Sub-workflow mechanism (Chapter 9), only the top-level run counts toward the quota — the child runs do not. So decomposing a big workflow into tidy sub-workflows costs you nothing in allowance. What does cost you is chaining workflows by having one call another's webhook: that inbound webhook call starts a genuine production execution and is metered. If you must chain, chain with the sub-workflow node, not internal webhook calls.
An active workflow is one whose trigger is switched on so it runs in production by itself — the toggle you flip in Chapter 10. For years, Cloud plans limited how many workflows you could keep active at once, and that breadth limit was a genuine planning constraint. n8n has since removed it: current Cloud plans allow unlimited active workflows on every tier. You still deactivate workflows for tidiness and safety, but you no longer ration activation against an allowance.
This simplifies plan sizing. Breadth is now free, so the only meters that scale with how much you automate are volume — the execution quota — and simultaneity — concurrency, covered next. An agency running one small automation for each of forty clients used to bump the old active-workflow ceiling; today those forty workflows cost nothing to keep active and are bounded only by the executions they collectively produce. Size a plan against execution volume and peak concurrency, not against how many workflows you intend to run.
Concurrency means how many production executions your instance may run at the same moment. Cloud enforces a per-plan concurrency cap. When more executions arrive than the cap allows, the extras are not dropped — they wait in a queue and start, in arrival order, when a slot frees up. You can see current active executions and your plan's limit at the top of the executions tab for a project or workflow. Note that the cap governs production executions only: manual test runs, sub-workflow runs, and error-workflow runs are not held back by it.
For most workloads you will never notice this. Where it bites is bursty, time-sensitive traffic: a webhook that receives two hundred events in ten seconds, or several schedule-triggered workflows all firing at the top of the same hour. Queued executions still run and still complete — but later than you expected, and if the downstream logic assumes freshness (say, replying to a chat message while the user is still looking at the screen), the delay is a real defect. Chapter 24 (Reliability Patterns) covers pacing techniques that smooth bursts; Chapter 33 covers how self-hosted queue mode handles the same problem with hardware instead of allowances.
Watch out: Concurrency queueing is silent. Nothing errors, nothing turns red — executions simply start late. If a workflow's timing suddenly gets mushy under load, check the executions tab for a backlog against the concurrency limit before you go hunting for bugs in the workflow itself. Chapter 39 (The Troubleshooting Encyclopedia) lists this among the classic "nothing is broken but everything is slow" causes.
Cloud keeps your execution history — the saved record of past runs you use for debugging and audit (Chapter 21) — for a limited window that varies by plan, with higher tiers keeping more and longer. Old executions are pruned automatically. If a run's data matters beyond that window — compliance evidence, financial reconciliation, "what exactly did we send this customer in March" — you must export it while it exists: push a summary row to a spreadsheet or database as part of the workflow itself, or pull history out through the public API (Chapter 35). Treat the built-in history as a debugging buffer, never as an archive.
| Dial | What it limits | Behavior at the limit | Where you see it |
|---|---|---|---|
| Execution quota | Production runs per month | No overage charge, but production stops until you upgrade or the cycle resets | Cloud account usage/plan page |
| Concurrency | Simultaneous production runs | Excess executions queue and start late | Top of the executions tab |
| History retention | How long past runs are kept | Older executions pruned automatically | Executions list simply ends |
An active-workflow cap once belonged on this list. n8n removed it, so on current plans you can activate as many workflows as you like — only the three dials above still meter you.
Subscription management lives in the Cloud account area rather than the editor: plan changes, payment method, invoices, and cancellation. A few operator notes that the billing page will not volunteer:
Plan changes take effect on the platform's schedule, not yours. Upgrades generally apply quickly — new quota, higher concurrency. Downgrades interact with what you already have: the lower tier carries a smaller execution quota and a lower concurrency cap, so a downgrade that drops you beneath your actual monthly burn will stop your workflows the moment you cross the new, lower ceiling. Before any downgrade, inventory your current-month execution burn and peak concurrency so you are choosing with data.
Trials behave differently from paid instances. Trial workspaces have tighter limits and may be paused or reclaimed when left idle. Do not judge Cloud's production behavior — especially anything timing-related — from a dormant trial instance.
The subscription is the instance. Cancelling does not merely stop billing; it eventually ends the workspace and the data in it. Before cancelling, run the full export procedure described in the migration section below, even if you think you have everything. Workflows are cheap to export and impossible to recover after the instance is gone.
Self-hosters decide when to upgrade n8n and do the work themselves. On Cloud, the platform does the work — but you still make the decisions, and this is one of the most underused parts of the admin dashboard.
Your instance runs a specific version of n8n,
visible in the admin dashboard (and in the editor's own settings
surface). New versions ship frequently — most weeks — and your instance
does not leap onto each one automatically the moment it exists. Instead,
the admin dashboard's Manage panel offers a version
dropdown with two release channels:
Saving a version change triggers a brief restart of your workspace, during which the editor is unavailable and triggers do not fire. Schedule version changes for a quiet window, and remember that missed schedule-trigger ticks during a restart are simply missed (Chapter 24 discusses catch-up patterns for exactly this).
A sane operating rhythm for a team that depends on its workflows:
Tip: If your organization runs more than one Cloud workspace — or you can justify a second small one — put the scratch workspace on the Beta channel and production on Stable. You get early sight of changes that will eventually reach production, with none of the risk. This is the Cloud equivalent of the staging-environment discipline self-hosters build in Chapter 32.
Data residency — the question of which country's soil your data is stored on — is a compliance question long before it is a technical one, and Cloud's answer is unusually clean: n8n Cloud data has historically been stored in the European Union, in Frankfurt, Germany, and the company behind n8n is itself German. For organizations subject to the EU's GDPR privacy regulation, an EU-resident automation platform is a genuinely convenient default. Two cautions before you make promises to your own compliance team. First, hosting footprints evolve and n8n does not offer a broad menu of customer-selectable regions, so if you need data pinned to a specific country, confirm the current arrangement rather than assuming it. Second, where data physically resides and who operates the underlying infrastructure are separate questions — an EU data region can still run on a globally operated cloud provider — so if your obligation is about provider sovereignty and not just geography, verify both with the vendor's current trust and privacy documentation.
What actually lives in that region, on your behalf:
Two operator moves follow from this. First, tune what gets saved: workflow settings let you reduce what execution logging retains for sensitive workflows (Chapter 21 covers the switches). Second, if your obligations require data to remain in a specific other jurisdiction — data must never leave the United States, or must stay in a particular country — and the Cloud region on offer does not match, that is not a configuration problem to work around. It is a structural signal pointing at self-hosting or at an enterprise conversation with the vendor about custom deployment, and it belongs in the decision framework at the end of this chapter.
Here is a constraint that surprises teams at the worst possible moment — usually mid-project, when the workflow is built and the last step is connecting to the company database.
Many protected systems restrict access by IP allowlist: a firewall rule admitting only traffic from specific, pre-approved network addresses. Corporate databases, SFTP servers, and some internal APIs commonly work this way. To allowlist a client, you need the client's address to be static — always the same.
n8n Cloud cannot guarantee static source IPs. The platform runs on shared, elastic cloud infrastructure that scales and shifts with demand, which means the address your HTTP Request node calls out from today may not be the address it calls out from next week. n8n does publish the current set of addresses its Cloud instances call out from, and you can allowlist that published set — but it carries two catches. The list is broad and shared across n8n's whole Cloud fleet, so allowlisting it opens your firewall to far more than your own instance, and n8n does not promise those addresses are permanent. Treat the published list as a stopgap, not a guarantee; the vendor's own guidance is to rely on strong authentication and encrypted transport rather than IP-based restrictions.
When the system you must reach insists on an allowlist anyway, you have a short menu:
Note the asymmetry: this constraint is about outbound traffic. Inbound is fine — your webhook URLs (Chapter 14) live at your stable instance subdomain, and external systems can target them reliably.
Watch out: Audit the allowlist question before committing a project to Cloud, not after. Ask every system owner in the integration path one question: "Can a client at a changing IP address authenticate to you?" A "no" discovered in week one changes your architecture; a "no" discovered at go-live changes your launch date.
Cloud is a managed, multi-tenant service — many customers sharing the same professionally run infrastructure — and the guardrails that keep every customer's instance safe also close off some of the escape hatches self-hosters take for granted. None of these are bugs; all of them are decision inputs.
Community nodes: verified only. The community-node ecosystem (Chapter 15) contains thousands of packages, but Cloud installs only the verified subset — nodes the vendor has vetted for quality and security, marked with a verification badge and installable directly from the editor's node panel. The verified catalog is real and growing, and it skews toward well-maintained, dependency-light packages. But the long tail of unverified community nodes — including plenty of genuinely useful ones — is simply unavailable on Cloud; running them requires self-hosting. If a workflow you are planning depends on a specific community package, check its verification status first: search for it in the editor's node panel and look for the badge.
No arbitrary code dependencies. The Code node (Chapter 19) runs JavaScript, plus a built-in Python option, with a set of pre-provided libraries. On self-hosted instances, an administrator can permit additional npm packages — npm being the JavaScript world's package registry — via environment configuration. On Cloud you cannot: no environment variables to set, no packages to add. If your logic needs a specialized library, your options are calling an external service that wraps it (Chapter 13), finding a verified node that provides it, or self-hosting.
No server-level configuration. The long catalog of environment variables that tune self-hosted n8n — payload size limits, execution-data pruning behavior, custom endpoints, and dozens more — is the platform's business on Cloud, not yours. You configure what the admin dashboard and editor expose (version, channel, timezone, workflow settings) and nothing beneath that. Likewise there is no filesystem to read or write and no shell to execute commands in — the Execute Command capability discussed in Chapter 19 is a self-hosting feature.
A fixed front door. Your instance lives at its assigned subdomain. Serving n8n from your own custom domain is a self-hosting concern (Chapter 32), not something Cloud offers as a general facility.
For most automation work these guardrails never bind. The moment one of them does bind — and stays binding across multiple projects — you have your first hard datum for the framework below.
Sooner or later most teams evaluate a move — Cloud to self-hosted for control or cost, or self-hosted to Cloud to shed operations work. The mechanics are the same in both directions, and so is the one hard truth at the center of them.
Workflows travel; credentials do not. Workflow definitions export cleanly as JSON — nodes, connections, settings, everything — and import into any other n8n instance with structure intact. Credentials do not travel, by design: they are encrypted with a key specific to the instance that created them, and no export will hand you the secrets in usable form. Migration therefore always includes re-creating every credential on the destination — re-entering API keys, re-running every OAuth consent dance — which is tedious but mechanical if you prepared for it.
The same steps in reverse, plus a compatibility screen before you start: identify any unverified community nodes (they will not run on Cloud — find verified or built-in equivalents, or rebuild those steps with the HTTP Request node), any Code-node use of extra npm packages, any Execute Command or filesystem usage, and any expressions referencing self-hosted environment variables. Each is a workflow edit to make in advance, not a surprise to absorb mid-cutover. It is also wise to bring your self-hosted instance to a version near Cloud's current one before exporting, so workflow JSON moves between like versions.
Watch out: Plan the webhook cutover as a distinct step with a rollback path. The safe sequence: build and verify everything on the destination with test traffic, then repoint external systems one at a time, keeping the old instance alive and receiving until the last producer has moved. Deleting the source instance is the final step of a migration, never an early one.
Everything in this chapter feeds one recurring decision: does Cloud remain the right home for this workload? The vendor is not neutral on the question and neither are the hosting providers who would love to sell you a server, so here is the operator's version, on the four axes that actually decide it.
Control. Do Cloud's guardrails bind you? Count the concrete instances: an unverified community node you needed, an npm package the Code node could not load, a server setting you could not change, an allowlisted system you could not reach. Zero or one workaround in a year is normal Cloud life. A recurring pattern of them means you are paying a managed-service premium and the workaround tax simultaneously — the worst of both.
Cost at volume. Cloud's per-execution pricing is excellent at low and moderate volume, where the subscription costs less than the fraction of an engineer's time self-hosting genuinely consumes. At high volume the curves cross: a modest server runs effectively unlimited executions for a flat infrastructure cost, while Cloud tiers climb with usage. Do the arithmetic honestly, though — the self-hosted side of the ledger must include the hours for setup, upgrades, backups, monitoring, and the occasional 2 a.m. incident, priced at what your time is actually worth. Chapter 32 will give you a realistic picture of that workload; do not price it at zero.
Residency and compliance. If Cloud's hosting region satisfies your obligations, this axis is neutral or even favors Cloud (a professionally operated, EU-resident platform is an easy story to tell an auditor). If your data must live in a jurisdiction or network Cloud does not offer, this axis alone decides the question — no quota or feature comparison overrides a compliance requirement.
Extensibility. Closely related to control but worth scoring separately: how much of your roadmap depends on the unverified community-node long tail, custom-built nodes (Chapter 15), external code libraries, or system-level integration? Teams doing mainstream SaaS-to-SaaS automation rarely need any of it. Teams building n8n into a product, or automating deeply idiosyncratic internal systems, need it constantly.
Against all four stands the counterweight: operational burden. Self-hosting converts every one of Cloud's conveniences — upgrades, backups, scaling, security patching, uptime — into your job. The following signals summarize the honest read:
| Signal you are seeing | Points toward |
|---|---|
| Guardrails have never bound you; volume is modest | Stay on Cloud |
| Execution volume makes the required tier cost more than a server plus the time to run it | Move to self-hosted |
| Data must reside where Cloud is not offered | Move (or an enterprise custom-deployment conversation) |
| Core systems require IP allowlisting and cannot offer alternatives | Move, or accept operating a relay |
| Roadmap depends on unverified community nodes, custom nodes, or npm libraries | Move |
| Nobody on the team can own a server's care and feeding | Stay on Cloud, and treat that as a fine answer |
| You want early features with zero upgrade labor | Stay, on the Beta channel — with a Stable production workspace |
Two closing honesties. First, this is not a one-way door in either direction: the migration mechanics above are symmetric, and the only genuinely expensive parts are re-creating credentials and repointing webhooks. Starting on Cloud, learning your real volume and real constraints, and moving later with data in hand is a perfectly good strategy — better than guessing. Second, the most common failure mode is not choosing wrong; it is choosing self-hosting for control nobody ends up using, then under-operating the server. If the framework above points you toward running your own instance, the next chapter — Chapter 32 (Self-Hosting from Zero) — shows you exactly what you are signing up for, so you can sign up for it with open eyes.
Running n8n on your own server sounds like a job for a systems administrator. It is not. If you can rent a small virtual server, paste commands into a terminal, and keep one secret string somewhere safe, you can run a production-grade n8n instance. This chapter is the complete path: what Docker actually is, how big a server you need, which database to choose, the handful of settings that matter, how to put the whole thing behind HTTPS, and the three rituals — updating, backing up, and restoring — that separate a hobby install from one you can trust with real work. Why you might self-host instead of using n8n Cloud is covered in Chapter 2 (The n8n Product Family and How It Is Sold) and Chapter 31 (Operating n8n Cloud Like an Owner); this chapter assumes you have decided to run it yourself and want to do it properly. Growing beyond a single server — queue mode, workers, and task runners — is Chapter 33 (Scaling and Performance: Queue Mode and Task Runners).
One orientation note before we start: everything in this chapter happens on a Linux server you control, over a terminal connection called SSH (Secure Shell — a way to type commands on a remote machine as if you were sitting at it). Any mainstream Linux distribution works; the examples assume a recent Ubuntu or Debian because that is what most rental servers offer by default.
n8n is a program written in JavaScript that needs a particular runtime (Node.js), particular libraries, and particular folder locations to work. Installing all of that by hand on a fresh server is possible but fiddly, and every server ends up slightly different — which is exactly how "it works on my machine" problems are born.
Docker solves this by shipping software as an image: a frozen, complete bundle containing the application plus everything it needs, down to the operating-system libraries. When you run an image, Docker creates a container — a lightweight, isolated copy of that bundle running on your server. The n8n team builds and publishes an official image with every release, so the thing running on your server is byte-for-byte the thing they tested.
Two more Docker terms and you know enough:
N8N_PORT=5678. This
is how you configure n8n without editing files inside the
container.A real n8n deployment is at least two containers: n8n itself and a
database. Docker Compose lets you describe the whole
arrangement — which images, which volumes, which environment variables,
which containers talk to which — in a single text file called
docker-compose.yml. Then docker compose up -d
starts everything, and docker compose down stops it. The
file is your deployment, written down. You can put it in version
control, copy it to a new server, and rebuild your entire setup in
minutes. Every example in this chapter uses Compose.
Installing Docker itself is one script on most Linux servers: the official convenience installer from Docker's website adds both Docker and the Compose plugin. Your server vendor may also offer an image with Docker preinstalled — a fine shortcut.
Because n8n is a Node.js application, you can also install it with
npm (the Node.js package manager) — npm install n8n -g and
then run n8n — or try it for a single evening with
npx n8n, which downloads and runs it without installing
anything permanent. The npm route is genuinely useful for a quick local
look on your laptop, and some people prefer it on servers they already
manage as Node.js hosts. But you then own everything Docker would have
handled: keeping Node.js at a supported version, restarting n8n if it
crashes (usually via a process manager such as systemd or pm2), and
keeping your environment from drifting. For a production server run by a
non-specialist, Docker Compose is the better default, and the rest of
this chapter assumes it.
n8n is modest by modern standards. For a starter production instance
— a handful of active workflows, schedules and webhooks, nothing
enormous — a small virtual server with one or two virtual CPUs and a
couple of gigabytes of memory is a comfortable home, and that class of
machine costs about as much per month as a sandwich at most cloud
vendors. Disk matters more than you would guess, and not for the
software: n8n records every execution (each run of a workflow, per the
mental model in Chapter 3). Current versions prune old execution data
automatically — by default, records are deleted on a rolling basis after
roughly two weeks — but verify pruning is on and tune the retention
window to your needs on day one (the EXECUTIONS_DATA_PRUNE
and EXECUTIONS_DATA_MAX_AGE environment variables control
it), because a burst of chatty workflows can still fill a small disk
between pruning runs. Start with a few tens of gigabytes; Chapter 21
(Executions: The System of Record) covers what you are keeping and
why.
Three workloads push you above the starter size sooner:
The honest advice: start small, watch memory and disk for two weeks, and resize. Resizing a virtual server is a ten-minute operation at every mainstream vendor, and n8n does not care — it wakes up on the bigger machine unchanged. When one machine is genuinely not enough, that is Chapter 33's territory, not a bigger single box.
Out of the box, n8n uses SQLite — a database that lives in a single file inside n8n's data volume, requiring no separate server. It is a perfectly good database and ideal for a trial: zero setup, nothing extra to run. But for a production instance you should choose PostgreSQL (commonly "Postgres"), a full database server that runs as its own container alongside n8n.
| Consideration | SQLite (default) | Postgres (recommended for production) |
|---|---|---|
| Setup effort | None | One extra container in your Compose file |
| Concurrency | One writer at a time; busy instances can stall | Handles many simultaneous executions comfortably |
| Backup story | Copy a file (carefully, while quiet) | Mature dump/restore tooling (pg_dump) built for the
job |
| Path to scaling | Dead end — queue mode requires Postgres | Required foundation for Chapter 33 |
| Corruption risk under load | Higher on busy instances | Low; designed for exactly this |
The clinching argument is the last row plus the first: choosing Postgres costs you perhaps ten extra lines in a Compose file on day one, and switching later means migrating data mid-flight. Take the ten lines now.
n8n has a very large catalog of settings, all controlled through environment variables. You need five to get a correct instance, and one of them is so important it gets its own section.
| Variable | What it does | Why it matters |
|---|---|---|
N8N_HOST |
The hostname n8n believes it is served at, e.g.
n8n.example.com |
Used when building links back to itself |
N8N_PORT |
The port n8n listens on inside the container (default 5678) | Usually left alone; your reverse proxy handles the public side |
N8N_PROTOCOL |
http or https |
Should be https once your reverse proxy is in
place |
WEBHOOK_URL |
The full public base URL, e.g.
https://n8n.example.com/ |
The address n8n hands to the outside world for webhooks |
GENERIC_TIMEZONE (and TZ) |
The instance's timezone, e.g. America/New_York |
Governs when schedules fire |
N8N_ENCRYPTION_KEY |
The key that encrypts stored credentials | Lose it and every credential is gone — see below |
Two of these deserve a closer look before the big one.
WEBHOOK_URL exists because of the
reverse proxy you are about to add. Inside your server, n8n listens on a
private port; the outside world reaches it through your public domain.
When n8n shows you a webhook address in the editor, or registers one
with a third-party service (some trigger nodes tell the remote service
"call me at this URL" — see Chapter 14), it builds that address from
WEBHOOK_URL. If it is wrong or missing, n8n advertises an
address nobody can reach, and every webhook trigger silently receives
nothing. This is among the most common self-hosting mistakes, and the
symptom — "my webhooks never fire" — rarely points at the cause.
Timezone matters because of schedule triggers
(Chapter 7). A Schedule Trigger set to "every day at 08:00" fires at
08:00 in the instance's timezone, and the out-of-the-box
default is UTC (Coordinated Universal Time — the global reference
clock). Set GENERIC_TIMEZONE to your operating timezone,
and set the standard TZ variable to the same value so that
anything reading the system clock — Code nodes, date formatting — agrees
with your schedules.
When you save a credential in n8n — an API key, an OAuth token, a
database password (Chapter 11 covers credentials from zero) — n8n
encrypts it before writing it to the database. The key used for that
encryption is the encryption key. If you set the
N8N_ENCRYPTION_KEY environment variable, n8n uses your
value. If you do not, n8n generates a random key on first startup and
stores it in a small config file inside its data volume.
Here is the trap. The encrypted credentials live in the database. The key lives somewhere else — in your Compose file or in that config file inside the volume. A backup of the database alone is therefore a backup of a locked safe with no combination. If you ever restore the database to a fresh instance that has a different (or newly generated) key, n8n starts fine, your workflows appear, everything looks healthy — and every single credential fails the moment a workflow tries to use one, unrecoverably. There is no reset, no support ticket, no recovery path. You re-create every credential by hand, which for an instance with dozens of OAuth connections is a very bad week.
Watch out: Losing the encryption key does not look like losing the encryption key. The instance boots, the editor loads, workflows and credential names all appear intact. The damage only surfaces when executions start failing with authentication errors. If you ever restore a backup and immediately see every credential failing, stop and check the key before touching anything else — restoring the correct key fixes it instantly; re-saving credentials over the top makes the originals unrecoverable.
The discipline is simple: set the key explicitly, before you save
your first credential. Generate a long random string
(openssl rand -hex 32 in any terminal produces a good one),
set it as N8N_ENCRYPTION_KEY, and store a copy in your
password manager, labeled clearly, the same way you would store the
master password to anything. From that moment, your backup story is
"database plus one string from the password manager," which is a story
you can actually execute at 2 a.m.
Tip: Keep secrets out of the Compose file itself. Docker Compose automatically reads a file named
.envsitting next todocker-compose.ymland substitutes values written as${VARIABLE_NAME}. Put the encryption key and database password in.env, reference them from the Compose file, and you can share or version-control the Compose file without leaking secrets.
Here is a complete, production-shaped docker-compose.yml
for n8n with Postgres. Every choice in it is explained below.
services:
postgres:
image: postgres:16
restart: always
environment:
- POSTGRES_USER=n8n
- POSTGRES_PASSWORD=${POSTGRES_PASSWORD}
- POSTGRES_DB=n8n
volumes:
- postgres_data:/var/lib/postgresql/data
healthcheck:
test: ["CMD-SHELL", "pg_isready -U n8n"]
interval: 5s
timeout: 5s
retries: 10
n8n:
image: docker.n8n.io/n8nio/n8n:<pinned-version>
restart: always
ports:
- "127.0.0.1:5678:5678"
environment:
- N8N_HOST=n8n.example.com
- N8N_PROTOCOL=https
- WEBHOOK_URL=https://n8n.example.com/
- GENERIC_TIMEZONE=America/New_York
- TZ=America/New_York
- N8N_ENCRYPTION_KEY=${N8N_ENCRYPTION_KEY}
- DB_TYPE=postgresdb
- DB_POSTGRESDB_HOST=postgres
- DB_POSTGRESDB_DATABASE=n8n
- DB_POSTGRESDB_USER=n8n
- DB_POSTGRESDB_PASSWORD=${POSTGRES_PASSWORD}
volumes:
- n8n_data:/home/node/.n8n
depends_on:
postgres:
condition: service_healthy
volumes:
postgres_data:
n8n_data:Reading it top to bottom:
postgres and
n8n, each from an official image. Compose puts them on a
private network where they reach each other by service name — that is
why DB_POSTGRESDB_HOST is simply
postgres.restart: always tells Docker to bring
each container back if it crashes or the server reboots. This is your
process supervision, free.<pinned-version> with a specific n8n version from the
releases page rather than using the latest tag. With
latest, any restart might silently jump you several
versions forward — you want updates to be a deliberate act, for reasons
the update section makes clear.127.0.0.1:5678:5678 publishes n8n's
port only on the server's loopback address, meaning only software on the
server itself — your reverse proxy — can reach it. The outside world
never talks to n8n directly./home/node/.n8n), which holds the config file, locally
installed community nodes, and binary data if you configure filesystem
storage for it.depends_on make
n8n wait until Postgres is actually accepting connections before
starting, avoiding a crash-loop on slow boots.With DNS pointed at your server (an A record — the DNS entry mapping
a name like n8n.example.com to your server's IP address),
docker compose up -d starts the stack. Visit the instance
and n8n walks you through creating the owner account —
the first user, who administers the instance. Multi-user setup, roles,
and everything team-shaped is Chapter 34 (Access, Security, and Secrets
for Teams).
A reverse proxy is a small web server that sits in front of n8n, receives all traffic from the internet on the standard web ports, and forwards it inward. It exists for one main reason here: HTTPS — encrypted traffic, the padlock in the browser. You need HTTPS not as a nicety but as a requirement: you will be typing credentials into this interface, third-party services generally refuse to deliver webhooks to plain-HTTP addresses, and browsers increasingly refuse to cooperate with unencrypted logins. n8n itself also expects to be behind HTTPS in production and uses secure cookies for its login session accordingly.
The easiest proxy for a non-specialist is Caddy: it obtains and renews free HTTPS certificates from Let's Encrypt (a nonprofit certificate authority) completely automatically, and its entire configuration for n8n is a few lines saying "for this domain, forward to localhost port 5678." nginx and Traefik are equally capable and better documented in some corners of the internet; nginx requires you to wire up certificate renewal yourself (usually via the certbot tool), and Traefik shines when you run many containers. Any of the three is fine. Two proxy-specific details matter regardless of choice:
Round this out with a basic firewall: allow SSH and the two web
ports, deny everything else. Your server vendor's control panel almost
certainly offers this as a checkbox-level feature, and on Ubuntu the
ufw tool does it in three commands.
Watch out: If you try to log in over plain HTTP at an IP address — say, testing before the proxy is up — n8n's secure-cookie behavior can refuse the login, and the on-screen error suggests disabling secure cookies via an environment variable. Treat that as a diagnostic hint, not a setting. The correct fix is finishing your HTTPS setup; disabling secure cookies on an internet-facing instance leaves your login session exposed.
n8n releases frequently — minor releases land weekly, and the project moves fast. Frequent updates are how you get new nodes, AI capabilities, and security fixes, but updating a system that runs your business deserves a ritual, not a reflex. Here is the one worth adopting.
First, read before you pull. Check the release notes for the versions between yours and the target, and specifically look for the breaking-changes list n8n maintains. Most releases are drama-free; occasionally a node changes behavior or a setting is renamed, and knowing beforehand turns a mystery outage into a planned edit.
Second, back up. A full backup (next section) before every update. Non-negotiable, because of the third point.
Third, understand that database migrations are
one-way. When a new n8n version starts, it may
migrate the database — restructure tables to the new
version's layout. This happens automatically and works well. But it does
not run in reverse: once migrated, the database belongs to the new
version, and simply pointing the old image at it is not supported.
Rolling back means restoring the pre-update database backup. This is why
the backup is non-negotiable and why latest is a bad tag
for production — an accidental upgrade is easy, an accidental downgrade
is not a thing.
Fourth, the mechanics — pleasantly boring: edit the
pinned version in docker-compose.yml, then
docker compose pull (fetch the new image) and
docker compose up -d (recreate the container on the new
image; volumes and data are untouched). Downtime is typically well under
a minute. Then spot-check: log in, open a workflow, run one manually
(Chapter 10's testing habits apply), and glance at executions for the
next hour.
Tip: Do not let the gap between your version and current grow to many months. Long-jump upgrades cross more breaking changes and more migrations in one leap, which multiplies the ways an update can surprise you. A small update monthly is far safer than a giant one yearly — put a recurring reminder on your calendar, or have an n8n workflow nag you, which is only fitting.
A backup you have never restored is a hope, not a backup. Here is the complete inventory of what an n8n instance needs to be rebuilt from nothing:
| What | Where it lives | How to capture it |
|---|---|---|
| The database (workflows, encrypted credentials, executions, settings, users) | The Postgres container's volume | pg_dump via docker compose exec |
| The encryption key | Your .env file / password manager (or n8n's config file
if you let it auto-generate) |
Already in your password manager — verify, don't assume |
The Compose file and .env |
Your server / your version control | Copy alongside every backup |
| n8n's data folder | The n8n_data volume |
Archive it if you use community nodes or filesystem-stored binary data |
The database dump is one command:
docker compose exec -T postgres pg_dump -U n8n n8n > n8n-backup-$(date +%F).sql(The -T tells Compose not to attach a terminal to the
command, which keeps stray terminal formatting out of the dump file.)
Schedule it daily via cron (the standard Linux scheduler) and ship the
files off the server — object storage (S3-style file buckets) at any
cloud vendor, or even a synced folder. A backup that lives only on the
server it protects vanishes with the server.
n8n also has a built-in export CLI
(n8n export:workflow --backup and its credentials sibling,
runnable inside the container with docker compose exec)
that writes workflows and credentials to JSON files. Treat it as a
supplement, not the backup: it is excellent for archiving workflow
definitions in version control and for moving workflows between
instances, but the database dump is the authoritative full-state copy.
Chapter 35 (The Platform Surface: API, CLI, Source Control, and Embed)
covers the CLI and source-control features properly.
The restore drill, on a fresh server: install
Docker; restore your Compose file and .env (with the
original encryption key — this is the moment that string earns its
keep); start only Postgres; feed it the dump
(docker compose exec -T postgres psql -U n8n n8n < backup.sql);
start n8n; log in and run a credentialed workflow to prove the key is
right. Run this drill once now, while nothing is on fire, and note how
long it takes. That number is your real recovery time, and the
confidence is worth an afternoon.
Tip: Put a quarterly "restore drill" reminder next to your update reminder. The two failure stories self-hosters actually experience are "updated without a backup" and "backed up without the key" — both are extinct in operations that rehearse.
Everything this chapter has built runs on the free Community edition with no license at all. n8n additionally offers a small set of extra conveniences to self-hosters who register their instance — free, in exchange for an email address. Look in the instance settings under usage and plan for the unlock option; n8n emails you a key that lights up a handful of quality-of-life features on the Community edition, historically including things like workflow folders for organizing a growing collection, improved debugging conveniences in the editor, and a short window of workflow history. The exact bundle has shifted over time, so read the current list on n8n's site rather than trusting any book's snapshot — but since the price is an email address, most self-hosters should simply do it.
n8n's paid self-hosted tiers unlock team-and-governance machinery: single sign-on, role-based access control and projects, external secrets integration, log streaming, environments with Git-based source control, and more — the features that matter once an instance stops being one person's tool, covered across Chapters 34 and 35. Commercially these come as a license key from n8n's sales team. Activation is deliberately mundane: enter the key in the instance settings under usage and plan, or supply it via an environment variable at startup so freshly rebuilt containers re-activate themselves. The license periodically checks in with n8n's servers to stay current; if your instance lives somewhere unusual, network-wise, discuss offline activation with n8n when you buy.
n8n is not open source in the strict sense; it is fair-code, distributed under the Sustainable Use License. For nearly everyone the license is invisible, but if you plan to build a business around n8n you need to know where the line sits.
Comfortably permitted: using n8n for your own company's automation, however commercial the company; personal use; and consulting — building, hosting, and maintaining workflows for clients is fine, because you are charging for your expertise, and this is exactly how a large ecosystem of n8n agencies operates. Not permitted without a commercial agreement: charging people for access to n8n itself — running a hosted n8n and selling accounts, white-labeling the editor inside your own product, or otherwise making n8n-the-software the thing your customers are paying for. For embedding n8n in your product, n8n sells a specific Embed license, mentioned in Chapter 2.
Watch out: The line between "automation service powered by n8n" (fine) and "reselling n8n" (not fine) can get genuinely blurry — for example, giving each client a login to an instance you host sits close to the boundary. The license text and n8n's own license FAQ are short and readable; if your business model puts n8n in front of your customers in any form, read both, and when still in doubt, ask n8n directly. An email now is cheaper than a relicensing conversation after you have customers.
You now have an instance that is, in every meaningful sense, production: pinned versions, Postgres underneath, HTTPS in front, an encryption key in the password manager, daily backups shipped off-box, and a rehearsed restore. From here the roads fork by need. More horsepower and concurrent executions: Chapter 33. More people, roles, and secrets hygiene: Chapter 34. Automating the platform itself — API, CLI, source control: Chapter 35. And if you have not yet built anything worth all this reliability, Volume H's worked projects are a fine reason to go make the server earn its keep.
Every n8n instance starts life as a single process on a single machine, and for a surprising number of teams it can stay that way forever. But if your automation estate grows — more workflows, more webhooks, heavier payloads, spikier traffic — there comes a day when one process is no longer enough, or no longer safe. This chapter is about that day: how to recognize it, what n8n's scaling architecture looks like in plain English, and how to grow from "one box doing everything" to a fleet of cooperating processes without losing your footing.
This is self-hosting territory. If you run n8n Cloud, the vendor operates this machinery for you, and your levers are plan choice and workflow design — see Chapter 31 (Operating n8n Cloud Like an Owner). If you self-host, this chapter builds directly on Chapter 32 (Self-Hosting from Zero), and it assumes you know what a container is (a packaged, isolated copy of an application that you can start, stop, and duplicate) and what an execution is (one run of one workflow — Chapter 3 covers the mental model, Chapter 21 the records it leaves behind).
In its default configuration — the docs call it "regular mode" — one n8n process does everything: it serves the editor UI, listens for incoming webhooks, fires schedule triggers, executes every workflow, writes execution records to the database, and answers API calls. It is a one-person restaurant where the same individual takes orders, cooks, serves, and washes dishes.
n8n is built on Node.js, a JavaScript runtime whose defining trait is the event loop: a single thread that juggles many tasks by switching between them whenever one is waiting on something (a network response, a database write). This makes n8n very efficient at its most common job — orchestrating API calls, which is mostly waiting — but it has a well-known failure mode: if any one task hogs the thread with heavy computation (parsing a giant JSON file, running a loop over a hundred thousand items, executing expensive Code node logic), everything else stalls. Webhooks time out, the UI freezes, schedules fire late. The restaurant grinds to a halt because the one worker is stuck stirring a single enormous pot.
So a single instance runs out of headroom in two distinct ways, and it pays to tell them apart:
Tip: Before any architecture change, spend one week measuring. Note executions per day, the busiest hour, average execution duration, and the size of your execution database. Every decision in this chapter gets easier with those four numbers in hand — and the worksheet at the end of the chapter is built around them.
Queue mode is n8n's answer to throughput: instead of one process doing everything, you split the roles across several processes that coordinate through a shared queue. The mental picture is a restaurant that finally hired staff: a front-of-house that takes orders, a ticket rail where orders wait, and as many cooks as the kitchen can hold.
Four kinds of pieces are involved.
The main instance is a normal n8n process with one crucial behavioral change: when production work needs to run, it does not execute it itself — it writes the job onto the queue. The main instance still serves the editor UI and the API, manages activation of workflows, fires schedule and polling triggers (deciding when work should happen, per Chapter 7), and by default still receives webhook calls. It is the brain and the receptionist, no longer the cook.
The queue itself lives in Redis, a small, extremely fast data store that keeps its data in memory. Think of it as the ticket rail: the main instance clips a ticket on ("run workflow X with this input"), and any available cook can pull the next ticket off. Redis is a separate piece of software you run alongside n8n — a standard, battle-tested component available on every cloud platform and as a container image. n8n uses an established Node.js job-queue library on top of it, which handles the bookkeeping: which jobs are waiting, which are in progress, which worker took what.
Redis holds only the coordination state — the tickets, not the meals. Workflow definitions, credentials, and execution results all live in the main database. If Redis restarts, queued-but-unstarted jobs can be lost, so treat it as production infrastructure: give it persistence or replication if accepted-but-unprocessed work matters to you.
A worker is another n8n process started with a
different command (n8n worker instead of plain
n8n). Workers do not serve the UI and do not decide when
things happen. They do exactly one thing: pull executions off the queue,
run them, and write the results to the database. Workers are
interchangeable and stateless, which is what makes queue mode scale —
need more capacity, start more workers; need less, stop some. Nothing
else has to change, because every worker reads the same workflow
definitions and credentials from the same shared database.
The optional fourth piece: dedicated webhook
processor processes (started with n8n webhook)
whose only job is receiving incoming webhook calls and enqueueing them.
You put a load balancer — a traffic distributor, the
standard building block of every scaled web service — in front of
several of them, so no single process is the bottleneck for accepting
inbound traffic, and a busy UI can never make you drop a webhook. Most
deployments do not need these on day one; they matter when webhook
volume itself is high or when you cannot afford to miss calls during
main-instance restarts. There is a configuration setting that tells the
main instance to stop serving production webhooks itself once dedicated
processors exist — check the queue-mode page of the current docs for the
exact variable.
One nuance: test webhook calls (the ones you trigger while building, per Chapter 14) are still handled by the main instance, because they need a live connection to your editor session.
| Process | Started as | Does | Does not |
|---|---|---|---|
| Main instance | n8n |
UI, API, activation, schedule/poll triggers, enqueueing, (by default) webhook receipt | Execute production workflows |
| Redis | separate service | Holds the queue of pending executions | Store workflows, credentials, or results |
| Worker | n8n worker |
Pulls jobs, executes workflows, writes results | Serve UI, receive webhooks, fire triggers |
| Webhook processor | n8n webhook |
Receives production webhooks, enqueues them | Execute workflows, serve UI |
Three things must be identical across every process in the fleet, and misconfiguring any of them produces confusing failures:
N8N_ENCRYPTION_KEY). Every main, worker, and
webhook processor must have the same key, or workers will fail
to decrypt credentials at execution time. Chapter 34 (Access, Security,
and Secrets for Teams) covers key handling.Turning queue mode on is, at its core, three moves: set the execution
mode to queue (EXECUTIONS_MODE=queue), give every process
the Redis connection details (the QUEUE_BULL_REDIS_* family
of environment variables), and start at least one worker alongside the
main. Chapter 32's container-based setup extends naturally: the worker
is the same n8n image with a different command and the same
environment.
Watch out: The classic queue-mode gotcha is a copied-but-not-identical
N8N_ENCRYPTION_KEY. Everything appears healthy — UI up, jobs flowing — until a worker touches a credential and every execution fails with decryption errors. When bringing up a fleet, verify the key matches byte-for-byte across all processes before you celebrate.
Concurrency is the number of executions a process will run simultaneously. It is your most important tuning knob in both modes.
In regular (single-process) mode, a concurrency limit caps how many
production executions run at once
(N8N_CONCURRENCY_PRODUCTION_LIMIT); additional work queues
up inside the process. Out of the box there is no cap — setting one
deliberately is your first line of defense on one box, keeping a burst
of triggers from stampeding your memory.
In queue mode, each worker has its own concurrency setting (a startup flag or environment variable; the default is a modest ten or so concurrent executions). Total fleet capacity is simply workers × per-worker concurrency.
There is a genuinely useful rule of thumb from queueing theory for sizing this, and it is worth internalizing:
needed concurrency = peak arrivals per second × average execution duration in seconds.
If your busiest window sees 5 executions arriving per second and the average execution takes 4 seconds, you need capacity for about 20 concurrent executions — say two workers at concurrency 10, plus headroom. The formula also reveals the cheapest scaling lever of all: make executions faster. Halving average duration doubles effective capacity without adding a single container.
Set per-worker concurrency by workload character, not ambition. Workflows that mostly wait on APIs can run many at once per worker; CPU- or memory-heavy workflows (large datasets, big files, heavy Code nodes) may need a lower setting so parallel executions do not exhaust the container's memory. It is perfectly reasonable to run two worker pools with different concurrency settings for different workload types.
Tip: Concurrency limits interact with external rate limits. Twenty concurrent executions all calling the same third-party API can trip its rate limiting even though n8n itself is fine. Pacing patterns for that live in Chapter 24 (Reliability Patterns: Idempotency, Pacing, and Recovery).
Queue mode scales how much runs. Task runners change where the riskiest part runs.
The Code node (Chapter 19) executes arbitrary JavaScript that your builders write. Historically that code ran inside the n8n process itself, which had two problems: a runaway script could stall or crash the whole process (the event-loop problem again), and code executing inside n8n's own process sits uncomfortably close to credentials and internals — a security concern the moment more than one trusted person builds workflows.
A task runner is a separate process whose only job is executing Code
node tasks. When a workflow reaches a Code node, the n8n process hands
the code and its input data to the runner, waits for the result, and
continues. If the code loops forever or devours memory, the runner
suffers — not your main or your workers. As of the 2.0 release, task
runners are on by default — the old N8N_RUNNERS_ENABLED
toggle is deprecated and no longer needed; on earlier 1.x versions you
had to opt in with N8N_RUNNERS_ENABLED=true.
Runners come in two modes:
Two practical consequences of the runner architecture are worth knowing before they surprise you. First, code now runs in a stricter sandbox, so a few old conveniences that relied on running inside n8n's own process no longer work by default — most notably, Code nodes lose direct access to the host's environment variables (you can re-enable that with a configuration flag if a workflow genuinely needs it), and in 2.0 the Python side of the Code node changed underneath as well. These are small rewrites, but plan for them when upgrading old workflows. Second, in external mode the modules available to your JavaScript are governed by the runner's configuration, so if your Code nodes rely on extra libraries, those need to be allowed and present in the runner environment. Chapter 34 covers the security rationale in more depth.
In queue mode, one kind of work historically stayed on the main
instance: manual executions — the runs you trigger by
clicking Execute workflow in the editor while building.
That made the main instance's health hostage to whatever half-finished,
possibly enormous workflow someone was testing at 2 pm on a Tuesday.
Modern n8n can push manual executions onto the queue like any other
job, so they run on workers instead
(OFFLOAD_MANUAL_EXECUTIONS_TO_WORKERS=true). It is off by
default, but n8n recommends turning it on for production queue-mode
deployments — and with task runners on by default in 2.0, offloading
also means the main instance no longer needs its own task-runner sidecar
just to service test runs. The editor still shows live progress; the
work just happens elsewhere. If your main instance gets sluggish exactly
when your team is actively building, this setting is the fix. The one
operational note: workers must be running for manual testing to work at
all once you offload, so keep at least one worker up even in quiet
hours.
Every execution writes a record — inputs, outputs, timing, status — into the database (Chapter 21 treats this as your system of record; Chapter 25 builds monitoring on it). At scale this table becomes the silent killer of performance. Tens of thousands of executions a day, each storing full intermediate data for every node, will balloon a database into the tens or hundreds of gigabytes, and everything that touches it — the executions list, startup, the pruning process itself — slows down.
Two levers control the bleeding:
EXECUTIONS_DATA_MAX_AGE, in hours) and a maximum count
(EXECUTIONS_DATA_PRUNE_MAX_COUNT). Defaults are generous;
at serious volume you will want to tighten them deliberately. Decide
your true retention need — audit, debugging, reporting — and export
anything needed longer to external storage via the API (Chapter 35)
rather than treating the executions table as an archive.Watch out: Pruning marks rows deleted before space is actually reclaimed, and databases do not necessarily shrink on disk when rows go away. If you slash retention on an already-huge PostgreSQL database, expect the file size to stay put until the database's own maintenance (vacuuming) catches up. Plan aggressive retention before the table gets huge, not after.
Workflows that handle files — PDFs, images, spreadsheets, audio (Chapter 20) — generate binary data, and where that data lives matters enormously at scale. In the default mode, binary data rides along inside the execution itself: held in memory while the workflow runs, then persisted with the execution record in the database. Big files inflate every execution's footprint (the fastest way to crash a worker) and swell the database with file content. The alternative filesystem mode writes files to local disk instead — but that has a sharper problem in queue mode: workers are separate containers with separate disks, so a file written by one process is invisible to another. Queue mode therefore does not support filesystem mode at all; your choices there are the database-backed default or external storage.
External storage means S3, Amazon's file-storage
service, whose API has become the de facto standard that many other
providers implement ("S3-compatible"). You point n8n at a bucket
(N8N_DEFAULT_BINARY_DATA_MODE=s3 plus a small family of
N8N_EXTERNAL_STORAGE_S3_* variables for host, bucket,
region, and access keys, set identically on every process), and binary
payloads flow to object storage while executions carry only lightweight
references. Every worker sees the same files, memory pressure drops, and
your database stops swallowing file content. One responsibility moves
with the data: n8n leaves cleanup of the bucket to you, so set a
lifecycle rule (S3's own expiry mechanism) unless you mean to keep files
forever.
Two caveats. First, S3 external storage is a paid, Enterprise-licensed feature of self-hosted n8n. Without it, the database-backed default still works in queue mode — you just carry the bloat, which makes workflow design your lever: write files to their real destination (a drive, a bucket, a DMS) as early as possible and pass references, not blobs, between nodes. Second, n8n officially supports AWS S3 itself; other S3-compatible services (Cloudflare R2, Backblaze B2, MinIO and friends) generally work but without official support, so test your provider before betting production on it. Note also that if an Enterprise license lapses while you are in S3 mode, the instance can still read existing binary data but stops writing new data to the bucket — worth knowing before a renewal deadline sneaks up.
Everything so far still has one main instance, and that is a single point of failure — a component whose failure takes the system down. If the main dies, workers keep draining the queue, but nothing new gets scheduled, webhooks bounce (unless dedicated processors catch them), and the UI is gone.
Multi-main setup, an Enterprise-licensed feature, runs two or more main instances behind a load balancer. The mains coordinate through Redis using leader election — an automatic vote that designates one instance as leader for the duties that must not be duplicated (firing each schedule exactly once, for example), while all mains serve UI, API, and webhook traffic. If the leader dies, another main is promoted within moments, and the platform keeps breathing through restarts, deploys, and hardware failures. One deployment detail is easy to miss: the load balancer must use sticky sessions (keep each browser pinned to the same main), or the editor misbehaves.
Be honest about whether you need it. Multi-main protects against scheduling and intake downtime measured in seconds to minutes. If your workflows tolerate a five-minute gap during a rare failure — and most business automation does, especially if you follow the recovery patterns of Chapter 24 — a single main with fast automatic restart, plus queue mode's worker redundancy, is a perfectly respectable posture. Multi-main earns its license fee when webhooks are revenue-bearing or compliance-critical and every minute of intake outage is money.
Scaling decisions made on vibes are how you end up with five workers and the same slow system. Benchmarking here does not mean a laboratory exercise; it means an hour of deliberate measurement.
Build a probe workflow. A Webhook trigger, one or two representative nodes (an HTTP Request to a stable endpoint, a small Set node), and a response. This measures the platform, with your real workflows' noise removed.
Generate load. Any HTTP load-testing tool
(hey, k6, ab, even a shell loop
with curl) fired at the probe's production URL. Start
gently — a few requests per second — and step up while watching four
dials:
docker stats or your host's monitoring). Which container
saturates first — main, worker, or database?Then test with a realistic workflow — one of your real heavyweights — and compare. If the probe flies and the real workflow crawls, your bottleneck is workflow design, not platform capacity, and Chapters 18 and 24 will do more for you than any container.
Interpret with the concurrency formula from earlier. Measure the max sustainable executions per second and the average duration; multiply for the concurrency actually in use; compare with what you provisioned. The gap tells you whether to add workers, raise per-worker concurrency, speed up executions, or fix the database.
Tip: Re-run the same probe benchmark after every major change — version upgrades, new worker counts, database moves — and keep the numbers in a doc. A drifting baseline caught early is a ten-minute fix; discovered during an outage, it is a very bad afternoon.
Pull it together. Fill this in with a real week of data (the executions list and Chapter 25's insights give you everything you need). The bands are planning heuristics from operational experience — not vendor limits, not guarantees — and the peak-hour numbers matter far more than the daily totals, because nobody's traffic is evenly spread.
Step 1 — Measure your demand.
| Input | Your value | How to get it |
|---|---|---|
| Executions per day (E) | ______ | Executions list / insights, averaged over a week |
| Peak-hour share (P) | ______ % | What fraction of a day's executions land in the busiest hour (often 15–25%) |
| Peak executions/second | ______ | E × P ÷ 3600 |
| Average execution duration (D) | ______ s | Sample durations from the executions list |
| Needed concurrency (C) | ______ | Peak per-second × D, then add ~50% headroom |
| Heavy factor | ______ | Rough share of executions that are CPU/memory-heavy (big files, big loops, Code-heavy) |
Step 2 — Read your band.
| Executions per day | Typical posture |
|---|---|
| Up to a few thousand | Regular mode on one adequately-sized box. Set a production concurrency cap, tune pruning, fix heavy workflows. Queue mode is optional insurance, not a need. |
| Thousands to a few tens of thousands | Queue mode with one main, one or two workers. Task runners in external mode. Tightened pruning. S3 binary offload if files are involved. |
| Tens of thousands to low hundreds of thousands | Queue mode with several workers (size by C), dedicated webhook processors behind a load balancer, offloaded manual executions, aggressive save/prune settings, a seriously provisioned PostgreSQL, metrics scraped and charted. |
| Beyond that | All of the above plus multi-main HA (Enterprise), autoscaled worker pools, and scheduled benchmark regression checks. At this point you are running a platform, and Chapters 34 and 35 are required reading. |
Step 3 — Sanity checks before you buy anything.
Scale is not a trophy. The best-run n8n installations are the ones where each piece of this machinery was added the week it was needed and not before — measured in, benchmarked, and written down. When an execution misbehaves on this larger stage, the debugging craft of Chapter 22 and the error-handling machinery of Chapter 23 work exactly the same way; there is simply more platform underneath them, quietly doing its job.
The day a second person gets a login to your n8n instance, the questions change. Alone, security meant protecting your credentials from the outside world — the habits built in Chapter 11 (Credentials from Zero). With a team, you also have to decide what the inside world can see and touch: who can edit the workflow that invoices customers, who can use the credential that reads the CEO's mailbox, who can install a community node that runs arbitrary code on your server. None of these are exotic scenarios. They are Tuesday, for any team of three or more.
This chapter is the governance layer of the compendium. It covers the machinery n8n gives you for saying who can do what: instance roles, projects as permission fences, and deliberate sharing of workflows and credentials. It then hardens the front door with two-factor authentication, single sign-on, and LDAP; moves secrets out of n8n entirely with external vaults; wires security events into your observability stack with log streaming; and teaches you to run n8n's built-in security audit. It ends with the deliverable the whole chapter builds toward: a hardening checklist for any n8n instance that faces the public internet. Throughout, remember one distinction from Chapter 10 (Test, Fix, Activate, and Keep It Tidy): folders are organization — they help people find things. Everything in this chapter is fencing — it decides whether they may touch what they find.
Every n8n instance — Cloud or self-hosted — has a flat list of user
accounts, each carrying one instance-wide role. You manage them in the
user administration area of the instance settings (look for
Settings > Users), where you invite people by email and
assign their role.
| Role | Who has it | What it can do |
|---|---|---|
| Owner | Exactly one account — the first one created on the instance | Everything: manage users and roles, change every instance setting, see and manage all workflows, credentials, and executions regardless of who created them |
| Admin | Accounts the owner promotes (an option on paid tiers) | Nearly everything the owner can do — user management, instance settings, visibility into all resources — without being the root account |
| Member | Everyone else; the default for new invites | Work only within their own personal space and whatever projects or shared resources they have been granted |
Three practical notes make this table useful rather than merely true.
First, the owner is a root account, and should be treated like one. It cannot be locked out of anything, which makes it the account attackers most want and the account you should use least. Sign in as owner to do owner things — promote an admin, change instance settings — and do your daily building as an ordinary account. On a self-hosted instance the owner is simply whoever completed the setup screen first, which is worth remembering when you stand up a new server: claim it immediately, before anything is exposed (see Chapter 32, Self-Hosting from Zero).
Second, members see almost nothing by default. A freshly invited member lands in an empty personal workspace. They cannot browse other people's workflows, open other people's credentials, or read the execution history of things they don't have access to. All access beyond their own work arrives through the two mechanisms in the next sections: project membership and explicit sharing. This default-deny posture is the single best thing about n8n's model — you never have to claw back access nobody should have had, because nobody starts with any.
Third, visibility is not the same as secrecy for the owner and admins. Owners and admins can see and manage everyone's workflows and credentials in the sense of listing, opening, reassigning, and deleting them. What nobody gets — at any role — is the ability to read a stored credential's secret values back out of the user interface. As Chapter 11 explained, n8n masks secret fields after saving and encrypts them at rest. Roles govern use and management, not disclosure.
Tip: Create at least one admin account besides the owner, held by a different human. If the owner's mailbox is compromised, unreachable, or on vacation, someone can still manage users and respond to an incident. One root account with no deputy is a single point of failure dressed up as tidiness.
A project in n8n is a shared container for workflows, credentials, and their executions, with its own member list and per-member roles. Everyone always has a personal project — the private space where your own work lives — and on paid tiers you can create team projects alongside it. When a workflow or credential lives in a team project, access is governed by project membership, not by who originally created the resource. People come and go; the project persists.
This is the fences counterpart to the folders you met in Chapter 10. Folders inside a project keep fifty workflows findable. The project boundary decides who can open the drawer at all. Use both, and don't ask folders to do a fence's job: putting a workflow in a folder named "Finance — do not touch" protects nothing.
Within a project, each member carries one of three roles. In broad strokes:
| Project role | Can manage members and project settings | Can create and edit workflows and credentials | Can view workflows and executions |
|---|---|---|---|
| Project admin | Yes | Yes | Yes |
| Project editor | No | Yes | Yes |
| Project viewer | No | No | Yes |
A project admin runs the fence: adds and removes members, changes roles, renames or deletes the project, and does everything an editor can. A project editor does the daily work — building, editing, testing, and using the project's credentials in the project's workflows — without being able to change who is inside the fence. A project viewer is read-only: able to open workflows and inspect execution history, which is exactly what an auditor, a support engineer triaging an incident, or a stakeholder who wants to understand a process needs — and nothing more. The read-only viewer role is a higher-tier feature, and the exact permission matrix has small print that shifts between releases, so consult your instance's documentation when a specific edge case matters. The three-tier shape, though, is stable and is what you should design around.
The quiet superpower of projects is what they do to credentials. A credential created inside a team project is usable by the project's admins and editors in that project's workflows — no per-person sharing ceremony, no orphaned access when its creator leaves the company. Offboarding someone becomes: remove them from the instance, and every fence they were inside closes behind them. Compare that with a personal-credential culture, where a departing teammate's account owns half the production credentials and offboarding becomes archaeology.
Projects are cheap to create (within your plan's allowance) but expensive to reorganize, so it pays to pick a scheme deliberately. Three patterns cover most teams:
By team or function — Marketing,
Finance Ops, Data Engineering. The default
choice. Membership maps to the org chart, and credential blast radius
maps to departmental trust: the Stripe credential lives in
Finance Ops, and marketing simply cannot select it.
By environment — Ops – Dev and
Ops – Production, with production's membership a strict
subset. Everyone builds and breaks things in dev; two people hold editor
rights in production, and everyone else is a viewer. This gives you a
promotion ritual — moving a workflow across the fence is a deliberate
act — even before you adopt the source-control tooling in Chapter 35
(The Platform Surface: API, CLI, Source Control, and Embed).
By client — for agencies: one project per client, so client A's credentials are structurally invisible to work happening for client B. This is not just tidiness; for many agencies it is a contractual requirement, and projects let you honor it mechanically instead of procedurally.
Whatever scheme you choose, keep personal projects for drafts and experiments only. Anything that matters to the business — anything activated, anything a colleague might need to fix at 2 a.m. — belongs inside a team project. A production workflow in a personal space is a bus-factor problem wearing a convenience costume.
Projects are the wholesale mechanism; sharing is
retail. From a workflow's or credential's sharing controls (the
Share option when you have it open, or the ...
menu in the list view) you can grant access to specific people or
projects without moving the resource.
Two properties make credential sharing safer than it sounds, and you should be able to recite both:
Sharing a credential grants use, never sight. A person or project you share a credential with can attach it to nodes and execute with it. They cannot open it and read the API key, and they cannot edit or re-share it. The secret stays sealed; only its power is delegated.
Workflows and credentials share separately. Sharing a workflow with a colleague does not automatically hand them the credentials wired into its nodes, and sharing a credential does not expose the workflows that use it. This occasionally produces a confusing moment — a collaborator opens a shared workflow and finds nodes whose credentials they cannot select or swap — but it is the correct default: collaboration on logic should not silently mean delegation of access.
Watch out: "Use, never sight" is real protection, but do not let it lull you. A credential's use is its power. Anyone who can attach your admin-scoped CRM credential to a node can build a workflow that exports every contact — without ever seeing the key. Share credentials by the standards of what they can do, not what they reveal.
The mechanics take minutes to learn; the judgment is where teams differ. A workable policy for most teams fits on one page:
automation@yourcompany.com) rather than a human.
Humans leave, change roles, and get their passwords rotated; each event
silently breaks every workflow riding on their identity.Stripe – Read – Prod from
Stripe – Admin – Prod from Stripe – Test.
Fine-grained credentials make fine-grained sharing possible and make the
blast radius of any single leak legible.Everything above assumes the person behind a session is who the account says. That assumption is what sign-in security defends.
Two-factor authentication (2FA) — n8n also uses the
term multi-factor authentication, or MFA — requires a second proof at
login: a six-digit code from an authenticator app (a time-based one-time
password, or TOTP, generated on your phone) alongside
your password. A phished or reused password alone no longer opens the
door. Each user enables it in their own account settings
(Settings > Personal), scanning a QR code with any
standard authenticator app and saving the recovery
codes — one-time backup codes for when the phone is lost. Store
those recovery codes in your password manager, not a screenshot
folder.
2FA is available across n8n deployments and is the highest-value, lowest-cost control in this chapter. At minimum, the owner and every admin should have it on before an instance faces the internet; depending on your plan and version you may also find an enforcement toggle in the user-management settings that requires it for everyone — if it is there, turn it on.
Single sign-on (SSO) delegates login to an identity provider (IdP) your company already runs — Okta, Microsoft Entra ID, Google Workspace, and peers. Users click "sign in," authenticate with the IdP (which enforces its own MFA, device checks, and session policies), and n8n trusts the result. The two protocols n8n speaks are SAML (the long-established XML-based enterprise standard) and OIDC (OpenID Connect, the newer, lighter protocol built on OAuth2 — the same choreography you met in Chapter 11, repurposed for login). Which one you use is usually dictated by whatever your IdP team prefers; from n8n's side both are configured in the instance's SSO settings and both achieve the same end.
The decision-level case for SSO is not convenience — it is centralized offboarding. When IT disables a departed employee's IdP account, their access to n8n dies with it, along with their access to everything else. No per-tool checklist, no forgotten account still holding project memberships. For any organization with a real IdP and more than a handful of n8n users, that one property justifies the setup afternoon. SSO is a paid, higher-tier feature on both Cloud and self-hosted; OIDC in particular is a recent and enterprise-gated addition, so check what your plan and version include before you promise it.
Watch out: SSO introduces a new failure mode — if the IdP is down or misconfigured, nobody can log in through it. n8n keeps a fallback: the instance owner can still sign in with email and password. Verify that fallback works before you need it, and protect that owner account with 2FA, because it is now the bypass around your IdP's defenses.
LDAP (Lightweight Directory Access Protocol) lets a self-hosted n8n instance authenticate users against a corporate directory such as Active Directory: users sign in to n8n with their directory password, and accounts can be provisioned from the directory automatically. It is the right fit for organizations whose identity source of truth is an on-premises directory rather than a cloud IdP. If you have both options, prefer SSO — it keeps passwords out of n8n's login flow entirely and carries the IdP's MFA with it. LDAP support, like SSO, sits in the enterprise-grade feature set.
| Mechanism | What it does | Typical fit |
|---|---|---|
| 2FA / MFA | Second proof (TOTP code) at n8n's own login | Everyone, always; the baseline |
| SAML SSO | Delegates login to a corporate IdP (XML-based standard) | Enterprises whose IdP team standardizes on SAML |
| OIDC SSO | Delegates login to an IdP via OAuth2-family protocol | Teams on modern IdPs; same benefits as SAML |
| LDAP | Checks passwords against a corporate directory | Self-hosted instances in Active Directory shops |
Chapter 11 established that n8n encrypts credentials at rest with an instance encryption key. That is good protection, but it still means the secrets are there — in n8n's database, decryptable by the instance itself. Organizations with a dedicated secrets infrastructure can go one step further: leave the secrets in a purpose-built secrets vault and have n8n fetch them at need. n8n's external secrets feature (enterprise-grade) supports the major vaults: HashiCorp Vault, AWS Secrets Manager, Azure Key Vault, GCP Secret Manager, Infisical, and 1Password.
The mechanics: an instance admin connects a provider in the instance
settings (look for Settings > External Secrets), giving
n8n read access to a scoped corner of the vault. n8n then syncs the
names of available secrets on a periodic refresh. When you
build a credential, instead of pasting an API key into the field, you
reference the secret with an expression of the form
{{ $secrets.providerName.secretName }} — the
double-curly-brace language from Chapter 17 (Expressions), pointed at
the vault. At execution time n8n resolves the reference and uses the
current value.
What this buys you, concretely:
Two boundaries to respect. External secret references work in
credential fields — they are not a general-purpose way
to inject values into ordinary node parameters, so don't design
workflows that expect $secrets everywhere. And the sync is
periodic, not instantaneous: after rotating a value or adding a new
secret, allow for the refresh interval (or trigger a manual reload in
the settings) before expecting n8n to see it.
Tip: Mirror your environment discipline inside the vault. A naming scheme like
prod/stripe/api_keyanddev/stripe/api_key, with n8n instances granted read access only to their own prefix, means even a fully compromised dev instance holds no production secrets — the strongest version of the dev/prod separation this volume keeps returning to.
n8n records plenty internally — Chapter 21 (Executions: The System of
Record) covered the execution log. Log streaming
(enterprise-grade) pushes structured events out of n8n in near
real time to destinations you configure: a syslog
server (the long-standing Unix system-logging protocol most log
collectors accept), a generic webhook (which is how you
reach most observability and SIEM platforms — a SIEM
being the security information and event management system your security
team uses to correlate events across tools), or Sentry.
You configure destinations in the instance settings
(Settings > Log Streaming) and choose which event groups
each destination receives: workflow lifecycle events (started, finished,
failed), node-level events, and — most relevant here — audit
events: user logins, user creation and deletion, credential
creation and updates, and similar administrative actions.
The security argument for streaming is one word: tamper-evidence. Logs that live only inside a system can be altered or erased by whoever compromises that system. Events shipped elsewhere the moment they occur are the version of history an attacker cannot rewrite. Streaming audit events to your SIEM answers the questions that follow any incident — who logged in, from where, what changed, when — from a source outside the blast radius. It also enables alerting: a credential modified at 3 a.m., a burst of failed logins, a new user created outside business hours.
Keep the purposes straight: Chapter 25 (Monitoring, Insights, and Proving Value) is about watching the work — throughput, failures, value delivered. Log streaming here is about watching the platform — identity and administrative actions. Same pipes, different questions; a healthy operation wires up both.
n8n ships a built-in security audit that inspects your instance and reports risks — a checklist that runs itself. On a self-hosted instance you run it from the command line on the host (see Chapter 35 for the CLI generally):
n8n audit
It is also exposed through n8n's public REST API (a POST
to the /audit endpoint, authenticated as the instance
owner) and through the n8n node's own Audit operation, so
you can generate a report programmatically — including, naturally, from
an n8n workflow on a schedule.
The report groups findings into five risk categories:
| Category | What it flags |
|---|---|
| Credentials | Credentials no resource uses, and credentials not used in any recent execution — access that exists for no reason |
| Database | Nodes that run queries built from expressions — the SQL-injection-shaped risks in your workflows |
| Filesystem | Nodes that read or write the host's filesystem — escape hatches worth knowing about |
| Nodes | Risky built-in nodes that can run code or reach the host (Execute Command, Code, HTTP Request, SSH, FTP) plus community and custom nodes — the arbitrary-code and third-party inventory |
| Instance | Instance-level hygiene: webhooks without authentication, risky settings, and whether your n8n version is outdated |
Read the report as a prompt for judgment, not a verdict. A filesystem finding on a workflow whose entire job is writing files is not a defect; the same finding on an instance that should never touch disk is a lead. The unused-credentials list, on the other hand, is nearly always actionable: delete what nothing uses. Run the audit on a schedule — monthly, and after any burst of building — and before and after major changes like enabling community nodes or exposing a new webhook surface, so you can diff the risk picture. Chapter 39 (The Troubleshooting Encyclopedia) is the companion when a finding needs a fix you don't recognize.
Everything above converges here. If your n8n instance is reachable from the public internet — which every Cloud instance is, and most useful self-hosted instances are, because webhooks need a public door (Chapter 14) — walk this list. Items marked (self-hosted) do not apply to Cloud, where the vendor operates that layer for you (Chapter 31 covers what remains yours on Cloud).
Transport and perimeter
Identity and access
Secrets
N8N_ENCRYPTION_KEY) set
explicitly and a copy stored in your password manager or vault — lose it
and every stored credential is permanently undecryptable.
(self-hosted)Attack surface
N8N_PUBLIC_API_DISABLED=true), and API keys treated like
owner passwords if it stays on. (self-hosted; Cloud users can
disable the API in instance settings where offered)N8N_COMMUNITY_PACKAGES_ENABLED=false); if enabled, vet
what you install — Chapter 15 (Community Nodes and Building Your Own)
explains why a node is code with your permissions.
(self-hosted)NODES_EXCLUDE — the command-execution and filesystem nodes
are the usual candidates. (self-hosted)N8N_BLOCK_ENV_ACCESS_IN_NODE=true); confirm it has not
been turned off. (self-hosted)N8N_RESTRICT_FILE_ACCESS_TO. (self-hosted)Webhooks
/webhook/orders), while treating obscurity as seasoning,
never the meal.Vigilance
n8n audit run monthly and after major changes; findings
triaged, not filed.Tip: Do not try to adopt this chapter in one afternoon. The order that pays fastest: 2FA for privileged accounts, the encryption key backed up, production work moved into projects, webhook authentication, then the audit to tell you what you missed. Everything else is compounding interest.
Security work never produces a feature you can demo, which is why it loses scheduling battles until the week it becomes the only thing anyone works on. The version of this material worth internalizing is small: default-deny plus deliberate grants, fences instead of folder names, secrets that grant use without sight, sign-in that offboards itself, and an audit trail that lives outside the building. Set those five up once, review them quarterly, and your n8n instance will be the rare system where growing the team does not mean growing the risk.
Everything you have done so far in this book, you did by pointing and clicking: dragging nodes onto a canvas, filling in fields, pressing the button to activate a workflow. That is the front door, and for most work it is the only door you need. But n8n is not only a place where you build automations. It is itself a running system with a database, a set of workflows, a pile of credentials, and a history of executions, and every one of those things can be read and changed from outside the editor. That outside surface is what this chapter is about.
Treating n8n as a programmable platform buys you three things that clicking cannot. First, repeatability: you can back up, copy, and re-create an entire instance the same way every time, instead of rebuilding it by hand. Second, promotion discipline: you can move a workflow from a place where you experiment to a place where real work happens, with a review step in between, so a half-finished change never lands on a live customer. Third, self-management: n8n can watch n8n, so a broken workflow tells on itself instead of failing silently. We will walk through each entry point, sort out which one you should reach for, and finish with a change-management checklist that ties the whole thing back to the monitoring habits from Chapter 25 (Monitoring, Insights, and Proving Value).
There are four distinct surfaces here, and they overlap enough that people confuse them. Keep them separate in your head:
| Surface | What it is | Where it runs | Who has it |
|---|---|---|---|
| Workflow JSON export/import | Copy a workflow out as a text file and back in | Inside the editor | Everyone, every plan |
| Public REST API | An HTTP interface to manage the instance over the network | A network endpoint on your instance | Cloud and self-hosted, on plans that include it |
| CLI | Admin/shell commands run on the server itself | The machine (or container) hosting n8n | Self-hosted (on-box) |
| Source Control | Git-backed promotion between environments | Wired into the editor | Business and Enterprise |
The mental split that matters most: the API talks to n8n over the network and works whether you are on Cloud or hosting it yourself, while the on-box CLI runs on the box and therefore exists only if you self-host (Chapter 32, Self-Hosting from Zero) and can open a shell on the server. (There is also a newer remote command-line tool, covered in the CLI section, that is really just the API in a shell wrapper.) JSON export is the humble universal option everyone can use, and Source Control is the grown-up version of moving work between instances. We will take them in order of how many readers can use them, starting with the API.
The public REST API is an HTTP interface, meaning you talk to it the
same way any program talks to any web service: you send a request to a
URL and get JSON back. If the HTTP Request node from Chapter 13 (The
HTTP Request Node: Talking to Any API) taught you to call other people's
APIs, this is n8n exposing exactly that kind of API for itself. The base
path is /api/v1 on your own instance, so on Cloud it is
something like https://your-workspace.app.n8n.cloud/api/v1
and self-hosted it is your own hostname. One gate to know up front: the
API is a paid feature and is not available during the Cloud free
trial.
The API does not use your login. It uses a separate long-lived token
called an API key, which you create in your settings
under Settings > n8n API. When you generate a key you
give it a label and an expiration, and
n8n shows you the key once. Copy it immediately and store it in a
password manager or your secrets vault, because you cannot see it again;
if you lose it you delete it and make a new one. Give every integration
its own key so you can revoke one without breaking the others.
Every request carries the key in a header named
X-N8N-API-KEY. A header is just a labelled piece of
metadata sent alongside the request; here the label is
X-N8N-API-KEY and the value is your token. There are no
OAuth handshakes and no per-request signing. That simplicity is
deliberate, and it is also the reason to treat the key like a root
password: by default a key has full access to everything the account can
do, so anyone holding it can read your workflows and executions and
create credentials on your instance. On Enterprise plans you can narrow
a key with scopes, restricting it to specific resources
and actions (say, read-only on executions for a monitoring integration);
a scoped key is the safer choice whenever a key does not need the run of
the whole instance.
Watch out: An n8n API key is effectively an admin credential for the whole instance. Do not paste it into a workflow's plain field, a shared doc, or a client-side script. Store it as an n8n credential or in an external secret store (Chapter 34, Access, Security, and Secrets for Teams) and reference it, never inline it.
The API is organised by resource, and the resource list maps closely to the things you already recognise from the editor. These are the groups you will actually use:
| Resource | What it manages | Typical use |
|---|---|---|
| Workflows | Create, read, update, activate, delete workflows | Back up, deploy, bulk-tag, toggle active state |
| Executions | List and inspect past runs; delete them | Meta-monitoring, cleanup, reporting |
| Credentials | Create and delete credentials; read their type schema | Provisioning; cannot read stored secret values |
| Tags | Manage the tags you organise workflows with | Housekeeping at scale |
| Variables | Read and manage instance variables | Sync config across instances |
| Users / Projects | Manage members and projects | Team provisioning |
| Source Control | Trigger a pull on the instance | Automate promotion (covered below) |
| Audit | Run a security audit and get findings back | Scheduled security posture checks |
The one asymmetry worth memorising: the Credentials endpoint lets you create and delete credentials, and hand back a credential type's schema, but it never returns the secret values inside existing ones. You can ask "what fields does a Slack credential need?" and you can push a new credential in, but you cannot pull "what is the actual token stored in credential 42?" back out. That is a security boundary, not a bug, and it shapes how promotion works later in this chapter.
To see exactly which endpoints and fields your version supports,
self-hosted instances serve an interactive Swagger playground at
/api/v1/docs (a page where you can read and try every
endpoint). That playground is not available on Cloud; Cloud users get
the same endpoint reference, with a live playground, on n8n's
documentation site. Either is generated from the API's own
specification, so it is always correct for the version you are on, which
is far safer than trusting a printed list (including this one) as
versions drift.
You do not need a special tool to use the API. You can call it with
curl from a terminal, from a script, from Postman, or from
an HTTP Request node inside n8n itself. A read looks like a
GET to /api/v1/workflows with your key header,
and it returns a paginated JSON list of your workflows. Activating a
workflow is a POST to
/api/v1/workflows/{id}/activate. Creating something is a
POST with a JSON body describing it. The whole thing
behaves like any other REST API, which is the point: once you learned
Chapter 13, you already know how to drive this.
One vocabulary note that trips people up: n8n 2.0 renamed the
editor's Activate/Deactivate toggle to
Publish/Unpublish. The public API, however, still speaks of
active workflows and still exposes the activate
endpoint, so in this chapter "activate" and "publish" mean the same
event seen from two surfaces.
Tip: The fastest way to learn the API is to do the thing once in the editor, then call the matching read endpoint and look at the JSON it returns. That JSON is the exact shape you send back when you want to create or update, so you rarely have to guess a field name.
Here is where the API earns its keep for an operator rather than a developer. Because a workflow can make HTTP calls, and the API is just HTTP, you can build a workflow whose job is to watch all the other workflows. This is the meta-monitoring pattern that Chapter 25 introduces from the reporting side; this chapter is the plumbing underneath it.
The canonical version is a small scheduled workflow that runs, say,
every fifteen minutes. It calls GET /api/v1/executions
filtered to failures since the last run, and if anything comes back it
summarises the failures (which workflow, when, the error) and posts a
message to Slack, email, or a dashboard. You can make that call with the
HTTP Request node, or with the purpose-built n8n node,
which wraps this same API with the operations and endpoints pre-filled
so you pick "Execution: Get Many" from a dropdown instead of typing the
URL. Now a workflow that dies quietly at 3 a.m. produces a visible alert
instead of a silent gap, and it does so without you configuring an error
workflow on every single automation (though for per-workflow reactions
you still want the error workflow from Chapter 23, Error Handling:
Retries, Error Branches, and Error Workflows). The two approaches
complement each other: error workflows react to one failure the instant
it happens, meta-monitoring sweeps the whole instance on a schedule and
catches patterns.
The same trick powers a nightly backup: a scheduled workflow calls
GET /api/v1/workflows, loops the results, and writes each
workflow's JSON to Git, cloud storage, or a Data Table (Chapter 20,
Files and Remembered State). You will notice this is n8n backing itself
up using its own API, which feels recursive but is exactly the kind of
self-maintenance a mature instance runs quietly in the background.
Watch out: A meta-monitoring workflow that queries executions is itself an execution, and it can fail like any other. Make sure it has an error workflow or an external heartbeat check, otherwise the one workflow you rely on to notice outages can go dark and take your visibility with it. A monitor that cannot report its own death is not a monitor.
The CLI in this section is a set of commands you run in a shell on the machine hosting n8n. Because it needs shell access to the server and direct reach into the database, it exists only when you self-host. On n8n Cloud there is no on-box shell, and everything these commands would do you accomplish through the API or the editor instead.
n8n's own docs now call this the Server CLI, to distinguish it from a newer, separate n8n CLI: a remote command-line tool that wraps the public REST API, authenticates with an API key, and therefore runs from any machine and works against any instance including Cloud. That remote tool is aimed at scripting, CI/CD pipelines, and AI-agent tooling, but it does nothing the API section above did not already cover — it is the API with a friendlier prompt. This section is about the on-box Server CLI, which reaches things the API cannot, because it talks straight to the database.
How you invoke the Server CLI depends on how you installed n8n. If
you installed it with npm, the n8n command is on your path
and you type n8n export:workflow --all. If you run it in
Docker (the common case), the command lives inside the container, so you
prefix it:
docker exec -u node -it <container> n8n export:workflow --all.
Same commands, different doorway.
The workhorses are four commands: export:workflow,
import:workflow, export:credentials, and
import:credentials. The export commands pull records out of
the n8n database as JSON files; the import commands push JSON files back
in. The flags you will use most:
--all exports everything of that type, rather than a
single record.--id=<id> targets one specific workflow or
credential.--output=<path> sets where files are
written.--separate writes one file per workflow into a
directory instead of one combined file, which is what you want if you
plan to track them in Git.--pretty formats the JSON so a human can read it.--backup is a convenience preset for a full, separated
backup.There are two gotchas that bite people. First, exports include the internal IDs of the records. When you import a file whose ID already exists in the target database, n8n overwrites that record rather than creating a duplicate. That is exactly what you want for a restore and exactly what you do not want if you were expecting a fresh copy, so know which you are doing. Second, commands operate on the database, and a running n8n may not notice the change until it restarts. If you import workflows into a live instance, plan to restart it so the changes take effect cleanly.
Credentials deserve special care. Exported credentials are encrypted with your instance's encryption key by default, which means they are only importable into an instance that shares that key. There is a flag to export them decrypted (in plain readable form), and it is occasionally necessary, but a file of decrypted credentials is a file of live passwords sitting on disk. Handle it accordingly, and delete it the moment you are done.
Watch out: Never let the credential encryption key drift between a source and target instance you intend to migrate between. If the keys differ, imported encrypted credentials load but cannot be decrypted, and every affected workflow fails at run time with a confusing decryption error rather than a clear "wrong key" message.
Beyond import and export, the CLI is where a few operations live that have no editor button:
execute --id <id> runs a single saved workflow by
ID from the shell. This is useful for a one-off scripted or CI-driven
run where you do not want a webhook or timer trigger.publish:workflow --id <id> and
unpublish:workflow control which saved version of a
workflow is live. These replaced the old
update:workflow --active command, deprecated in n8n 2.0
when the editor's activate/deactivate toggle became publish/unpublish.
Deliberately, publish:workflow has no --all
flag, so you cannot bulk-publish every workflow by accident.user-management:reset returns the instance to its
pre-setup state by removing all user accounts. This is the lockout
rescue: if you are the owner, you have forgotten your password, and you
have no SMTP configured to email a reset, this gets you back in.license:info reports the state of your self-hosted
license, which tells you at a glance whether the paid features in this
chapter (Source Control, Variables) are actually enabled.audit runs a security audit against the instance and
returns findings such as risky credentials, unprotected webhooks, and
outdated settings. It is worth running on a schedule.Watch out:
user-management:resetdeletes every user account on the instance. It is a genuine rescue tool, not a maintenance command; run it only when you are locked out, and expect to re-invite your whole team afterward.
Not every reader is on a Business plan, and none of the above requires you to be. The most portable, most democratic entry point is the plain workflow JSON that everyone has, on every plan, right inside the editor. Every workflow is, underneath the canvas, a JSON document describing its nodes, their settings, and the connections between them. You can get that document out and back in without any API key or shell.
From the canvas you can select nodes and copy them straight to your clipboard, then paste them into another workflow or another instance; the canvas serialises the selection as JSON and deserialises it on paste. You can download a whole workflow to a file from the workflow's menu, and you can import from a file, from a URL, or from clipboard the same way. This is how the template gallery works, how people share solutions in the community, and how you move a single workflow between two instances when you do not have Source Control.
What travels in that JSON is the structure: nodes, parameters, expressions, notes, and connections. What does not travel is your secrets. A node that uses a credential carries only a reference to that credential (its name and type), not the token inside it. So when you import a workflow into a fresh instance, the nodes arrive intact but show up needing a credential selected, and you connect them to credentials that exist on that instance. This is the same secret-stays-put principle you will see again in Source Control, and it is a feature: your JSON can be shared publicly without leaking a single password.
Tip: Before any risky edit, download the workflow's JSON as a one-file snapshot. It is the poor operator's version control: if the edit goes wrong, you delete the broken workflow and re-import the file to get back exactly where you were. Keep these snapshots in a dated folder and you have a rollback path that costs nothing and works on every plan.
A variable in n8n is a named value you set once
through the interface and read anywhere in your workflows as
$vars.THE_NAME. Inside a workflow they are read-only: you
reference them in expressions (Chapter 17, Expressions: The
Double-Curly-Brace Language) and in Code nodes, but you set and change
them only through the interface, not from a running workflow. You manage
them in the Variables area of your instance. They are a paid feature —
available on n8n's paid Cloud plans (Pro and above) and on self-hosted
Enterprise — and on recent versions they can be scoped either globally
across the whole instance or narrowly to a single project, with a
project-scoped value winning over a global one of the same name inside
that project.
The point of variables is to pull environment-specific configuration
out of your workflow logic. Suppose a workflow posts to an API whose
base URL is different on your test server and your production server.
Rather than hard-code the URL into a node, where promoting the workflow
would carry the wrong value along, you store it in a variable named
API_BASE_URL and reference
{{ $vars.API_BASE_URL }} in the node. Now the same workflow
JSON runs correctly on both instances, because each instance holds its
own value for that variable. The same goes for a spreadsheet ID that
differs between environments, a feature flag you flip to turn a branch
on or off, or a support email address that changes by region.
There is a hard line to draw here, and it is the reason variables are not the answer to everything. Variables are for configuration, not for secrets. They are readable by anyone who can build workflows on the instance, they are not encrypted the way credentials are, and their names (as stubs) travel through Source Control. An API base URL is fine. An API token is not; that belongs in a credential (Chapter 11, Credentials from Zero) or an external secret store (Chapter 34).
Watch out: Do not put passwords, tokens, or keys in variables because it feels convenient. Variables are visible to workflow builders and are not protected like credentials. The moment a secret lands in a variable, it has effectively been shared with everyone who can open the editor.
Everything so far has been about moving pieces of n8n around by hand or by script. Source Control is the built-in, disciplined version of that, available on Business and Enterprise plans, and it is how serious teams run more than one environment.
An environment is a separate n8n instance dedicated to a stage of work: a development instance where you build and break things, and a production instance where the real, active workflows run against real data and real customers. The reason to separate them is the same reason you do not rehearse a play on opening night in front of the audience. When development and production are the same instance, every edit you make is live the instant you save it, and a half-finished change or a typo in an expression fails a real run. Environments give you a safe place to work and a controlled step to move finished work into production.
Source Control connects each n8n instance to a branch of a
Git repository. Git is the standard version-control system
developers use to track changes to files over time; a branch is one
parallel line of that history. You set this up in the instance settings
under Settings > Source Control, where you point n8n at
a repository, provide an SSH key so it can authenticate, and choose the
branch this instance lives on. A common arrangement is a development
instance on a dev branch and a production instance
connected to main, with production's connection set
read-only so nobody can push straight to it.
Once connected, n8n stores your workflows, tags, and variable and credential stubs as files in that repository. Because it is real Git underneath, you get real Git benefits: a full history of who changed what and when, the ability to review a change as a diff before it lands, and the ability to roll back to any previous commit.
Work moves through two operations. Push takes the current state of your instance and commits it to the connected branch. Pull takes what is in the branch and loads it into the instance. The intended shape is one-directional: you build on development, push to Git, then pull into production. Here is precisely what each operation carries:
| Item | Pushed to Git | Pulled into target |
|---|---|---|
| Workflows | Full definition | Loaded, ready to activate |
| Tags | Yes | Loaded |
| Variables | Name and stub, no value | Present but you set the value on the target |
| Credentials | Stub only: ID, name, type (and any expression-based fields) | Present but you fill in the real secret on the target |
The line to internalise is the credential row. A push never puts your secret values into Git. The repository gets a stub describing that a credential of a certain type and name exists, but the actual token, password, or key stays out of version control entirely. When production pulls a workflow that references credential "Stripe Prod," the workflow arrives wired to that credential slot, and production supplies the real Stripe secret from its own copy of that credential. Your Git history is therefore safe to keep alongside your code, because it contains no live secrets.
One more detail follows from n8n's publish model: a push captures each workflow's current saved version, not whatever is currently published, and a pull loads that saved version without turning it on. The target does not go live until you publish it there — either by hand or, if you choose it in the pull dialog, by the optional auto-publish step. Promotion never flips production on by surprise.
Two operational realities come with this. First, direction discipline matters. Because both instances can push and pull, it is possible to edit the same workflow in two places and create a mess. n8n resolves conflicts automatically for credentials and variables, but it deliberately does not try to auto-merge conflicting workflows; instead it makes you choose explicitly what to do when you push or pull a workflow that changed on both sides. The way to avoid ever facing that choice is to keep work flowing one way: development is where edits happen, production only ever pulls. Second, permissions enforce the shape. Instance owners and admins can both push and pull; project admins can push their project's changes up but cannot pull, which keeps the act of loading changes into an environment in the hands of whoever owns that environment.
Tip: Make your production instance's connection read-only and never edit workflows directly on it. If every production change must arrive by a pull from Git, you get a reviewable, revertible record of every single thing that ever went live, and you make the "someone hot-fixed prod at midnight and nobody knows what they changed" problem structurally impossible.
Chapter 11 (Credentials from Zero) promised that the question of how one workflow uses different secrets in different environments would be answered here. This is the answer, and it falls out naturally from the stub design above.
The workflow references a credential by its slot: a credential of a given name and type. Each environment holds its own real value in that slot. Your development instance's "Stripe" credential contains a test key; your production instance's "Stripe" credential, in the same-named slot, contains the live key. The workflow JSON is identical in both places and never mentions either key. You populate each environment's real secrets once, on that instance, and from then on every pull brings workflows that simply plug into the credentials already sitting there. Combine this with variables for the non-secret differences (the base URLs, the IDs) and a single workflow definition runs correctly across every stage without a single environment-specific value baked into it.
For teams that want to avoid entering secrets by hand on each instance at all, the next step up is an external secret store (Chapter 34): each environment's n8n connects to its own vault, or its own path within a shared vault, and pulls the right secret at run time. The workflow references a secret by name, and which value that resolves to depends entirely on which vault the instance is wired to. Same workflow, environment-appropriate secret, and no secret ever typed into n8n or committed to Git.
Watch out: After a pull into a fresh environment, workflows will reference credentials and variables that exist as empty stubs until you fill them in. Do not activate a pulled workflow before you have populated its credentials and variables on the target, or its first real run fails on a missing secret. Populating the target is a required step of promotion, not an optional afterthought.
One surface remains, and most readers will never touch it, but you should know it exists so you can recognise when it is being discussed. n8n Embed, sometimes called the OEM offering, is a separate commercial arrangement for companies that want to build n8n's workflow editor into their own product and offer automation to their own customers under their own roof. If you are running a SaaS and you want your users to build integrations without leaving your app, Embed is the path that lets you surface n8n's editor inside your interface and provision workflows and credentials for each of your customers programmatically through the API.
Two things distinguish Embed from everything else in this chapter. It is a paid, separately licensed offering that you arrange with n8n's sales team rather than a feature you toggle on in settings; and the degree to which you can hide n8n's own branding depends on the specific licensing tier, with the standard OEM path keeping some n8n branding visible in the editor and full white-labelling being a heavier commitment. Embed is not the tool for automating your own internal instance. For that, you already have the API, the CLI, and Source Control described above. Embed is specifically for reselling n8n's building experience to third parties, and if that is not your business, you can safely file it away as a thing that exists.
The through-line of this whole chapter is that a change to a production workflow should never be a surprise. Whether you have Enterprise Source Control or nothing but the JSON download button, the same discipline applies, and it closes the loop back to the monitoring you set up in Chapter 25. Here is the checklist, in order:
Draft away from production. Make the change on your development instance, or if you have only one instance, on a duplicated copy of the workflow, never on the live one. The live version keeps running untouched while you work.
Review the change as a diff. With Source Control, this is a literal Git diff you can read before you push. Without it, download the new JSON and compare it against your last snapshot, or simply walk the changed nodes one by one. The goal is that no change goes live that a human has not deliberately looked at.
Test with pinned data. Run the workflow against pinned input so you are testing the new logic against known, stable data rather than whatever happens to arrive (Chapter 10, Test, Fix, Activate, and Keep It Tidy, and the pinning technique in Chapter 22, The Debugging Craft). Confirm the output is what you expect before anything touches production.
Promote deliberately, then populate the target. Move the finished workflow into production by the mechanism you have: pull it from Git, import the JSON, or push it through the API. Immediately confirm that the target's credentials and variables are populated, because promotion carries the workflow but not its secrets.
Activate on purpose. Turn the workflow on — the button reads Publish in current versions — as an explicit, separate act, not as a side effect of importing it. Know its trigger and know what it will do on its first real run.
Watch Insights and executions after activation. This is the step people skip and the step that catches regressions. For the first runs after a change, watch the executions list (Chapter 21, Executions: The System of Record) and the Insights metrics (Chapter 25) for a spike in failures, a jump in run time, or a drop in volume. Your meta-monitoring workflow from earlier in this chapter is what makes this automatic: it is already querying the executions endpoint, so a change that starts failing announces itself instead of hiding.
Keep a rollback ready. Because you snapshotted the JSON in step 2 or pushed to Git, going back is a re-import or a revert to the previous commit. A change you cannot undo is a change you should not have shipped.
Run that loop every time and the platform surface stops being a pile of separate tools and becomes a single practice: build safely, review honestly, promote cleanly, and watch closely. The API, the CLI, Source Control, and the humble JSON file are just the four ways to carry a change through it, and Chapter 25's monitoring is how you know the change was good after it landed. That is what it means to operate n8n as an owner (Chapter 31, Operating n8n Cloud Like an Owner) rather than a tourist: the instance is not just something you click, it is something you can hold in your hands, version, move, and trust.