TutorialsJoint

4 easy ways to install n8n

August 14, 2025

In this tutorial you will learn 4 ways to install and run n8n. You can choose what suites you.

  1. Install and run n8n – fastest way.
  2. Install and run n8n on Docker.
  3. Install and run n8n and PostgreSQL on Docker.
  4. Install and run n8n and PostgreSQL without Docker.

There are some prerequisites and Assumptions.

If you don’t know how to install certbot, Nginx, docker please go through This tutorial here to know more about it and prepare your server.

Install and run n8n

To run n8n directly without anything else we need node.js. If you don’t have node js you can follow the steps below and install node.

Install node.js

Installing node.js really easy, you just need to visit nodejs.org and go to download page, from there you will find multiple options to choose the version and method how you want to install. Becuase i am using ubuntu 24.04 i’ll choose linux and latest LTS version of the node, then copy paste the command on my terminal to install node.

Terminal window
# Download and install nvm:
curl -o- https://raw.githubusercontent.com/nvm-sh/nvm/v0.40.3/install.sh | bash
# in lieu of restarting the shell
\. "$HOME/.nvm/nvm.sh"
# Download and install Node.js:
nvm install 22
# Verify the Node.js version:
node -v # Should print "v22.18.0".
nvm current # Should print "v22.18.0".
# Verify npm version:
npm -v # Should print "10.9.3".

It’ll install the node and required dependencies for you the final output should show you the version of the node nvm and npm.

#######
Computing checksum with sha256sum
Checksums matched!
Now using node v22.18.0 (npm v10.9.3)
Creating default alias: default -> 22 (-> v22.18.0)
v22.18.0
v22.18.0
10.9.3

Once node is installed you can proceed to install n8n.

Install and run n8n

n8n is just like another node package which you can install and run locally or globally. it’s ideal to install n8n globally. for that you just need to run following command.

npm install -g n8n

Once the installation is done, you can simply run n8n by running command n8n.

n8n
Finished migration AddProjectDescriptionColumn1747824239000
Starting migration AddLastActiveAtColumnToUser1750252139166
Finished migration AddLastActiveAtColumnToUser1750252139166
Starting migration AddInputsOutputsToTestCaseExecution1752669793000
Finished migration AddInputsOutputsToTestCaseExecution1752669793000
There is a deprecation related to your environment variables. Please take the recommended actions to update your configuration:
- N8N_RUNNERS_ENABLED -> Running n8n without task runners is deprecated. Task runners will be turned on by default in a future version. Please set `N8N_RUNNERS_ENABLED=true` to enable task runners now and avoid potential issues in the future. Learn more: https://docs.n8n.io/hosting/configuration/task-runners/
[license SDK] Skipping renewal on init: license cert is not initialized
Version: 1.106.3
Editor is now accessible via:
http://localhost:5678
Press "o" to open in Browser.

By default it uses SQLite as databsae, it’s good for testing but for better usages it’s good to use it with Postgres as database. By default it runs on port 5678 on localhost, That is why you need reverse proxy to map it to domain. If your reverse proxy is ready you can access n8n directly on browser.

The proxy config looks like this:

cat /etc/nginx/sites-enabled/demodev.app
server {
server_name demodev.app;
location / {
proxy_pass http://localhost:5678;
proxy_http_version 1.1;
# WebSocket support
proxy_set_header Upgrade $http_upgrade;
proxy_set_header Connection "upgrade";
# Forward headers
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header X-Forwarded-Proto $scheme;
}
listen 443 ssl; # managed by Certbot
ssl_certificate /etc/letsencrypt/live/demodev.app/fullchain.pem; # managed by Certbot
ssl_certificate_key /etc/letsencrypt/live/demodev.app/privkey.pem; # managed by Certbot
include /etc/letsencrypt/options-ssl-nginx.conf; # managed by Certbot
ssl_dhparam /etc/letsencrypt/ssl-dhparams.pem; # managed by Certbot
}
server {
if ($host = demodev.app) {
return 301 https://$host$request_uri;
} # managed by Certbot
listen 80;
server_name demodev.app;
return 404; # managed by Certbot
}

You will need to enter your email, firstname, lastname, password when you open browser for first time and then you get in. the default layout looks like this:

n8n UI

Everything looks fine until this point but there is an issue, which still needs handling. to confirm that you need to create new workflow from top right, orange button.

