Evomi

Blog / Network Performance

Self-Hosted n8n: What Changes When Your Workflows Have One IP Address

The ScraperThe Scraper9 min read

The reasons for self-hosting n8n are usually about data. Credentials stay in your database, executions stay on your disk, and nothing about the customer records moving through a workflow leaves infrastructure you control. All of that is true and it is a good reason.

What comes with it, and rarely appears on the list, is the network. On Cloud you do not know or control which address your HTTP Request nodes go out from, and mostly you do not need to. Self-hosted, you know exactly: it is your VPS. One address, in a hosting provider's range, with reverse DNS naming that provider, shared by every workflow on the instance and — because you almost certainly pointed a subdomain at the same box — associated with your organisation by anyone who cares to look.

For most automation that is fine. For anything that collects data from the public web, it is the single biggest behavioural difference between Cloud and self-hosted, and it shows up about three weeks in.

The Egress Address Is Now a Fact About You

Three things are true of your instance's outbound IP that were not true before.

It is in a hosting ASN. Every address belongs to a block, every block has an owner, and the owner of yours is Hetzner or DigitalOcean or AWS. A site sorting traffic by that one attribute can filter your entire provider's range at essentially no cost to itself, because its real customers are not browsing from a rack. This is not a judgement about your traffic; it is a cheap heuristic that is right often enough to keep.

It is static and shared across every workflow. Cloud instances move around. Yours does not. So the eleven schedules you have accumulated, each polling something on its own timer, are one client from the target's point of view — and the aggregate pattern is much more distinctive than any of the individual workflows.

It is attributable. n8n.yourcompany.com resolving to the same address your workflows go out from ties your collection traffic to your organisation by name. That is fine when you intend it, and it deserves to be a decision rather than a side effect of DNS.

Whether any of this matters depends entirely on what you are calling. Internal APIs, partner systems and authenticated SaaS: irrelevant, and none of what follows applies. Public web pages you do not own: it is the whole game.

Separating Instance Traffic From Collection Traffic

The important move is not "put a proxy on it". It is deciding, per call, which identity should make it — and then making sure the boring calls keep working.

n8n gives you two levers, and using the wrong one is the common mistake.

The per-node Proxy option is the right lever for collection. Options → Proxy on an HTTP Request node takes a full URL with credentials, and it takes precedence over everything instance-wide. Only the nodes you set it on are affected, which is what you want: your Slack notifications, your database writes, your OAuth refreshes and n8n's own outbound calls all keep leaving from the address they always did.

http://USER:PASS_country-DE@rp.evomi.com:1000

The details — country and city targeting, sticky sessions, forcing a new IP without losing the session — are in using proxies in the n8n HTTP Request node.

The environment variables are the wrong lever for collection and the right one for a different job. HTTP_PROXY, HTTPS_PROXY and ALL_PROXY apply to every outbound call the instance makes. Set one to a residential endpoint and you have just routed n8n's licence check, your Postgres-over-TLS connection and every webhook callback through a consumer connection somewhere else in the world, which will produce a set of failures that look unrelated to each other and unrelated to what you changed.

Two things to know if you set them anyway, for a corporate egress proxy or similar:

  • Lowercase wins. n8n uses the proxy-from-env package, and http_proxy takes precedence over HTTP_PROXY when both are present. An instance that was configured eighteen months ago may not be using the variable you are reading.

  • Set NO_PROXY at the same time, with your internal hosts and your own APIs in it. Doing it afterwards means doing it during an incident.

Community Nodes Are Yours to Manage Now

On Cloud, verified community nodes install from the nodes panel and the platform keeps them working. Self-hosted, the same install button exists, but the lifecycle is yours.

Four environment variables govern what is possible:

Variable

Default

Effect

N8N_COMMUNITY_PACKAGES_ENABLED

true

Master switch. false disables verified and unverified alike

N8N_VERIFIED_PACKAGES_ENABLED

true

Whether verified nodes appear in the nodes panel for install

N8N_UNVERIFIED_PACKAGES_ENABLED

true, changing to false

Whether arbitrary npm packages can be installed. Current builds print a deprecation notice on startup saying the default will flip in a future version, so set it explicitly either way

N8N_COMMUNITY_PACKAGES_PREVENT_LOADING

false

Emergency brake: skip loading installed community nodes at startup, for when a bad one stops the instance booting

A reasonable posture for a team instance is verified on, unverified off. Verified nodes have been through n8n's review; unverified means anyone's npm package running in your n8n process, and "the instance owner installed something from npm at 11pm" is not a configuration you want to be able to reach by accident.

The genuinely useful pair for anyone who treats infrastructure as code:

YAML
services:
  n8n:
    image: docker.n8n.io/n8nio/n8n
    restart: unless-stopped
    environment:
      - N8N_COMMUNITY_PACKAGES_MANAGED_BY_ENV=true
      - N8N_COMMUNITY_PACKAGES=[{"name":"n8n-nodes-evomi","version":"1.0.2"}]

That pair needs n8n 2.21.0 or newer. With MANAGED_BY_ENV on (it defaults to false), n8n reconciles installed packages against that list on every startup — installing what is missing, correcting versions, and uninstalling anything not on the list. You can watch it happen in the boot log, which opens with communityPackagesManagedByEnv is enabled — reconciling installed community packages from env vars and then prints a line per package it acts on (Installed community package 'n8n-nodes-evomi@1.0.2' from env).

The Community nodes settings page becomes read-only. That makes a rebuilt container come up identical to the one it replaced, which is the property you self-hosted for, and it makes the node list reviewable in a pull request.

The uninstall behaviour is a real edge. Somebody installing a node through the UI on a MANAGED_BY_ENV instance will find it gone after the next restart, and the workflow that used it broken, with nothing in the logs that obviously explains it. Turn it on deliberately and tell the team.

Self-Hosted MCP Endpoints Need Two Settings

If you are exposing workflows over MCP — n8n's built-in instance server, or an MCP Server Trigger node — self-hosting adds two failure modes that Cloud users never see. Both produce a connection that establishes successfully and then does nothing, which is the hardest shape of failure to diagnose.

Reverse proxy buffering. MCP runs over SSE or streamable HTTP. nginx buffers responses by default, and a buffered event stream delivers nothing until it decides to flush:

Shell
location /mcp/ {
    proxy_http_version          1.1;
    proxy_buffering             off;
    gzip                        off;
    chunked_transfer_encoding   off;

    proxy_set_header            Connection '';

    # the rest of your proxy headers
}

Set all of them explicitly rather than relying on defaults, so nothing inherits from elsewhere in the config.

Queue mode with multiple webhook replicas. These transports need the same instance to hold a connection for its whole life. One webhook replica is fine as-is. With several, you have to create a dedicated single-container replica set and route every /mcp* request to it at the ingress or load balancer. Without that, connections break intermittently and unpredictably.

While you are in the reverse proxy: N8N_PROXY_HOPS tells n8n how many proxies it sits behind, and getting it wrong means every request appears to come from your proxy's address, which quietly breaks anything that reasons about client IP.

The Identity Question Underneath All This

There is a version of this problem that no proxy configuration answers.

The reason a single hosting IP gets filtered is that operators have almost nothing else to sort traffic by. They cannot tell your inventory-monitoring workflow from a scraper farm, so they use the address block, and everyone in that block gets the same treatment. Changing the address changes which bucket you land in. It does not make you legible.

The bots that do not get filtered are the ones that can prove who they are. Googlebot's claim is verifiable by reverse DNS, and the whole verification ritual — plus the cryptographic signature schemes being standardised to replace it — is worth understanding if you run automation at any scale, because it is the only durable answer to this problem. Related and more immediate: the distinction between a crawler and an agent acting for a specific person is one most sites' tooling cannot currently make, and an n8n instance running AI-agent workflows is squarely in the category that gets misclassified.

The unglamorous version of the same advice applies today and costs nothing, and n8n has a setting for it that almost nobody has turned on. By default the instance sends a bare n8n User-Agent, which is exactly the shape of string a WAF treats as unidentified automation. Two variables fix it:

Shell
N8N_ENFORCE_GLOBAL_USER_AGENT=true
N8N_GLOBAL_USER_AGENT_VALUE=YourCompanyBot/1.0 (+https://yourcompany.com/bot)

The first swaps the bare string for an RFC-compliant Mozilla/5.0 (compatible; n8n/<version>; +https://n8n.io/) on every outbound request. The second overrides that with whatever you want — which is how you name your own organisation instead of disclosing your n8n version to every server you touch. Setting the second is the better move for collection work: a User-Agent that says who you are with a contact URL in it, a request rate that matches what you told anyone it would be, and honouring robots.txt where it applies. That combination resolves more blocks than any network change, and it is free.

What People Get Wrong

  • Setting HTTPS_PROXY instance-wide to fix one workflow. It routes everything, including n8n's own calls, and the resulting failures look unconnected to the change.

  • Assuming a fresh VPS IP is clean. Addresses get recycled. Yours has a history you did not write, and it may already be on lists.

  • Running collection workflows and internal automation on one instance without thinking about it. They are the same client to everything outside. Sometimes that is fine; it should be a decision.

  • Turning on MANAGED_BY_ENV without telling anyone. UI installs vanish on restart.

  • Forgetting N8N_PROXY_HOPS. Everything appears to originate at your reverse proxy.

  • Treating the egress address as the whole problem. It is the first signal, not the only one — there is a long stack of others underneath it.

  • Leaving the MCP endpoint unauthenticated because it is on your own server. Your own server has a public IP and a DNS record.

Wrapping Up

Self-hosting n8n trades a managed network for a controlled one, and the trade is usually worth making — but the controlled network is only controlled if you actually make decisions about it. The useful mental model is that your instance now has an identity on the public internet: one address, in a hosting range, tied to your domain, shared by every workflow you run.

Which means the question stops being "how do I avoid getting blocked" and becomes "which of my workflows should be speaking as my company, and which should not". Per-node proxying is how you answer it, and keeping the instance's own traffic out of that path is what keeps the rest of the system working while you do.