# How to install N8N

Sign up for DigitalOcean using my link and get $200 for your projects, valid for 60 days! Click here: [GET $200](https://m.do.co/c/b7582264f56f)

This tutorial is available as a video on YouTube: [https://www.youtube.com/watch?v=KZh9Wodsa40](https://www.youtube.com/watch?v=KZh9Wodsa40)

## Updating the Operating System

**Step 1** - Update the operating system.

```plaintext
sudo apt update && sudo apt upgrade -y
```

## Installing Node and NPM

On the official Node.js website, you can download the latest binaries as shown in the figure below. As of this writing, the LTS (long time support) version is **22.22.0. We'll use it in this tutorial.**

**Step 2** - Use the command below in your terminal to download the binaries.

```plaintext
curl -o node.tar.xz https://nodejs.org/dist/v22.22.0/node-v22.22.0-linux-x64.tar.xz
```

**Step 3** - Use the command below to extract the package and enter the folder.

```plaintext
tar -xf node.tar.xz && cd node-v22.22.0-linux-x64
```

**Step 4** - With the command below, we'll copy the files to make them available globally.

```plaintext
sudo cp -R * /usr/local/
```

Done! After this, Node should be available for use. Run the commands below to verify.

```plaintext
node -v && npm -v && cd ..
```

If everything is correct, you should see something like shown in the figure.

![](https://cdn.hashnode.com/res/hashnode/image/upload/v1743533596855/4f82b112-2e6d-482d-b538-2dfd5244b576.png?auto=compress,format&format=webp align="center")

## PostgreSQL Installation

**Step 5** - Install PostgreSQL

```plaintext
sudo apt install postgresql postgresql-contrib -y
```

**Step 6** - Check if the service is active:

```plaintext
sudo systemctl status postgresql
```

**Step 7** - Switch to the administrative `postgres` user:

```plaintext
sudo -i -u postgres
```

**Step 8** - Open the PostgreSQL prompt:

```plaintext
psql
```

**Step 9** - Now execute the SQL commands:

⚠️ Don't forget to replace with your own secure password.

```plaintext
-- Create a new user
CREATE ROLE n8n_user WITH LOGIN PASSWORD 'put-your-super-strong-password-here';

-- Create database
CREATE DATABASE n8n_db OWNER n8n_user;

-- Grant permissions to the user on the database
GRANT ALL PRIVILEGES ON DATABASE n8n_db TO n8n_user;
GRANT ALL ON SCHEMA public TO n8n_user;
ALTER SCHEMA public OWNER TO n8n_user;
```

**Step 10** - Now disconnect from the database

```bash
\q
```

**Step 11** - Disconnect from the `postgres` user

```bash
exit
```

## N8N Installation

**Step 12** - Install N8N

```bash
npm install n8n -g
```

**Step 13** - Create a specific user for n8n:

```bash
sudo adduser --system --group --home /home/n8n n8n
```

**Step 14** - Configure the directory where n8n will store files and credentials.

```bash
sudo mkdir -p /home/n8n/.n8n
sudo chown -R n8n:n8n /home/n8n/.n8n
```

**Step 15** - Generate a new encryption key with the command below, we'll use it next.

```bash
openssl rand -base64 48
```

**Step 16** - Create a `.env` file for the service (centralizes configs and makes it easy to maintain):

```bash
sudo nano /home/n8n/.n8n/.env
```

**Step 17** - Copy the content below into the `.env` file you just created.

```bash
# ===== Core =====
N8N_ENFORCE_SETTINGS_FILE_PERMISSIONS=true
N8N_USER_FOLDER=/home/n8n/.n8n
N8N_HOST=YOUR-SERVER-IP
N8N_PORT=5678
# If exposing publicly, define BASE_URL (https://your-domain):
# N8N_EDITOR_BASE_URL=https://your-domain
# WEBHOOK_URL=https://your-domain/
N8N_RUNNERS_ENABLED=true
N8N_SECURE_COOKIE=false
# ===== Database (Postgres) =====
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=put-your-super-strong-password-here
# Pool (optional, improves stability)
DB_POSTGRESDB_POOL_SIZE=10

# ===== Security (adjust according to your scenario) =====
# Basic Auth for editor
#N8N_BASIC_AUTH_ACTIVE=true
#N8N_BASIC_AUTH_USER=admin
#N8N_BASIC_AUTH_PASSWORD=another-super-strong-password

# Encryption key for credentials
N8N_ENCRYPTION_KEY=PUT-HERE-THE-ENCRYPTION-KEY-YOU-GENERATED

# ===== Queues (optional) - if using queues with Redis =====
# QUEUE_BULL_REDIS_HOST=127.0.0.1
# QUEUE_BULL_REDIS_PORT=6379
```

**Step 18** - Apply restrictive permissions:

```bash
sudo chmod 600 /home/n8n/.n8n/.env
```

**Step 19** - Create the service file to run n8n with `systemd`.  
This ensures that n8n is started automatically with the server and is monitored.

```bash
sudo nano /etc/systemd/system/n8n.service
```

**Step 20** - Copy the content below to the file you just created.

```bash
[Unit]
Description=n8n Automation (Workflow) Service
After=network-online.target postgresql.service
Wants=network-online.target

[Service]
# Dedicated user
User=n8n
Group=n8n
EnvironmentFile=/home/n8n/.n8n/.env
WorkingDirectory=/home/n8n

# App startup
ExecStart=/usr/local/bin/n8n start
Restart=always
RestartSec=5

# Limits and robustness
LimitNOFILE=65535

# Logs to journal (journalctl)
StandardOutput=journal
StandardError=journal

# Hardening (adjust if you need access outside home)
NoNewPrivileges=true
PrivateTmp=true
ProtectSystem=full
ProtectHome=read-only
ReadWritePaths=/home/n8n /tmp /var/tmp

# If n8n needs to bind ports <1024, remove the line below
CapabilityBoundingSet=
AmbientCapabilities=

# Longer timeout for initial migrations
TimeoutStartSec=120

[Install]
WantedBy=multi-user.target
```

**Step 21** - Reload `systemd`, enable the n8n service at startup and start it immediately.

```bash
sudo systemctl daemon-reload
sudo systemctl enable n8n
sudo systemctl start n8n
```

**Step 22** - Check if the service is running correctly.

```bash
systemctl status n8n
journalctl -u n8n -f
```

## Troubleshooting

If you see a migration/permission error in Postgres, run a manual test:

```bash
sudo -u n8n env $(cat /home/n8n/.n8n/.env | xargs) n8n start
```

This way you can see errors with the same variables as the service.

### **Did I help you?**

Buy me a beer! 🍺😁

PIX QrCode

![](https://cdn.hashnode.com/res/hashnode/image/upload/v1726095550809/4e01f0a4-4231-47ac-9baf-7c1c7d263fb4.png?auto=compress,format&format=webp align="left")