Empty workflow layout of n8n

Empty workflow layout

Add a webhook node.

Add webhook node

Add webhook node as starter.

Double click the node to open it’s details and check if url matches your domain or still shows localhost:5678. If it still shows localhost, we need to do little more configuration. However if you just make the call by replaceing localhost:4678 by your domain for example https://demodev.app/webhook/abc..xyz it will still work fine.

n8n webhook

n8n webhook node detailed view

If you want to actually see the correct URL you just need to put some environment variables and test again.

nano ~/.n8n/.env

Put these parameters in the .env file.

N8N_PROTOCOL=https
N8N_HOST=demodev.app
N8N_PORT=5678
WEBHOOK_URL=https://demodev.app

Once done save the file and then run n8n again to test. you should now see correct URL.

fixed webhook URL

Fixed webhook URL in n8n

We still have one more issue to solve, currently if you run n8n command the terminal is occupied and you see the logs there, one way to free up is using pm2 . let’s quickly see this as well.

first let’s install pm2 globally.

npm install -g pm2

then just start n8n with pm2

pm2 start "n8n" --name n8n
-------------
__/\\\\\\\\\\\\\____/\\\\____________/\\\\____/\\\\\\\\\_____
_\/\\\/////////\\\_\/\\\\\\________/\\\\\\__/\\\///////\\\___
_\/\\\_______\/\\\_\/\\\//\\\____/\\\//\\\_\///______\//\\\__
_\/\\\\\\\\\\\\\/__\/\\\\///\\\/\\\/_\/\\\___________/\\\/___
_\/\\\/////////____\/\\\__\///\\\/___\/\\\________/\\\//_____
_\/\\\_____________\/\\\____\///_____\/\\\_____/\\\//________
_\/\\\_____________\/\\\_____________\/\\\___/\\\/___________
_\/\\\_____________\/\\\_____________\/\\\__/\\\\\\\\\\\\\\\_
_\///______________\///______________\///__\///////////////__
Runtime Edition
PM2 is a Production Process Manager for Node.js applications
with a built-in Load Balancer.
Start and Daemonize any application:
$ pm2 start app.js
Load Balance 4 instances of api.js:
$ pm2 start api.js -i 4
Monitor in production:
$ pm2 monitor
Make pm2 auto-boot at server restart:
$ pm2 startup
To go further checkout:
http://pm2.io/
-------------
[PM2] Spawning PM2 daemon with pm2_home=/home/ubuntu/.pm2
[PM2] PM2 Successfully daemonized
[PM2] Starting /home/ubuntu/.nvm/versions/node/v22.18.0/bin/n8n in fork_mode (1 instance)
[PM2] Done.
┌────┬────────┬─────────────┬─────────┬─────────┬──────────┬────────┬──────┬───────────┬──────────┬──────────┬──────────┬──────────┐
│ id │ name │ namespace │ version │ mode │ pid │ uptime │ ↺ │ status │ cpu │ mem │ user │ watching │
├────┼────────┼─────────────┼─────────┼─────────┼──────────┼────────┼──────┼───────────┼──────────┼──────────┼──────────┼──────────┤
│ 0 │ n8n │ default │ 0.40.3 │ fork │ 5242 │ 0s │ 0 │ online │ 0% │ 33.0mb │ ubuntu │ disabled │

now you should test and see same results.

Okay well this is fine but if you are running big operations and as the size of the database grows SQLite will not be sufficient, so you should move to use postgres instead. but before doing that let’s see how easy it is to run n8n with docker.

Actually if you want to use docker in first place you would not even need to install node.js on the system only docker is fine.

Install and run n8n on Docker

As we mentioned in prerequisite that docker is required, at this stage i expect you have docker installed and you just want to run n8n there, easiest way is using docker compose.

let’s make a project folder to keep the .env file and docker-compose.yml file.

mkdir -p ~/n8n-docker && cd ~/n8n-docker
nano .env

put the same .env in there as well.

N8N_PROTOCOL=https
N8N_HOST=demodev.app
N8N_PORT=5678
WEBHOOK_URL=https://demodev.app
N8N_EDITOR_BASE_URL=https://demodev.app

then create the docker compose file.

nano docker-compose.yml
version: "3.8"
services:
n8n:
image: n8nio/n8n:latest
container_name: n8n
restart: unless-stopped
ports:
- "5678:5678" # Nginx will proxy to this
env_file:
- ./.env
environment:
- N8N_LISTEN_ADDRESS=0.0.0.0
volumes:
- n8n_data:/home/ubuntu/n8n-docker/data/ # persistent config & workflows
volumes:
n8n_data:

Just make sure the path you provide in the volumes actually exists.

yes that’s it, just do docker-compose up now.

docker compose up -d
WARN[0000] /home/ubuntu/n8n-docker/docker-compose.yml: the attribute `version` is obsolete, it will be ignored, please remove it to avoid potential confusion
[+] Running 14/14
✔ n8n Pulled 47.8s
✔ fe07684b16b8 Pull complete 1.0s
✔ 49b72e7e39fe Pull complete 4.0s
✔ c1ab3b052759 Pull complete 4.1s
✔ 845179b1d451 Pull complete 4.2s
✔ ad03744990d9 Pull complete 6.1s
✔ 4f4fb700ef54 Pull complete 6.1s
✔ 82fe61b89c46 Pull complete 42.5s
✔ 8bbefc0a3d20 Pull complete 42.7s
✔ 61fa1f23038c Pull complete 42.7s
✔ e0b8394f15f7 Pull complete 42.7s
✔ 43ef2e487a4a Pull complete 42.9s
✔ 6e7f4382f6c9 Pull complete 43.9s
✔ 554527a3fe56 Pull complete 44.4s
[+] Running 3/3
✔ Network n8n-docker_default Created 0.1s
✔ Volume "n8n-docker_n8n_data" Created 0.0s
✔ Container n8n Started 0.6s
$ docker ps
CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES
f9063badf0a8 n8nio/n8n:latest "tini -- /docker-ent…" 11 seconds ago Up 11 seconds 0.0.0.0:5678->5678/tcp, [::]:5678->5678/tcp n8n

The proxy is already in place so let’s just reload the browser and test.

n8n first login

Create first account in n8n

Everything should work as expected.

Again as we discussed SQLite will be insufficient in long run so let’s just give out n8n a Postgres. to do that we need to update docker compose to include postgres.

Run n8n with PostgreSQL

I’ll start by stopping docker compose

docker compose down
WARN[0000] /home/ubuntu/n8n-docker/docker-compose.yml: the attribute `version` is obsolete, it will be ignored, please remove it to avoid potential confusion
[+] Running 2/2
✔ Container n8n Removed 0.3s
✔ Network n8n-docker_default Removed 0.1s
:~/n8n-docker$ docker ps
CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES
:~/n8n-docker$ docker ps -a
CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES
be3bb46927d1 hello-world "/hello" About an hour ago Exited (0) About an hour ago unruffled_rosalind
nano .env
N8N_PROTOCOL=https
N8N_HOST=demodev.app
N8N_PORT=5678
WEBHOOK_URL=https://demodev.app
N8N_EDITOR_BASE_URL=https://demodev.app
#This part is for n8n
DB_TYPE=postgresdb
DB_POSTGRESDB_HOST=postgres
DB_POSTGRESDB_PORT=5432
DB_POSTGRESDB_DATABASE=n8n_db
DB_POSTGRESDB_USER=n8n_user
DB_POSTGRESDB_PASSWORD=someSecuredPasswordHere
#this part is for postgres
POSTGRES_DB=n8n_db
POSTGRES_USER=n8n_user
POSTGRES_PASSWORD=someSecuredPasswordHere

now let’s update docker compose as well, again make sure the volume mapping path actually exists.

nano docker-compose.yml
version: "3.8"
services:
postgres:
image: postgres:15
container_name: n8n-postgres
restart: unless-stopped
environment:
- POSTGRES_DB=${POSTGRES_DB}
- POSTGRES_USER=${POSTGRES_USER}
- POSTGRES_PASSWORD=${POSTGRES_PASSWORD}
volumes:
- postgres_data:/home/ubuntu/n8n-docker/data
healthcheck:
test: ["CMD-SHELL", "pg_isready -U ${POSTGRES_USER} -d ${POSTGRES_DB}"]
interval: 10s
timeout: 5s
retries: 10
n8n:
image: n8nio/n8n:latest
container_name: n8n
restart: unless-stopped
depends_on:
postgres:
condition: service_healthy
ports:
- "5678:5678"
env_file:
- ./.env
environment:
- N8N_LISTEN_ADDRESS=0.0.0.0
volumes:
- n8n_data:/home/ubuntu/n8n-docker/data
volumes:
n8n_data:
postgres_data:
docker compose up -d
WARN[0000] /home/ubuntu/n8n-docker/docker-compose.yml: the attribute `version` is obsolete, it will be ignored, please remove it to avoid potential confusion
[+] Running 15/15
✔ postgres Pulled 12.1s
✔ 396b1da7636e Pull complete 2.7s
✔ a03503af0785 Pull complete 2.7s
✔ 9f525d1104ea Pull complete 3.0s
✔ fd87013ac660 Pull complete 3.1s
✔ f8c1b3205ec9 Pull complete 3.7s
✔ a5375d1f2b9e Pull complete 3.9s
✔ f59d823fdd89 Pull complete 3.9s
✔ f2a6712d4122 Pull complete 4.0s
✔ 254103873ab5 Pull complete 8.6s
✔ 289e360b57ea Pull complete 8.6s
✔ 8d0b35f2f6ae Pull complete 8.7s
✔ 36feeb56fd36 Pull complete 8.7s
✔ 8ed078864b8f Pull complete 8.8s
✔ bd45fc4f0bbd Pull complete 8.8s
[+] Running 4/4
✔ Network n8n-docker_default Created 0.1s
✔ Volume "n8n-docker_postgres_data" Created 0.0s
✔ Container n8n-postgres Healthy 11.0s
✔ Container n8n Started 11.0s
:~/n8n-docker$ docker ps
CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES
4ea05f689323 n8nio/n8n:latest "tini -- /docker-ent…" 26 seconds ago Up 15 seconds 0.0.0.0:5678->5678/tcp, [::]:5678->5678/tcp n8n
540e5c084592 postgres:15 "docker-entrypoint.s…" 26 seconds ago Up 25 seconds (healthy) 5432/tcp n8n-postgres

Now test again and it should work normally like it did before.

Well for me that went really well and it’s working without issues. Hope the same goes for you if you have issues you can comment in the youtube video added at the bottom.

Now, if you don’t like docker and want to run n8n without docker we can do that as well by following the steps provided in next section.

Before that i’ll down the docker.

docker compose down
WARN[0000] /home/ubuntu/n8n-docker/docker-compose.yml: the attribute `version` is obsolete, it will be ignored, please remove it to avoid potential confusion
[+] Running 3/3
✔ Container n8n Removed 0.4s
✔ Container n8n-postgres Removed 0.2s
✔ Network n8n-docker_default Removed 0.1s
:~/n8n-docker$ docker ps
CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES
:~/n8n-docker$ docker ps -a
CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES
be3bb46927d1 hello-world "/hello" About an hour ago Exited (0) About an hour ago unruffled_rosalind

Run n8n with Postgres on host without Docker

So we need postgres on the host, so install it, create database, create user and password and grant all privileges on the database for that user.

Terminal window
sudo apt update
sudo apt install -y postgresql postgresql-contrib
sudo systemctl enable --now postgresql

This will ensure postgres is installed and enabled. Now let’s create the database and user.

Terminal window
:~/n8n-docker$ sudo -u postgres psql
psql (16.9 (Ubuntu 16.9-0ubuntu0.24.04.1))
Type "help" for help.
postgres=# CREATE ROLE n8n_user LOGIN PASSWORD 'someStrongPasswordHere';
CREATE ROLE
postgres=# CREATE DATABASE n8n_db OWNER n8n_user;
CREATE DATABASE
postgres=# \c n8n_db
You are now connected to database "n8n_db" as user "postgres".
n8n_db=# ALTER SCHEMA public OWNER TO n8n_user;
ALTER SCHEMA
n8n_db=# GRANT ALL ON SCHEMA public TO n8n_user;
GRANT
n8n_db=# \q

I’ll create a new directory and create ecosystem.config.js file there. The env can be put in this file itself.

mkdir n8n && cd n8n
nano ecosystem.config.js

The ecosystem.config.js file is for pm2, installation of pm2 i have shown already in this article above.

