The complete n8n self-hosting guide
A practical, production-minded walkthrough for self-hosting n8n — from npx and a single Docker container to Docker Compose behind a reverse proxy and full Kubernetes deployments across AWS, Azure, GCP, and OpenShift. Faithful to the official docs, with sources linked throughout.
Updated
What this document is. A practical installation guide for n8n in self-hosted mode that doubles as an article on why it’s worth hosting n8n on your own infrastructure. All technical instructions are faithful to the official n8n documentation; the links point to the original sources and to the resources referenced in each section.
Table of contents
- Why self-host n8n: the advantages
- Before you start: what you need to know
- Install with npm (local machine)
- Install with Docker (recommended)
- Use Docker Compose (Linux server)
- Deploy to a cloud provider
- Which option to choose
- Sources
1. Why self-host n8n: the advantages
n8n can be used in two ways: through the managed n8n Cloud service, or hosted by yourself (self-hosting). Hosting n8n on your own infrastructure brings concrete advantages that surface throughout the official documentation:
-
An isolated, predictable environment. n8n recommends Docker for most self-hosting needs precisely because it provides a clean, isolated environment, avoids operating system and tooling incompatibilities, and makes database and environment management simpler.
-
Control over your data. When you host n8n, you control where your credentials, past executions, and workflows are stored. n8n uses the
.n8ndirectory (and/or a database) to keep sensitive data such as the encryption key, instance logs, and source control feature assets — all under your management. -
Scalability on your terms. For larger deployments, n8n supports PostgreSQL as a more robust database backend than SQLite, and the documentation presents Kubernetes as the best method for scaling n8n as demand changes.
-
Deployment flexibility. You can run n8n locally to try it out (npx, npm), in a single Docker container, with Docker Compose behind a reverse proxy (Caddy or Traefik), or in managed Kubernetes clusters on any of the major cloud providers — choosing whatever best balances cost, effort, and robustness.
-
Potentially lower cost. The OpenShift Local documentation illustrates this well: running a cluster locally is free, versus roughly $135–400/month for a managed equivalent in the cloud. For many cases, a simple shared server (a basic DigitalOcean Droplet or a Hetzner CPX11 type) is enough.
Important — the flip side. Self-hosting n8n requires technical knowledge: setting up and configuring servers and containers, managing application resources and scaling, securing servers and applications, and configuring n8n itself. Mistakes can lead to data loss, security issues, and downtime. n8n recommends self-hosting for expert users; if you aren’t experienced at managing servers, n8n recommends n8n Cloud.
2. Before you start: what you need to know
Stable and Beta versions. n8n releases a new minor version most weeks. The stable version is for production use; the beta version is the most recent release and may be unstable. To report issues, use the forum.
Documentation index. For the complete documentation index, see llms.txt. Any documentation page is available in Markdown by appending .md to the URL.
Useful cross-cutting resources:
- Environment variables — how to configure n8n’s behaviors and settings.
- Scaling n8n.
- Quickstarts — to start exploring n8n.
3. Install with npm (local machine)
Source: Install with npm
npm is a quick way to get started with n8n on your local machine. You must have Node.js installed. n8n requires a Node.js version between 20.19 and 24.x, inclusive.
Try n8n with npx
You can try n8n without installing it using npx. From the terminal, run:
npx n8n
This command downloads everything that’s needed to start n8n. You can then access n8n and start building workflows by opening http://localhost:5678.
Install globally with npm
To install n8n globally, use npm:
npm install n8n -g
To install or update to a specific version of n8n, use the @ syntax to specify the version. For example:
npm install -g n8n@0.126.1
To install the next version:
npm install -g n8n@next
After installation, start n8n by running:
n8n
# or
n8n start
Next steps: try out n8n using the Quickstarts.
Updating
To update your n8n instance to the latest version, run:
npm update -g n8n
To install the next version:
npm install -g n8n@next
Docker required (for the tunnel). The tunnel uses cloudflared, which runs as a Docker container. Make sure Docker is installed on your machine, even when running n8n via npm.
For npm installations, use the services only approach. Start cloudflared as a standalone service, then run n8n locally:
# Terminal 1: Start the cloudflared tunnel service
pnpm --filter n8n-containers services --services cloudflared
# Terminal 2: Start n8n locally
pnpm dev
The services command starts cloudflared, fetches the public tunnel URL, and writes a .env file to packages/cli/bin/.env with WEBHOOK_URL and N8N_PROXY_HOPS=1. n8n picks up this .env automatically on startup.
Clean up when done:
pnpm --filter n8n-containers services:clean
For the full stack approach (n8n and cloudflared both in containers), refer to the Docker tunnel setup.
Reverting an upgrade
Install the older version you want to go back to. If the upgrade involved a database migration:
- Check the feature documentation and release notes to see if there are any manual changes you need to make.
- Run
n8n db:reverton your current version to roll back the database. To revert more than one migration, repeat the process.
Windows troubleshooting
If you experience issues running n8n on Windows, make sure your Node.js environment is correctly set up. Follow Microsoft’s guide to Install NodeJS on Windows.
4. Install with Docker (recommended)
Source: Install with Docker
n8n recommends using Docker for most self-hosting needs: it provides a clean, isolated environment, avoids OS and tooling incompatibilities, and makes database and environment management simpler. You can also use n8n in Docker with Docker Compose; you can find configurations for various architectures in the n8n-hosting repository.
There’s also a video guide: https://www.youtube.com/embed/6ET3G7GiqZA?si=mwCKbtyLqNCRc2pa
Prerequisites
Before proceeding, install Docker:
- Docker Desktop is available for Mac, Windows, and Linux. It includes the Docker Engine and Docker Compose.
- Docker Engine and Docker Compose are also available as separate packages for Linux — use these for Linux machines without a graphical environment or when you don’t want the Docker Desktop UI.
Starting n8n
From your terminal, run the following commands, replacing the <YOUR_TIMEZONE> placeholders with your timezone:
docker volume create n8n_data
docker run -it --rm \
--name n8n \
-p 5678:5678 \
-e GENERIC_TIMEZONE="<YOUR_TIMEZONE>" \
-e TZ="<YOUR_TIMEZONE>" \
-e N8N_ENFORCE_SETTINGS_FILE_PERMISSIONS=true \
-e N8N_RUNNERS_ENABLED=true \
-v n8n_data:/home/node/.n8n \
docker.n8n.io/n8nio/n8n
This command creates a volume to store persistent data, downloads the required n8n image, and starts the container with the following settings:
- Maps and exposes port
5678on the host. - Sets the container’s timezone:
- the
TZenvironment variable sets the system timezone (controls what commands likedatereturn); - the
GENERIC_TIMEZONEenvironment variable sets the correct timezone for schedule-oriented nodes like the Schedule Trigger node.
- the
- Enforces secure file permissions for the n8n configuration file.
- Enables task runners, the recommended way of executing tasks in n8n.
- Mounts the
n8n_datavolume to the/home/node/.n8ndirectory to persist your data across container restarts.
Once running, access n8n by opening http://localhost:5678.
Using with PostgreSQL
By default, n8n uses SQLite to save credentials, past executions, and workflows. n8n also supports PostgreSQL, configurable using environment variables.
Persisting the
.n8ndirectory is still recommended. With PostgreSQL, n8n doesn’t need.n8nfor the SQLite file, but the directory still contains important data such as encryption keys, instance logs, and source control feature assets. While you can work around some of these requirements (for example, by setting theN8N_ENCRYPTION_KEYenvironment variable), it’s best to keep mapping a persistent volume for the directory to avoid potential issues.
To use n8n with PostgreSQL, run the following commands, replacing the placeholders (within angle brackets <>) with your actual values:
docker volume create n8n_data
docker run -it --rm \
--name n8n \
-p 5678:5678 \
-e GENERIC_TIMEZONE="<YOUR_TIMEZONE>" \
-e TZ="<YOUR_TIMEZONE>" \
-e N8N_ENFORCE_SETTINGS_FILE_PERMISSIONS=true \
-e N8N_RUNNERS_ENABLED=true \
-e DB_TYPE=postgresdb \
-e DB_POSTGRESDB_DATABASE=<POSTGRES_DATABASE> \
-e DB_POSTGRESDB_HOST=<POSTGRES_HOST> \
-e DB_POSTGRESDB_PORT=<POSTGRES_PORT> \
-e DB_POSTGRESDB_USER=<POSTGRES_USER> \
-e DB_POSTGRESDB_SCHEMA=<POSTGRES_SCHEMA> \
-e DB_POSTGRESDB_PASSWORD=<POSTGRES_PASSWORD> \
-v n8n_data:/home/node/.n8n \
docker.n8n.io/n8nio/n8n
You can find a complete docker-compose file for PostgreSQL in the n8n hosting repository.
Updating
In Docker Desktop, go to the Images tab and select Pull from the context menu to download the latest image. From the command line:
# Pull latest (stable) version
docker pull docker.n8n.io/n8nio/n8n
# Pull specific version
docker pull docker.n8n.io/n8nio/n8n:1.81.0
# Pull next (unstable) version
docker pull docker.n8n.io/n8nio/n8n:next
After pulling the updated image, stop the container and start it again. From the command line (replace <container_id> with the ID found in the first command):
# Find your container ID
docker ps -a
# Stop the container
docker stop <container_id>
# Remove the container
docker rm <container_id>
# Start the container
docker run --name=<container_name> [options] -d docker.n8n.io/n8nio/n8n
Updating via Docker Compose:
# Navigate to the directory containing your docker compose file
cd </path/to/your/compose/file/directory>
# Pull latest version
docker compose pull
# Stop and remove the older version
docker compose down
# Start the container
docker compose up -d
Tunnel = development only. The tunnel feature is a convenience tool for local development, and its underlying implementation may change between versions. It isn’t safe to use in production.
To use webhooks for trigger nodes of external services (like GitHub), n8n has to be reachable from the web. n8n provides a tunnel service via cloudflared. There are two ways to use it:
- Full stack (n8n + cloudflared in containers; the tunnel URL prints on startup):
pnpm stack --tunnel - Services only (run n8n locally with
pnpm dev/pnpm startand cloudflared as a standalone service) — see the commands in the npm section.
Next steps: see the README for the Docker image; learn more about configuring and scaling n8n.
5. Use Docker Compose (Linux server)
Source: Use Docker Compose
These instructions cover how to run n8n on a Linux server using Docker Compose. If you already have Docker and Docker Compose installed, you can skip to step 3. You can find configurations for various architectures in the n8n-hosting repository.
1. Install Docker and Docker Compose
The installation method depends on your Linux distribution:
Verify they’re available:
docker --version
docker compose version
2. Optional: Non-root user access
To grant access to run Docker without sudo for the current user (assuming they have sudo access):
sudo usermod -aG docker ${USER}
# Register the `docker` group membership with the current session without changing your primary group
exec sg docker newgrp
For a different user (replace <USER_TO_RUN_DOCKER>):
sudo usermod -aG docker <USER_TO_RUN_DOCKER>
Verify docker group membership with:
groups
3. DNS setup
To host n8n online or on a network, create a dedicated subdomain pointed at your server. Add an A record:
| Record type | Name | Destination |
|---|---|---|
| A | n8n (or your desired subdomain) | <your_server_IP_address> |
4. Create an .env file
Create a project directory and navigate into it:
mkdir n8n-compose
cd n8n-compose
Inside n8n-compose, create an .env file and adjust it to your information:
# DOMAIN_NAME and SUBDOMAIN together determine where n8n will be reachable from
# The top level domain to serve from
DOMAIN_NAME=example.com
# The subdomain to serve from
SUBDOMAIN=n8n
# The above example serves n8n at: https://n8n.example.com
# Optional timezone used by Cron and other scheduling nodes
# New York is the default value if not set
GENERIC_TIMEZONE=Europe/Berlin
# The email address to use for the TLS/SSL certificate creation
SSL_EMAIL=user@example.com
5. Create the local files directory
Inside your project directory, create a local-files directory for sharing files between the n8n instance and the host (for example, using the Read/Write Files from Disk node):
mkdir local-files
The Compose file below can create this directory automatically, but doing it manually ensures the right ownership and permissions.
6. Create the Docker Compose file
Create a compose.yaml file and paste the following:
services:
traefik:
image: "traefik"
restart: always
command:
- "--api.insecure=true"
- "--providers.docker=true"
- "--providers.docker.exposedbydefault=false"
- "--entrypoints.web.address=:80"
- "--entrypoints.web.http.redirections.entryPoint.to=websecure"
- "--entrypoints.web.http.redirections.entrypoint.scheme=https"
- "--entrypoints.websecure.address=:443"
- "--certificatesresolvers.mytlschallenge.acme.tlschallenge=true"
- "--certificatesresolvers.mytlschallenge.acme.email=${SSL_EMAIL}"
- "--certificatesresolvers.mytlschallenge.acme.storage=/letsencrypt/acme.json"
ports:
- "80:80"
- "443:443"
volumes:
- traefik_data:/letsencrypt
- /var/run/docker.sock:/var/run/docker.sock:ro
n8n:
image: docker.n8n.io/n8nio/n8n
restart: always
ports:
- "127.0.0.1:5678:5678"
labels:
- traefik.enable=true
- traefik.http.routers.n8n.rule=Host(`${SUBDOMAIN}.${DOMAIN_NAME}`)
- traefik.http.routers.n8n.tls=true
- traefik.http.routers.n8n.entrypoints=web,websecure
- traefik.http.routers.n8n.tls.certresolver=mytlschallenge
- traefik.http.middlewares.n8n.headers.SSLRedirect=true
- traefik.http.middlewares.n8n.headers.STSSeconds=315360000
- traefik.http.middlewares.n8n.headers.browserXSSFilter=true
- traefik.http.middlewares.n8n.headers.contentTypeNosniff=true
- traefik.http.middlewares.n8n.headers.forceSTSHeader=true
- traefik.http.middlewares.n8n.headers.SSLHost=${DOMAIN_NAME}
- traefik.http.middlewares.n8n.headers.STSIncludeSubdomains=true
- traefik.http.middlewares.n8n.headers.STSPreload=true
- traefik.http.routers.n8n.middlewares=n8n@docker
environment:
- N8N_ENFORCE_SETTINGS_FILE_PERMISSIONS=true
- N8N_HOST=${SUBDOMAIN}.${DOMAIN_NAME}
- N8N_PORT=5678
- N8N_PROTOCOL=https
- NODE_ENV=production
- WEBHOOK_URL=https://${SUBDOMAIN}.${DOMAIN_NAME}/
- GENERIC_TIMEZONE=${GENERIC_TIMEZONE}
- TZ=${GENERIC_TIMEZONE}
volumes:
- n8n_data:/home/node/.n8n
- ./local-files:/files
volumes:
n8n_data:
traefik_data:
This file configures two containers: n8n and traefik (an application proxy that manages TLS/SSL certificates and routing). It creates and mounts two Docker Volumes and mounts the local-files directory:
| Name | Type | Container mount | Description |
|---|---|---|---|
n8n_data | Volume | /home/node/.n8n | Where n8n saves its SQLite database file and encryption key. |
traefik_data | Volume | /letsencrypt | Where traefik saves TLS/SSL certificate data. |
./local-files | Bind | /files | A local directory shared between n8n and the host. In n8n, use the /files path. |
7. Start Docker Compose
sudo docker compose up -d
To stop the containers:
sudo docker compose stop
8. Done
You can now reach n8n using the subdomain + domain combination defined in your .env (in the example, https://n8n.example.com). n8n is only accessible over secure HTTPS, not plain HTTP. If you have trouble reaching your instance, check your server’s firewall settings and your DNS configuration.
6. Deploy to a cloud provider
The sections below cover deploying n8n to cloud providers. The approaches fall into two broad groups: single server with Docker Compose + Caddy (DigitalOcean, Hetzner) and Kubernetes for scaling (AWS, Azure, GKE), plus serverless (Cloud Run), PaaS (Heroku), and local (OpenShift CRC) options.
6.1. DigitalOcean
Source: Deploy to Digital Ocean
This guide uses Caddy (a reverse proxy that automatically creates and manages SSL/TLS certificates) and Docker Compose.
Create a Droplet
- Log in to DigitalOcean.
- Select the project to host the Droplet, or create a new project.
- In your project, select Droplets from the Manage menu.
- Create a new Droplet using the Docker image available on the Marketplace tab.
For most usage levels, a basic shared CPU plan is enough. DigitalOcean lets you choose between SSH key and password-based authentication; SSH keys are considered more secure.
Log in to your Droplet and create a new user
The rest of the guide requires logging in to the Droplet via SSH (see How to Connect to Droplets with SSH). You should create a new user to avoid working as root:
- Log in as root.
- Create a new user:
adduser <username> - Follow the CLI prompts to finish creating the user.
- Grant administrative privileges:
usermod -aG sudo <username>(you can now usesudo). - Set up SSH for the new user: Add Public Key Authentication.
- Log out of the Droplet.
- Log back in via SSH as the new user.
Clone the configuration repository
Clone the files from the n8n-docker-caddy repository into the logged-in user’s home folder:
git clone https://github.com/n8n-io/n8n-docker-caddy.git
cd n8n-docker-caddy
The two folders are caddy_config (Caddy configuration) and local_files (files you upload or add in n8n).
Create Docker volumes (to persist the Caddy cache and n8n data):
sudo docker volume create caddy_data
sudo docker volume create n8n_data
Set up DNS: create an “A” record for the n8n subdomain pointing to the Droplet’s IP. See An Introduction to DNS Terminology, Components, and Concepts.
Open ports (80 for non-secure traffic, 443 for secure):
sudo ufw allow 80
sudo ufw allow 443
Configure n8n: edit the .env file (which contains inline comments to help):
nano .env
See details in Environment variables. The docker-compose.yml file uses the .env variables, so you normally don’t need to change it (nano docker-compose.yml to inspect it).
Configure Caddy: edit the Caddyfile in the caddy_config folder and replace the placeholder domain with yours:
nano caddy_config/Caddyfile
n8n.<domain>.<suffix> {
reverse_proxy n8n:5678 {
flush_interval -1
}
}
Start:
sudo docker compose up -d
Test by opening the subdomain + domain URL in your browser and entering the user name and password defined earlier. To stop: sudo docker compose stop.
Updating (Docker Compose): cd to the directory, then docker compose pull, docker compose down, docker compose up -d.
6.2. Hetzner
Source: Deploy to Hetzner
As with DigitalOcean, this uses Caddy + Docker Compose.
Create a server
- Log in to the Hetzner Cloud Console.
- Select the project, or create a new one with + NEW PROJECT.
- Select + CREATE SERVER on the project.
Since this guide uses Docker, under the Image section select “Docker CE” from the APPS tab. For most usage levels, the CPX11 type is enough. Hetzner lets you choose between SSH and password-based authentication; SSH is more secure (the guide assumes SSH).
Log in to your server via SSH (see Access with SSH/rsync/BorgBackup). You can find the public IP in the server listing of your project.
Install Docker Compose (the Hetzner Docker image doesn’t include it):
apt update && apt -y upgrade
apt install docker-compose-plugin
Clone the configuration repository from n8n-docker-caddy into the root user folder:
git clone https://github.com/n8n-io/n8n-docker-caddy.git
cd n8n-docker-caddy
Folders: caddy_config and local_files.
Create Docker volumes:
docker volume create caddy_data
sudo docker volume create n8n_data
Set up DNS: an “A” record for the subdomain pointing to the server’s IP (see intro to DNS).
Open ports:
sudo ufw allow 80
sudo ufw allow 443
Configure n8n (nano .env; see Environment variables) and configure Caddy (nano caddy_config/Caddyfile):
n8n.<domain>.<suffix> {
reverse_proxy n8n:5678 {
flush_interval -1
}
}
Start: docker compose up -d. Test in your browser. To stop: sudo docker compose stop.
6.3. Heroku
Source: Deploy to Heroku
This guide uses Docker Compose, Heroku’s PostgreSQL service for storage, and a Deploy to Heroku button for a one-click deployment (with minor configuration).
Create the Heroku project from the template — the Deploy to Heroku button:
This opens the Create New App page. Set a name and choose the region.
Configure environment variables: Heroku pre-fills the options from the env section of app.json. You can change any value, but you must change:
- N8N_ENCRYPTION_KEY — used to encrypt user account details before saving them to the database.
- WEBHOOK_URL — should match the application name you create, so webhooks have the correct URL.
Deploy: select Deploy app. After the build, Heroku provides links to Manage App or View. To connect your domain, see the Heroku networking/DNS documentation.
Customize the template: fork the repository and deploy from the fork. By default the Dockerfile pulls the latest image (change the tag on the top line to pin a version). Heroku doesn’t allow EXPOSE in Docker apps — it provides a dynamic PORT variable, and entrypoint.sh handles this; you can then access n8n on port 80 (Docker limitations with Heroku). The heroku.yml defines the addons (PostgreSQL) and the build via the Docker buildpack.
6.4. AWS (EKS)
Source: Deploy to AWS
This guide self-hosts n8n with PostgreSQL as the backend, using Kubernetes to manage the resources and reverse proxy. AWS offers several options (EC2, EKS); this guide uses EKS. Using Kubernetes adds complexity but is the best method for scaling as demand changes.
Prerequisites: a mix of the AWS UI and the eksctl CLI tool. Although not mentioned in the eksctl documentation, you also need to install the AWS CLI and configure its authentication.
Create a cluster:
eksctl create cluster --name n8n --region <your-aws-region>
This can take a while. Once created, eksctl automatically sets the kubectl context to the cluster.
Clone the configuration repository (n8n-hosting):
git clone https://github.com/n8n-io/n8n-hosting.git
cd n8n-hosting/kubernetes
Configure Postgres. For larger-scale deployments, Postgres is more robust than SQLite.
-
Persistent storage volume: the default AWS storage class, gp3, is suitable and is defined in the
postgres-claim0-persistentvolumeclaim.yamlmanifest:spec: storageClassName: gp3 accessModes: - ReadWriteOnce -
Postgres environment variables: the
postgres-secret.yamlfile contains placeholders to replace with your values. Postgres uses a root user (POSTGRES_USER) for administration, but it’s best practice to create a non-root user (POSTGRES_NON_ROOT_USER) for n8n — this improves security and prevents accidental changes. Thepostgres-deployment.yamluses these values.
Configure n8n.
-
File storage volume: while not essential, persistent volumes help maintain uploaded files and persist manual encryption keys between restarts. Created by
n8n-claim0-persistentvolumeclaim.yaml, mounted inn8n-deployment.yaml:volumes: - name: n8n-claim0 persistentVolumeClaim: claimName: n8n-claim0 -
Pod resources: Kubernetes lets you specify minimum resources and limits. The example in
n8n-deployment.yaml:resources: requests: memory: "250Mi" limits: memory: "500Mi"A minimum of 250mb and maximum of 500mb per container, leaving CPU to Kubernetes. As a guide, values for the n8n cloud offerings: Start 320mb RAM / 10 millicore CPU burstable; Pro (10k) 640mb / 20 millicore; Pro (50k) 1280mb / 80 millicore.
-
Optional — environment variables: create an
n8n-secret.yaml(see Environment variables).
Deployments and Services. The n8n-deployment.yaml and postgres-deployment.yaml manifests define the applications (environment variables, image, resource limits, volumes/volumeMounts, scaling and restart policies — the examples define one instance of each pod; adjust as needed). The postgres-service.yaml and n8n-service.yaml manifests expose the services via the Kubernetes load balancer, on ports 5432 and 5678 by default.
Send to the cluster:
kubectl apply -f .
Namespace error. You may see an error about not finding the “n8n” namespace because the resource isn’t ready yet. Run the command again, or apply the namespace first:
kubectl apply -f namespace.yaml.
Set up DNS. Create a DNS record for the subdomain pointing to a static address of the instance. To find the address: in the AWS console, Amazon Elastic Kubernetes Service > Clusters > (cluster name) > Resources tab > Service and networking > Services > select the n8n service and copy the Load balancer URLs value. Use that value suffixed with the n8n port (5678).
Use HTTP. This guide uses HTTP connections. If you click the Load balancer URLs value, EKS takes you to an “HTTPS” URL that results in an error. To fix this, when you open the n8n subdomain, use HTTP.
Delete resources: kubectl delete -f .
6.5. Azure (AKS)
Source: Deploy to Azure
Self-hosting with PostgreSQL and Kubernetes. Prerequisite: the Azure command line tool.
Azure offers Azure Container Instances, Linux VMs, and Azure Kubernetes Service (AKS). This guide uses AKS — more complex, but the best for scaling.
Open AKS: from the Azure portal select Kubernetes services.
Create a cluster: Create > Create a Kubernetes cluster, select your options, then Create.
Set the Kubectl context: open the cluster details page and the Connect button; copy and run the snippets in a terminal to point your local Kubernetes at the new cluster.
Clone the repository (n8n-hosting):
git clone https://github.com/n8n-io/n8n-hosting.git
cd n8n-hosting/kubernetes
Configure Postgres. The default storage class is suitable, defined in postgres-claim0-persistentvolumeclaim.yaml (for specialized requirements, see Azure storage options). The postgres-secret.yaml contains placeholders to replace; the postgres-deployment.yaml uses those values.
Configure n8n. The file volume (n8n-claim0-persistentvolumeclaim.yaml) is required for using nodes that interact with files (like the binary data node) and for persisting manual encryption keys:
volumes:
- name: n8n-claim0
persistentVolumeClaim:
claimName: n8n-claim0
Pod resources (same as the AWS example; see the reference values above) and, optionally, an n8n-secret.yaml (Environment variables).
Deployments and Services as described above; services exposed via load balancer on ports 5432 and 5678.
Send to the cluster:
kubectl apply -f .
(On a namespace error, repeat or apply kubectl apply -f namespace.yaml.)
Set up DNS: create a DNS record for the subdomain pointing to the IP of the n8n service, found under Services & ingresses of the cluster, External IP column, adding port 5678. For a static IP with AKS, see this tutorial.
Delete resources: kubectl delete -f .
6.6. Google Cloud Run
Source: Deploy to Google Cloud Run
Cloud Run is a serverless container runtime. There are two modes: easy mode (fast, in-memory data — demo only) and durable mode (production, with a database and secret manager). For GKE, see the next section. You can also enable OAuth access to Google Workspace services (Gmail, Drive, etc.) as n8n tools.
Before you begin: if you don’t yet have a Google Cloud project, create one first and enable billing (even if the service runs for free, you must have billing enabled to deploy).
Easy mode
The fastest way. Warning: data is in-memory — anytime the service scales to zero or is redeployed, the n8n data is lost. Use durable mode for production.
Open the Cloud Shell Terminal (in the console, type “G” then “S”, or click the terminal icon). If needed, log in and enable the API:
gcloud auth login
gcloud services enable run.googleapis.com
Required: custom health check endpoint. Cloud Run reserves
/healthzfor its own health checks. Since n8n uses this path by default, it conflicts. Set theN8N_ENDPOINT_HEALTHenvironment variable to a custom path (included in the commands below).
Deploy n8n (you can choose another region instead of us-west1):
gcloud run deploy n8n \
--image=n8nio/n8n \
--region=us-west1 \
--allow-unauthenticated \
--port=5678 \
--no-cpu-throttling \
--memory=2Gi \
--set-env-vars="N8N_ENDPOINT_HEALTH=health"
When finished, open the Service URL in another tab. Optionally, to keep the service running as long as possible (manual scale to 1, avoiding autoscaling to 0):
gcloud run deploy n8n \
--image=n8nio/n8n \
--region=us-west1 \
--allow-unauthenticated \
--port=5678 \
--no-cpu-throttling \
--memory=2Gi \
--scaling=1 \
--set-env-vars="N8N_ENDPOINT_HEALTH=health"
This doesn’t fully prevent data loss (for example, on redeploys). For truly persistent data, use durable mode.
Durable mode
A more durable, production-grade deployment with a database for persistence and secret manager for sensitive data. If you prefer Terraform, see this example.
Enable APIs and set variables:
## You may need to login first
gcloud auth login
gcloud services enable run.googleapis.com
gcloud services enable sqladmin.googleapis.com
gcloud services enable secretmanager.googleapis.com
export PROJECT_ID=your-project
export REGION=region-where-you-want-this-deployed
Postgres database (takes a few minutes; change the root-password):
gcloud sql instances create n8n-db \
--database-version=POSTGRES_13 \
--tier=db-f1-micro \
--region=$REGION \
--root-password="change-this-password" \
--storage-size=10GB \
--availability-type=ZONAL \
--no-backup \
--storage-type=HDD
gcloud sql databases create n8n --instance=n8n-db
gcloud sql users create n8n-user \
--instance=n8n-db \
--password="change-this-password"
You can save the n8n-user password to a file for the next step (delete it afterward).
Store sensitive data in Secret Manager (recommended):
gcloud secrets create n8n-db-password \
--data-file=/your/password/file \
--replication-policy="automatic"
Create an encryption key (this example generates a random one):
openssl rand -base64 -out my-encryption-key 42
gcloud secrets create n8n-encryption-key \
--data-file=my-encryption-key \
--replication-policy="automatic"
Then delete the my-encryption-key and password files — the values are stored in Secret Manager.
Service account for Cloud Run (restricted to only what it needs):
gcloud iam service-accounts create n8n-service-account \
--display-name="n8n Service Account"
gcloud secrets add-iam-policy-binding n8n-db-password \
--member="serviceAccount:n8n-service-account@$PROJECT_ID.iam.gserviceaccount.com" \
--role="roles/secretmanager.secretAccessor"
gcloud secrets add-iam-policy-binding n8n-encryption-key \
--member="serviceAccount:n8n-service-account@$PROJECT_ID.iam.gserviceaccount.com" \
--role="roles/secretmanager.secretAccessor"
gcloud projects add-iam-policy-binding $PROJECT_ID \
--member="serviceAccount:n8n-service-account@$PROJECT_ID.iam.gserviceaccount.com" \
--role="roles/cloudsql.client"
Deploy the Cloud Run service:
gcloud run deploy n8n \
--image=n8nio/n8n:latest \
--command="/bin/sh" \
--args="-c,sleep 5;n8n start" \
--region=$REGION \
--allow-unauthenticated \
--port=5678 \
--memory=2Gi \
--no-cpu-throttling \
--set-env-vars="N8N_PORT=5678,N8N_PROTOCOL=https,N8N_ENDPOINT_HEALTH=health,DB_TYPE=postgresdb,DB_POSTGRESDB_DATABASE=n8n,DB_POSTGRESDB_USER=n8n-user,DB_POSTGRESDB_HOST=/cloudsql/$PROJECT_ID:$REGION:n8n-db,DB_POSTGRESDB_PORT=5432,DB_POSTGRESDB_SCHEMA=public,GENERIC_TIMEZONE=UTC,QUEUE_HEALTH_CHECK_ACTIVE=true" \
--set-secrets="DB_POSTGRESDB_PASSWORD=n8n-db-password:latest,N8N_ENCRYPTION_KEY=n8n-encryption-key:latest" \
--add-cloudsql-instances=$PROJECT_ID:$REGION:n8n-db \
--service-account=n8n-service-account@$PROJECT_ID.iam.gserviceaccount.com
When finished, open the Service URL — you should see the n8n login screen.
Troubleshooting: a “Cannot GET /” screen usually means n8n is still starting up; refresh the page.
(Optional) Enable Google Workspace services as n8n tools. Setting up OAuth is recommended. First, enable the APIs you want (Sheets/Docs have their own API, the Drive one isn’t enough):
gcloud services enable gmail.googleapis.com
gcloud services enable drive.googleapis.com
gcloud services enable sheets.googleapis.com
gcloud services enable docs.googleapis.com
gcloud services enable calendar-json.googleapis.com
Re-deploy with the OAuth callback URLs as variables:
export SERVICE_URL="your-n8n-service-URL"
## e.g. https://n8n-12345678.us-west1.run.app
gcloud run services update n8n \
--region=$REGION \
--update-env-vars="N8N_HOST=$(echo $SERVICE_URL | sed 's/https:\/\///'),WEBHOOK_URL=$SERVICE_URL,N8N_EDITOR_BASE_URL=$SERVICE_URL"
Finally, set up OAuth at https://console.cloud.google.com/auth: click “Get Started” (if shown); fill in “App Information”; for “Audience” choose “Internal” (only your Workspace) or “External”; enter “Contact Information”; if “External”, add test users; under “Clients” > “Create client” choose “Web application”, put the n8n service URL in “Authorized JavaScript origins” and /rest/oauth2-credential/callback in “Authorized redirect URIs” (download the client JSON, which contains the secret); under “Data Access” add the scopes (e.g. for Sheets: https://googleapis.com/auth/drive.file and https://googleapis.com/auth/spreadsheets). You can then test by adding a Tool in n8n with the credentials from the JSON.
6.7. Google Kubernetes Engine (GKE)
Source: Deploy to Google Kubernetes
Google Cloud offers Cloud Run, Compute Engine (VMs), and Kubernetes Engine (GKE). This guide uses GKE. For Cloud Run, see the previous section. Most steps use the Google Cloud UI, but you can use the gcloud tool.
Prerequisites:
- The gcloud command line tool
- The gke-gcloud-auth-plugin (install the gcloud CLI first)
Create a project: in the Google Cloud Console, project dropdown > NEW PROJECT, then select it (always confirm you’re in the correct project).
Enable the Kubernetes Engine API: search “Kubernetes” in the top search bar, choose “Kubernetes Engine”, and select ENABLE.
Create a cluster: from the GKE page, Clusters > CREATE. Select the Standard option — n8n does not work with an “Autopilot” cluster. You can keep the defaults except for what you need to change (e.g. location).
Set the Kubectl context: open the cluster details and CONNECT; paste and run the snippet in the gcloud CLI.
Clone the repository (n8n-hosting):
git clone https://github.com/n8n-io/n8n-hosting.git
cd n8n-hosting/kubernetes
Configure Postgres. Running Postgres on GCP requires a specific Kubernetes Storage Class (see this guide); the storage.yaml manifest creates it. You can change the regions under allowedTopologies > matchedLabelExpressions > values (default us-central):
allowedTopologies:
- matchLabelExpressions:
- key: failure-domain.beta.kubernetes.io/zone
values:
- us-central1-b
- us-central1-c
The postgres-secret.yaml contains placeholders to replace; the postgres-deployment.yaml uses those values.
Configure n8n. File volume (required for nodes that interact with files and for persisting manual encryption keys) via n8n-claim0-persistentvolumeclaim.yaml:
volumes:
- name: n8n-claim0
persistentVolumeClaim:
claimName: n8n-claim0
Pod resources (present in n8n-deployment.yaml and postgres-deployment.yaml; same reference values noted in the AWS section) and, optionally, an n8n-secret.yaml (Environment variables).
Deployments and Services as in the other Kubernetes sections; services exposed via load balancer on ports 5432 and 5678.
Send to the cluster:
kubectl apply -f .
(Namespace error: repeat or apply kubectl apply -f namespace.yaml.)
Set up DNS: a DNS record for the subdomain pointing to the IP of the n8n service, found under Services & Ingress of the cluster, Endpoints column. For reserved IPs with GKE, see this tutorial.
Delete resources: kubectl delete -f .
6.8. OpenShift Local (CRC)
Source: Deploy to OpenShift Local (CRC)
This guide deploys n8n on OpenShift Local (CRC), Red Hat’s tool for running a local OpenShift cluster. It mirrors the AWS/EKS deployment but runs entirely on your machine — ideal for testing n8n in an OpenShift environment without cloud costs. You need a machine with significant resources.
OpenShift vs standard Kubernetes concepts (summary): kubectl → oc; Namespace → Project; Ingress/LoadBalancer → Route (built-in); EBS StorageClass (gp3) → CRC’s built-in storage provisioner; RDS PostgreSQL → in-cluster PostgreSQL via Helm (Bitnami); ElastiCache Redis → in-cluster Redis; AWS S3 → in-cluster MinIO; OIDC/IAM and AWS Load Balancer Controller → not needed; ~$135–400/month → free (runs on your machine).
Machine prerequisites: 4+ physical CPU cores with virtualization support; 32+ GB free RAM (CRC reserves 9 GB for the VM); 100 GB free disk; OS Ubuntu (22.04 LTS or newer).
Prepare Ubuntu
Open a terminal (Ctrl+Alt+T). Update the system:
sudo apt update && sudo apt upgrade -y
Check CPU virtualization support (output 0 = disabled, enable VT-x/AMD-V in the BIOS; 1 or more = good):
egrep -c '(vmx|svm)' /proc/cpuinfo
Install KVM and libvirt, and virtiofsd (required to share the filesystem with the VM):
sudo apt install -y qemu-kvm libvirt-daemon-system libvirt-clients bridge-utils
sudo apt install -y virtiofsd
sudo systemctl start libvirtd
sudo systemctl enable libvirtd
sudo systemctl status libvirtd
Add your user to the required groups (you must log out and log back in, or reboot, otherwise CRC fails with “permission denied”):
sudo usermod -aG libvirt $USER
sudo usermod -aG kvm $USER
sudo reboot
After rebooting, verify with groups (should list libvirt and kvm). Install NetworkManager (required to manage DNS for the internal domains *.apps-crc.testing, api.crc.testing):
sudo apt install -y network-manager
sudo systemctl start NetworkManager
sudo systemctl enable NetworkManager
nmcli general status
Install tools
Red Hat account and pull secret: CRC needs a free Red Hat account to pull images. Create a free account; at console.redhat.com/openshift/create/local click Download OpenShift Local, select Linux, and download the .tar.xz to ~/Downloads. On the same page, Copy pull secret and save it to a file.
Install CRC:
cd ~/Downloads
tar xf crc-linux-amd64.tar.xz
sudo mv crc-*-linux-amd64/crc /usr/local/bin/
crc version
Install Helm (installs n8n and supporting services into the cluster):
curl https://raw.githubusercontent.com/helm/helm/main/scripts/get-helm-3 | bash
helm version
Set environment variables (only last for the current terminal session; re-run when opening a new terminal):
export NAMESPACE=n8n-$(date +%Y%m%d)
echo "Namespace:$NAMESPACE"
Start OpenShift Local
Run setup (only once; configures KVM networking, checks requirements, and downloads the bundle ~2.5 GB):
crc setup
CRC defaults to 9 GB of RAM; n8n needs more. Set 14 GB before starting (persists across crc stop/crc start):
crc config set memory 14336
Recommended: save the pull secret to a file and start with it:
nano ~/pull-secret.txt # paste the pull secret, Ctrl+O, Ctrl+X
chmod 600 ~/pull-secret.txt
crc start --pull-secret-file ~/pull-secret.txt
This takes 10–15 minutes. When done, the web console URL and credentials are shown (kubeadmin with a generated password, developer/developer). Save the kubeadmin password (you can retrieve it later with crc console --credentials).
Verify DNS and API (on Ubuntu, CRC configures the resolver automatically):
sudo ss -tlnp | grep 6443
Configure your shell (makes oc available in the terminal):
eval $(crc oc-env)
echo 'eval $(crc oc-env)' >> ~/.bashrc
source ~/.bashrc
oc version
Log in to the cluster (replace the kubeadmin password):
oc login -u kubeadmin -p <your-kubeadmin-password> https://api.crc.testing:6443
oc whoami
Standalone deployment
Standalone mode: n8n as a single pod with SQLite, no external database or Redis. Ideal for exploring n8n locally.
oc new-project $NAMESPACE
Grant the security permission (SCC). OpenShift enforces Security Context Constraints; the chart runs as UID 1000, so you must allow it (use the explicit form — the -z shorthand can silently fail):
oc adm policy add-scc-to-user anyuid \
system:serviceaccount:$NAMESPACE:n8n
oc get rolebindings -n $NAMESPACE
Create the required secret and back up the encryption key immediately (losing it makes all stored credentials permanently unreadable):
oc create secret generic n8n-secrets \
--namespace $NAMESPACE \
--from-literal=N8N_ENCRYPTION_KEY="$(openssl rand -hex 32)" \
--from-literal=N8N_HOST="localhost" \
--from-literal=N8N_PORT="5678" \
--from-literal=N8N_PROTOCOL="http"
oc get secret n8n-secrets -n $NAMESPACE \
-o jsonpath='{.data.N8N_ENCRYPTION_KEY}' | base64 --decode
Create the values file n8n-standalone-values.yaml (nano n8n-standalone-values.yaml):
queueMode:
enabled: false
database:
type: sqlite
useExternal: false
redis:
enabled: false
persistence:
enabled: true
size: 5Gi
secretRefs:
existingSecret: "n8n-secrets"
service:
type: ClusterIP
port: 5678
securityContext:
enabled: true
resources:
main:
requests:
cpu: 100m
memory: 256Mi
limits:
cpu: "1"
memory: 1Gi
config:
timezone: UTC
Deploy. The Helm chart hard codes seccompProfile: RuntimeDefault, which OpenShift 4.14+ rejects even with anyuid. The fix is to pull the chart locally, remove those two lines, and install from the patched copy:
helm pull oci://ghcr.io/n8n-io/n8n-helm-chart/n8n --version 1.0.3 --untar
sed -i '/seccompProfile:/d; /type: RuntimeDefault/d' ~/n8n/templates/deployment-main.yaml
grep -n "seccomp\|RuntimeDefault" ~/n8n/templates/deployment-main.yaml # should return nothing
helm install n8n ~/n8n/ \
--namespace $NAMESPACE \
--values n8n-standalone-values.yaml \
--wait \
--timeout 10m
Access via port forward (simpler than Routes for local access):
oc port-forward service/n8n-main --namespace $NAMESPACE 5678:5678
Keep it running and open http://localhost:5678 (n8n prompts you to create the owner account). Ctrl+C to stop the tunnel. Check status with oc get pods -n $NAMESPACE.
Multi-instance queue mode
Runs multiple n8n pods with a shared database, message queue, and object storage. Requires an n8n Enterprise license. Instead of AWS managed services, it uses in-cluster equivalents: RDS PostgreSQL → PostgreSQL (Bitnami), ElastiCache Redis → Redis (Bitnami), S3 → MinIO (Bitnami).
Install in-cluster services:
oc new-project $NAMESPACE
helm repo add bitnami https://charts.bitnami.com/bitnami
helm repo update
PostgreSQL (replace the password; the adaptSecurityContext=auto flag lets OpenShift assign the correct UID):
helm install postgresql bitnami/postgresql \
--namespace $NAMESPACE \
--set auth.username=n8n \
--set auth.password='YourStrongPassword123' \
--set auth.database=n8n_enterprise \
--set global.compatibility.openshift.adaptSecurityContext=auto \
--wait
Endpoint: postgresql.YOUR_NAMESPACE.svc.cluster.local (replace YOUR_NAMESPACE with the actual value). Redis:
helm install redis bitnami/redis \
--namespace $NAMESPACE \
--set auth.enabled=false \
--set architecture=standalone \
--set global.compatibility.openshift.adaptSecurityContext=auto \
--wait
Endpoint: redis-master.$NAMESPACE.svc.cluster.local. MinIO (S3-compatible):
helm install minio bitnami/minio \
--namespace $NAMESPACE \
--set auth.rootUser=minioadmin \
--set auth.rootPassword='MinioStrongPassword123' \
--set global.compatibility.openshift.adaptSecurityContext=auto \
--wait
Endpoint: http://minio:9000. Create the bucket by opening the MinIO console (oc port-forward svc/minio 9001:9001 -n $NAMESPACE, then http://localhost:9001, log in with minioadmin/password): Buckets > Create Bucket named n8n-data. Ctrl+C to stop the port-forward.
Deploy n8n. Grant SCC to the n8n-enterprise service account:
oc adm policy add-scc-to-user anyuid \
system:serviceaccount:$NAMESPACE:n8n-enterprise
Create the secrets (back up the encryption key; the passwords must match those set above):
oc create secret generic n8n-enterprise-secrets \
--namespace $NAMESPACE \
--from-literal=N8N_ENCRYPTION_KEY="$(openssl rand -hex 32)" \
--from-literal=N8N_HOST="localhost" \
--from-literal=N8N_PORT="5678" \
--from-literal=N8N_PROTOCOL="http"
oc create secret generic n8n-enterprise-db-secret \
--namespace $NAMESPACE \
--from-literal=password='YourStrongPassword123'
oc create secret generic n8n-minio-secret \
--namespace $NAMESPACE \
--from-literal=root-password='MinioStrongPassword123'
Create n8n-multimain-ocp-values.yaml (replace the 3 values marked # <-- REPLACE: the license key and the two YOUR_NAMESPACE):
license:
enabled: true
activationKey: "your-enterprise-license-key-here" # <-- REPLACE
multiMain:
enabled: true
replicas: 2
queueMode:
enabled: true
workerReplicaCount: 2
workerConcurrency: 5
webhookProcessor:
enabled: true
replicaCount: 1
disableProductionWebhooksOnMainProcess: true
database:
type: postgresdb
useExternal: true
host: "postgresql.YOUR_NAMESPACE.svc.cluster.local" # <-- REPLACE YOUR_NAMESPACE
port: 5432
database: n8n_enterprise
schema: "public"
user: n8n
passwordSecret:
name: "n8n-enterprise-db-secret"
key: "password"
redis:
enabled: true
useExternal: true
host: "redis-master.YOUR_NAMESPACE.svc.cluster.local" # <-- REPLACE YOUR_NAMESPACE
port: 6379
tls: false
s3:
enabled: true
bucket:
name: "n8n-data"
region: "us-east-1"
host: "http://minio:9000"
auth:
autoDetect: false
accessKeyId: "minioadmin"
secretAccessKeySecret:
name: "n8n-minio-secret"
key: "root-password"
storage:
mode: "s3"
availableModes: "filesystem,s3"
forcePathStyle: true
serviceAccount:
create: true
name: n8n
Replace the YOUR_NAMESPACE placeholders automatically and verify:
echo $NAMESPACE
sed -i "s/YOUR_NAMESPACE/$NAMESPACE/g" n8n-multimain-ocp-values.yaml
grep "svc.cluster.local" n8n-multimain-ocp-values.yaml
Pull and patch the chart (if you haven’t already) and install:
helm pull oci://ghcr.io/n8n-io/n8n-helm-chart/n8n --version 1.0.3 --untar
sed -i '/seccompProfile:/d; /type: RuntimeDefault/d' ~/n8n/templates/deployment-main.yaml
grep -n "seccomp\|RuntimeDefault" ~/n8n/templates/deployment-main.yaml
helm install n8n ~/n8n/ \
--namespace $NAMESPACE \
--values n8n-multimain-ocp-values.yaml \
--wait \
--timeout 15m
Create a Route for external access (equivalent to Ingress/LoadBalancer, no extra controller) and get the URL:
oc expose svc/n8n-main -n $NAMESPACE
export ROUTE=$(oc get route n8n-main -n $NAMESPACE -o jsonpath='{.spec.host}')
echo "n8n URL: http://$ROUTE"
Update the host secret with the Route hostname and restart the pods:
ENCRYPTION_KEY=$(oc get secret n8n-enterprise-secrets -n $NAMESPACE \
-o jsonpath='{.data.N8N_ENCRYPTION_KEY}' | base64 --decode)
oc create secret generic n8n-enterprise-secrets \
--namespace $NAMESPACE \
--from-literal=N8N_ENCRYPTION_KEY="$ENCRYPTION_KEY" \
--from-literal=N8N_HOST="$ROUTE" \
--from-literal=N8N_PORT="5678" \
--from-literal=N8N_PROTOCOL="http" \
--dry-run=client -o yaml | oc apply -f -
oc rollout restart deployment -n $NAMESPACE
oc rollout status deployment/n8n-main -n $NAMESPACE
Verify all pods are Running with oc get pods -n $NAMESPACE and open the URL in your browser.
Updating, stopping/resuming, and quick reference
Updating (pull and re-patch the new chart version, then helm upgrade):
rm -rf ~/n8n/
helm pull oci://ghcr.io/n8n-io/n8n-helm-chart/n8n --version <new-version> --untar
sed -i '/seccompProfile:/d; /type: RuntimeDefault/d' ~/n8n/templates/deployment-main.yaml
# Standalone
helm upgrade n8n ~/n8n/ --namespace $NAMESPACE --values n8n-standalone-values.yaml
# Multi-instance
helm upgrade n8n ~/n8n/ --namespace $NAMESPACE --values n8n-multimain-ocp-values.yaml
Stop/resume CRC (no need to delete between sessions):
crc stop
crc start
After restarting, re-export the variables and log back in:
eval $(crc oc-env)
export NAMESPACE=n8n-YYYYMMDD # use your original date
oc login -u kubeadmin -p <password> https://api.crc.testing:6443
The original documentation also includes an extensive Troubleshooting section (SCC/seccomp errors, pods stuck in Pending due to memory, DNS for *.apps-crc.testing, EACCES writing to /home/node/.n8n/, viewing logs with oc logs ...) and a table of items to save (kubeadmin password, encryption key, values files, MinIO and PostgreSQL passwords). See the source for the full detail.
7. Which option to choose
| Scenario | Recommended option |
|---|---|
| Try it quickly on your local machine | npx / npm |
| Simple, isolated self-hosting | Docker |
| Single production server with automatic HTTPS | Docker Compose, DigitalOcean, or Hetzner |
| Managed one-click deploy | Heroku |
| Serverless / quick demo on Google Cloud | Cloud Run — easy mode |
| Scalable production with Kubernetes | AWS/EKS, Azure/AKS, or GKE |
| Test OpenShift without cloud costs | OpenShift Local (CRC) |
In any case, keep the cross-cutting constants in mind: persist the .n8n directory (the encryption key!), prefer PostgreSQL at scale, and use Kubernetes when you need to scale as demand changes. And, once more: self-hosting is recommended for expert users — if you’d rather not manage infrastructure, there’s n8n Cloud.
8. Sources
All the instructions above derive from the official n8n documentation: