Self-hosted n8n alternatives compared
Activepieces, Windmill, Node-RED, Automatisch and Huginn measured against n8n on licence, RAM, database, AI steps and the backup that breaks restores.
What to use instead of n8n
The self-hosted n8n alternatives worth your time on a VPS (virtual private server) are Activepieces, Windmill, Node-RED, Automatisch and Huginn. Activepieces is the closest replacement for the way most people use n8n, and its core is MIT licensed. Windmill fits a team that would rather write Python or TypeScript than drag boxes around a canvas. Node-RED is the small one, and it needs no database at all.
Plenty of readers should stay where they are. The n8n licence permits internal business use, so if you run flows for your own company, the licence is not your problem. Migration is not free either. Nothing in this list reads an n8n export, so you rebuild every flow by hand and re-enter every credential. Installing n8n itself is a separate job, covered in the n8n install on a VPS with Docker and HTTPS, and n8n against Zapier and Make covers how this whole category compares with the hosted services.
Why people look for self-hosted n8n alternatives
Two reasons come up again and again.
The first is the licence. n8n ships under the Sustainable Use License v1.0, which the project calls fair-code rather than open source. The licence grants the right to "use or modify the software only for your own internal business purposes or for non-commercial or personal use", and it forbids providing the software to other people commercially. Files and folders with .ee in the name sit under a separate n8n Enterprise License. If you want to run automations on behalf of paying clients, that is a hard stop. If you are an internal operations team, it changes nothing about your day.
The second is memory. n8n is a Node.js process, and workflow data sits in memory while the run happens. The n8n documentation names the causes: the amount of JSON data, the size of binary data, the number of nodes in a workflow, the Code node, manual executions (which copy the data again for the editor), and other workflows running at the same time. The documented fix is not a different product. It is queue mode with separate worker processes, plus Postgres instead of the default SQLite file at ~/.n8n/database.sqlite. Large jobs also want batching, because a Loop Over Items node feeding a sub-workflow keeps only one slice of the data in memory at a time. Try that before you rebuild sixty flows somewhere else.
Which self-hosted n8n alternatives are still maintained
Licence text is easy to read, so everyone compares licences. Project health is easy to skip. These are the 6 projects in this comparison, with the newest tagged release each one had on 4 August 2026.
The data behind this chart
[
{
"tool": "n8n",
"licence": "Sustainable Use License",
"latest_release": "2.33.3",
"released": "2026-07-31",
"days_since_release": 4
},
{
"tool": "Activepieces",
"licence": "MIT core, commercial ee",
"latest_release": "0.86.3",
"released": "2026-07-17",
"days_since_release": 18
},
{
"tool": "Windmill",
"licence": "AGPLv3 source, CE image",
"latest_release": "v1.778.0",
"released": "2026-08-04",
"days_since_release": 0
},
{
"tool": "Node-RED",
"licence": "Apache 2.0",
"latest_release": "5.0.4",
"released": "2026-07-30",
"days_since_release": 5
},
{
"tool": "Automatisch",
"licence": "AGPL-3.0, commercial ee",
"latest_release": "v0.15.0",
"released": "2025-08-08",
"days_since_release": 361
},
{
"tool": "Huginn",
"licence": "MIT",
"latest_release": "v2022.08.18",
"released": "2022-08-18",
"days_since_release": 1447
}
]Two rows change the shortlist. Automatisch last tagged v0.15.0, which is 361 days old, and its default branch has had no commit since 15 January 2026. Huginn last tagged a release 1447 days ago, yet its commit log is active this month. That is the opposite pattern: the code moves and the releases do not, so running it means running an untagged image.
Check this yourself before you trust any comparison, including this one. Open the releases page for the project on GitHub, then the commit list for its default branch. A project with a fresh release and a quiet commit log is coasting. A project with fresh commits and no release in years is asking you to run code that nobody has cut a version for.
Activepieces: the closest match, and MIT at the core
Activepieces is the like-for-like choice. It is a visual builder with triggers and steps, which it calls pieces, and the README claims more than 280 of them. Every piece is also exposed as an MCP (model context protocol) server, so an LLM (large language model) client can call the same connectors as tools. The core is MIT licensed. Two directories, packages/ee/ and packages/server/api/src/app/ee, carry a commercial licence, and using what is inside them on your own server needs a paid agreement.
Read that split before you migrate, because it is wider than in most MIT projects. The Activepieces pricing page describes the Community Edition as "open source, free forever, with no cap on runs, users, or flows", and places Agents and Chat, Projects, API access, and the whole administration layer (single sign-on, user roles, audit logs, secret managers, branding, Git sync) outside it. So the Community Edition is a complete automation engine with unlimited flows and users, and it is not a platform you can drive from an API. If your plan was to generate flows programmatically, that plan needs a licence.
The runtime shape is one app container, one or more worker containers, Postgres and Redis. AP_DB_TYPE=POSTGRES and AP_REDIS_TYPE=STANDALONE are the defaults. There is a single-container mode with an embedded database and an in-process queue (AP_DB_TYPE=PGLITE with AP_REDIS_TYPE=MEMORY), and the documentation says it "is only meant for personal use or testing". Take that at face value. Those modes cannot run more than one instance, so growing out of them is a migration, not a flag.
Windmill: code first, and heavier than it looks
Windmill runs scripts in Python, TypeScript, Go, Bash and SQL, then composes them into flows. If your automations are mostly code with a small amount of glue around them, it fits better than any node canvas.
The licence needs care. The source is AGPLv3 when compiled without the enterprise feature flag. The images published at ghcr.io/windmill-labs/windmill are the Community Edition, which includes code that is not open source and is free to use inside quotas. Windmill's pricing page sets those quotas at 50 users, 3 workspaces and 10 GiB of workspace object storage, with unlimited executions. For one person or one small team that ceiling is far away, so the practical question is not the quota. It is that the binary you run is not the AGPL build.
Weight is the other consideration. Windmill's own docker-compose.yml ships a Postgres 16 database, one server, three default workers with a 2048M memory limit each, a native worker and a Caddy proxy. The documented rule of thumb is "1 worker per 1vCPU and 1-2 GB RAM". You can cut the replica count on a small box. You should know that you are cutting it, because the workers are what actually run your jobs.
Windmill's AI features are documented as build-time help: code generation, flow building, chat and form filling. They need you to add a model provider resource in workspace settings first. If what you want is an agent step that runs on a schedule and calls tools, n8n's AI Agent node is still the more direct route, and building an AI agent in n8n covers that shape.
Node-RED: the small one, with no database at all
Node-RED is Apache 2.0, the most permissive licence in this comparison. It is one Node.js process with a /data volume. No Postgres. No Redis. Pin it as nodered/node-red:5.0.4, which is the current release.
It grew out of IoT (internet of things) wiring, so it is event-shaped rather than connector-shaped. Nodes for third-party services come from the community library and vary in quality, which is the trade you make for the small footprint. There is no first-class AI agent step. For a small VPS handling webhooks and message-queue traffic, it is the lightest thing here that works, and it starts in seconds.
Huginn and Automatisch: check the commit log first
Huginn is MIT, written in Ruby on Rails, and needs MySQL or PostgreSQL. It thinks in agents that watch a source and emit events, which is a different model from a flow canvas, and it has no LLM story. The code still gets commits, but the last tagged release is from August 2022, so running it means the ghcr.io/huginn/huginn image built from the default branch. Pick it when the agent model matches your problem, not as a general n8n replacement.
Automatisch is AGPL-3.0 apart from its .ee files, and it looks like a simpler n8n: Postgres, Redis, a small catalogue of apps. It is the tool that single-deploy tutorials keep recommending. The release history says to wait. A year without a release and half a year without a commit is not a reason to panic if you already run it, and it is a reason not to start a new production deployment on it.
What an Activepieces stack really costs in RAM
Measured idle and working memory is not a figure anyone can publish for you, because it depends on your own flows and how much data they carry. What you can read is what each vendor tells you to budget. Activepieces documents the shape below, and the sentence beside it matters more than the numbers: "A concurrency-1 worker is busy for a flow's whole duration (up to 10 min), so size by concurrent flows, not trigger rate."
The data behind this chart
[
{
"label": "App container",
"vcpu": 1,
"ram_gb": 1
},
{
"label": "Worker (each)",
"vcpu": 0.5,
"ram_gb": 1
},
{
"label": "Postgres",
"vcpu": 2,
"ram_gb": 4
},
{
"label": "Redis",
"vcpu": 1,
"ram_gb": 1
}
]One worker is 0.5 vCPU and 1 GB, and it runs exactly one flow at a time. Postgres is sized at 4 GB. The project's own compose file ships five worker replicas, so by that sizing the stack in the repository asks for roughly 11 GB before your flows do anything interesting. Single-tool tutorials copy that file and call it a small deployment.
On a 4 GB VPS, run two workers, keep Postgres in the same compose project, and measure. docker stats --no-stream prints one line per container with its real resident memory, which beats any figure published by a vendor or a blog. If a container grows without limit, cap it, and memory limits in Docker Compose shows the syntax.
A compose file for Activepieces on one VPS
Pin the tag. latest means the next docker compose pull can move the database schema under you without warning. Version 0.86.3 is what the project pins in its own compose file as of 4 August 2026.
Generate the two secrets first, using the lengths the documentation specifies.
openssl rand -hex 16 # AP_ENCRYPTION_KEY, encrypts stored connections
openssl rand -hex 32 # AP_JWT_SECRET, signs session tokensWrite .env next to the compose file:
AP_ENGINE_EXECUTABLE_PATH=dist/packages/engine/main.js
AP_ENVIRONMENT=prod
AP_FRONTEND_URL=https://automation.example.com
AP_ENCRYPTION_KEY=REPLACE_WITH_HEX_16
AP_JWT_SECRET=REPLACE_WITH_HEX_32
AP_DB_TYPE=POSTGRES
AP_POSTGRES_DATABASE=activepieces
AP_POSTGRES_HOST=postgres
AP_POSTGRES_PORT=5432
AP_POSTGRES_USERNAME=postgres
AP_POSTGRES_PASSWORD=REPLACE_WITH_A_LONG_RANDOM_PASSWORD
AP_REDIS_TYPE=STANDALONE
AP_REDIS_HOST=redis
AP_REDIS_PORT=6379
AP_EXECUTION_MODE=UNSANDBOXED
AP_TELEMETRY_ENABLED=falseAP_FRONTEND_URL has to be the public HTTPS address, because Activepieces otherwise tries to use your public IP address when it builds webhook URLs. Every webhook you hand to a third party is built from that value, so if it still points at localhost, the URL you paste into another service never reaches your server.
services:
app:
image: ghcr.io/activepieces/activepieces:0.86.3
restart: unless-stopped
ports:
- '127.0.0.1:8080:80'
depends_on:
- postgres
- redis
env_file: .env
environment:
- AP_CONTAINER_TYPE=APP
volumes:
- ./cache:/usr/src/app/cache
worker:
image: ghcr.io/activepieces/activepieces:0.86.3
restart: unless-stopped
depends_on:
- app
env_file: .env
environment:
- AP_CONTAINER_TYPE=WORKER
deploy:
replicas: 2
volumes:
- ./cache:/usr/src/app/cache
postgres:
image: pgvector/pgvector:0.8.0-pg14
restart: unless-stopped
env_file: .env
environment:
- POSTGRES_DB=${AP_POSTGRES_DATABASE}
- POSTGRES_USER=${AP_POSTGRES_USERNAME}
- POSTGRES_PASSWORD=${AP_POSTGRES_PASSWORD}
volumes:
- postgres_data:/var/lib/postgresql/data
redis:
image: redis:7.0.7
restart: unless-stopped
volumes:
- redis_data:/data
volumes:
postgres_data:
redis_data:That file is the project's own compose with four changes: the worker count drops from five to two, the published port binds to 127.0.0.1 instead of every interface, the fixed container names are gone because a service with replicas cannot use them, and the explicit network block is dropped because compose creates one anyway.
docker compose up -d
docker compose psEvery service should show Up, including two worker containers. A container that restarts in a loop prints its reason in docker compose logs worker, so read that before changing anything. The port binding means nothing reaches the app from outside until you put a reverse proxy with TLS (transport layer security) in front, which running Traefik in front of several compose apps covers. Keep .env at mode 600 and out of git, as in handling secrets in compose env files.
The backup that every guide leaves out
All of these tools encrypt stored credentials, so a database dump on its own is not a backup. You need the dump and the key that decrypts it. The trap is that most of these tools generate that key for you, quietly, and store it somewhere you are not backing up.
n8n is the clearest case. If you never set N8N_ENCRYPTION_KEY, n8n "creates a random encryption key automatically on the first launch and saves it in the ~/.n8n folder", then uses that key to encrypt credentials before they reach the database. Dump Postgres, restore it onto a fresh container with a fresh volume, and the workflows come back while every credential is ciphertext nobody can read. Set the variable explicitly, and set the same value on every worker when you run queue mode.
Node-RED has the same shape. Credentials live in their own encrypted file, and the key is credentialSecret in settings.js. When you do not set one, the runtime generates a random key and saves it under _credentialSecret in its own settings store inside /data. The stock settings file states the consequence: "once you set this property, do not change it - doing so will prevent node-red from being able to decrypt your existing credentials and they will be lost." Back up the whole /data volume, not just the flows file.
Activepieces keeps AP_ENCRYPTION_KEY in your .env, documented as a "32-character (16-byte) hexadecimal key used to encrypt connections". Huginn keeps APP_SECRET_TOKEN in its environment. Automatisch has three of them: ENCRYPTION_KEY, WEBHOOK_SECRET_KEY and APP_SECRET_KEY. In each case the secret lives in an environment file, which means the environment file is part of the backup.
Windmill is the exception worth knowing about. Its variables and secrets are encrypted with a workspace-specific symmetric key that Windmill stores in its own database, so a single Postgres dump carries both halves. That is convenient for restores, and it means the dump alone is enough to read every secret, so protect the file as if it were the secrets themselves.
For the Activepieces stack above, the backup is two files:
cd /srv/activepieces
docker compose exec -T postgres pg_dump -U postgres -Fc activepieces > "ap-$(date +%F).dump"
cp .env "ap-env-$(date +%F).bak"
chmod 600 ap-*.dump ap-env-*.bakThen prove the backup works, because an untested backup is a guess. Restore the dump into a scratch compose project that uses a deliberately different AP_ENCRYPTION_KEY, then run a flow that uses a saved connection. It fails, because the ciphertext in the database was produced with the other key. Repeat the restore with the real key from .env and the same flow runs. Those two runs are the only evidence that your backup is a backup. Send both files off the server on a schedule with restic backups from a VPS, since a backup on the same disk dies with the disk.
When to stay on n8n
Stay if the work is internal to your own company, because that is exactly what the Sustainable Use License permits. Stay if you lean on breadth, since n8n claims more than 1500 integrations, or on its AI Agent node built on LangChain, which nothing else here matches for ready-made agent steps. Driving n8n workflows with Claude shows what that looks like in practice.
Move to Activepieces if you want a permissive licence on the automation core and a stack you can read end to end. Move to Windmill if your flows are really code wearing a user interface. Move to Node-RED if the box is small and the work is event-shaped. Do not move because a benchmark told you n8n is heavy. Measure your own instance first, then read what is worth self-hosting in 2026 and choose once, because the second migration costs as much as the first.
FAQ
Which self-hosted n8n alternative is closest to n8n?
Activepieces. It is the same idea: a visual builder where a trigger starts a flow and each step calls a service, with a large catalogue of connectors. Its core is MIT licensed, it runs on Postgres and Redis under Docker, and its pieces double as MCP servers for LLM clients. The gap to watch is that API access and the agent features sit in the commercial enterprise directories, so a Community Edition instance is driven through its web interface rather than programmatically.
Is Activepieces really open source?
The core is, under the MIT licence. Two directories, packages/ee/ and packages/server/api/src/app/ee, are commercially licensed, and using those features on your own server needs a paid licence. The vendor's pricing page puts Agents and Chat, Projects, API access, single sign-on, user roles, audit logs, secret managers, branding and Git sync outside the Community Edition, while runs, users and flows stay uncapped. So it is genuinely open source for building and running automations, and it is not open source for the team and governance layer.
How much RAM does Activepieces need on a VPS?
Activepieces documents 0.5 vCPU and 1 GB for each worker, 1 vCPU and 1 GB for the app container, 4 GB for Postgres and 1 GB for Redis. A worker handles one flow at a time for that flow's whole duration, so you size by peak concurrent flows rather than by how often triggers fire. The compose file in the repository ships five workers, which is around 11 GB of published sizing. Two workers on a 4 GB VPS is a reasonable start, and docker stats --no-stream confirms the real number for your flows while they run.
What do I have to back up so a restore actually works?
The database dump and the encryption key, together. For Activepieces that is a pg_dump of the activepieces database plus the .env file holding AP_ENCRYPTION_KEY. For n8n it is the database plus N8N_ENCRYPTION_KEY, which n8n generated for you inside the ~/.n8n folder if you never set it. For Node-RED, back up the whole /data volume, because the credentials file and the key that decrypts it both live there. Windmill is the exception: its workspace key sits inside its own Postgres database, so the dump carries everything and must be guarded as if it were the secrets themselves.
Can I import my n8n workflows into another tool?
No. These projects import and export their own flow formats, not n8n's. A migration means rebuilding each flow in the new builder and creating each credential again from the original service. That work is the real cost of switching, so count your flows before you decide. Twelve flows is an afternoon. Two hundred flows is a project, and it is usually cheaper to fix n8n's memory use with queue mode and Postgres than to rebuild them all.