module.exports = {
apps: [
{
name: 'n8n',
script: 'n8n',
env: {
// Database configuration
DB_TYPE: 'postgresdb',
DB_POSTGRESDB_HOST: '127.0.0.1',
DB_POSTGRESDB_PORT: '5432',
DB_POSTGRESDB_DATABASE: 'n8n_db',
DB_POSTGRESDB_USER: 'n8n_user',
DB_POSTGRESDB_PASSWORD: 'someStrongPasswordHere',
// Webhook / URL config
N8N_PROTOCOL: 'https',
N8N_HOST: 'demodev.app',
WEBHOOK_URL: 'https://demodev.app',
N8N_EDITOR_BASE_URL: 'https://demodev.app',
// Internal listening
N8N_LISTEN_ADDRESS: '127.0.0.1',
N8N_PORT: '5678'
},
},
],
};
Terminal window
pm2 start ecosystem.config.js
[PM2][WARN] Applications n8n not running, starting...
[PM2] App [n8n] launched (1 instances)
┌────┬────────┬─────────────┬─────────┬─────────┬──────────┬────────┬──────┬───────────┬──────────┬──────────┬──────────┬──────────┐
id name namespace version mode pid uptime status cpu mem user watching
├────┼────────┼─────────────┼─────────┼─────────┼──────────┼────────┼──────┼───────────┼──────────┼──────────┼──────────┼──────────┤
0 n8n default 0.40.3 fork 10905 0s 0 online 0% 24.6mb ubuntu disabled
└────┴────────┴─────────────┴─────────┴─────────┴──────────┴────────┴──────┴───────────┴──────────┴──────────┴──────────┴──────────┘
:~/n8n$ pm2 status
┌────┬────────┬─────────────┬─────────┬─────────┬──────────┬────────┬──────┬───────────┬──────────┬──────────┬──────────┬──────────┐
id name namespace version mode pid uptime status cpu mem user watching
├────┼────────┼─────────────┼─────────┼─────────┼──────────┼────────┼──────┼───────────┼──────────┼──────────┼──────────┼──────────┤
0 n8n default 0.40.3 fork 10905 11s 0 online 0% 267.5mb ubuntu disabled
└────┴────────┴─────────────┴─────────┴─────────┴──────────┴────────┴──────┴───────────┴──────────┴──────────┴──────────┴──────────┘
:~/n8n$ pm2 logs n8n
[TAILING] Tailing last 15 lines for [n8n] process (change the value with --lines option)
/home/ubuntu/.pm2/logs/n8n-error.log last 15 lines:
0|n8n | (node:5242) [DEP0060] DeprecationWarning: The `util._extend` API is deprecated. Please use Object.assign() instead.
0|n8n | (Use `node --trace-deprecation ...` to show where the warning was created)
/home/ubuntu/.pm2/logs/n8n-out.log last 15 lines:
0|n8n | Starting migration AddProjectDescriptionColumn1747824239000
0|n8n | Finished migration AddProjectDescriptionColumn1747824239000
0|n8n | Starting migration AddLastActiveAtColumnToUser1750252139166
0|n8n | Finished migration AddLastActiveAtColumnToUser1750252139166
0|n8n | Starting migration AddInputsOutputsToTestCaseExecution1752669793000
0|n8n | Finished migration AddInputsOutputsToTestCaseExecution1752669793000
0|n8n |
0|n8n | There is a deprecation related to your environment variables. Please take the recommended actions to update your configuration:
0|n8n | - N8N_RUNNERS_ENABLED -> Running n8n without task runners is deprecated. Task runners will be turned on by default in a future version. Please set `N8N_RUNNERS_ENABLED=true` to enable task runners now and avoid potential issues in the future. Learn more: https://docs.n8n.io/hosting/configuration/task-runners/
0|n8n |
0|n8n | [license SDK] Skipping renewal on init: license cert is not initialized
0|n8n | Version: 1.106.3
0|n8n |
0|n8n | Editor is now accessible via:
0|n8n | https://demodev.app

Because we have been using same nginx config for reverse proxy it should work as it is, open the URL and test. It should work normally as expected.

To enable the autostart of pm2 on reboot just save it and set it for startup.

pm2 save
pm2 startup

n8n has great potential and with it’s AI nodes it becomes a great and powerful tool for automation. You can subscribe to my youtube channel for video tutorials. The Video version of this tutorial is given below.

If you have any issues you can reach out by commenting on the youtube video: