---
title: "n8n là gì? Cài đặt n8n với Docker và tạo workflow tự động hóa"
description: "n8n là gì? Nền tảng workflow automation fair-code: cài n8n bằng Docker, tạo workflow GitHub gửi Slack, viết custom node và dùng AI Agent trong n8n."
canonical: "https://200lab.io/blog/n8n-la-gi"
published: "2025-03-03T04:42:01Z"
updated: "2026-10-31T08:09:54Z"
authors: ["Hướng nội"]
tags: ["Backend"]
reading_minutes: 22
access: "public"
---

Bạn có thường xuyên phải đối mặt với việc tự động hóa các tác vụ lặp đi lặp lại như đồng bộ dữ liệu, tạo webhook handlers hay xử lý sự kiện giữa các API và dịch vụ khác nhau?

Là developers, chúng ta thường phải viết hàng trăm dòng code để xử lý các vấn đề tích hợp phức tạp. Đây chính là lúc **n8n**, một nền tảng workflow automation fair-code, trở nên hữu ích. Với n8n, bạn có thể tự động hóa quy trình làm việc một cách nhanh chóng mà không cần tốn quá nhiều effort vào việc viết code tùy chỉnh.

Điểm đặc biệt của n8n là nó được thiết kế dành cho developers, với triết lý **"developer-first"**: hỗ trợ JavaScript/TypeScript native, khả năng mở rộng thông qua custom nodes, và triển khai self-hosted để kiểm soát dữ liệu.

Trong bài viết này, chúng ta sẽ xem cách triển khai n8n với Docker, tạo một workflow giám sát GitHub commits và gửi thông báo qua Slack, cũng như xây dựng custom nodes bằng CLI mới. Bài viết được cập nhật tháng 10/2026 theo n8n 2.x, bản stable mới nhất lúc viết là 2.45.7.

## 1. n8n là gì?

n8n là một nền tảng workflow automation giúp developers tạo workflow để kết nối các app, dịch vụ và API mà không cần viết quá nhiều code.

Bản trước của bài gọi n8n là mã nguồn mở, nói chính xác hơn thì n8n là fair-code và phân phối theo Sustainable Use License, chi tiết trong [license của n8n](https://docs.n8n.io/n8n-community-license). Mã nguồn được công khai trên GitHub với hơn 200 nghìn star, cho phép bạn dùng miễn phí cho mục đích nội bộ doanh nghiệp, cá nhân hoặc phi thương mại. Tuy nhiên, bạn không được đem n8n đi host làm dịch vụ bán cho khách hay white-label.

Những đặc điểm nổi bật của n8n:

1. **Fair-code**: Codebase công khai trên GitHub, cho phép bạn kiểm tra, tùy chỉnh và đóng góp.
2. **Self-hosted hoặc n8n Cloud**: Bạn có thể tự deploy n8n trên hạ tầng riêng để kiểm soát dữ liệu, hoặc dùng bản Cloud do n8n quản lý.
3. **Hơn 500 integration có sẵn**: Kết nối nhanh với các dịch vụ phổ biến, kèm theo node "HTTP Request" để gọi bất kỳ API nào.
4. **Code node linh hoạt**: Hỗ trợ viết code xử lý dữ liệu trực tiếp bằng JavaScript hoặc Python, cùng với JavaScript expressions ngay trong cấu hình node.
5. **Khả năng mở rộng với custom node**: Tự xây dựng node riêng bằng TypeScript khi cần tích hợp dịch vụ đặc thù.
6. **Tích hợp AI**: Hỗ trợ xây dựng workflow với AI Agent node và giao thức MCP.

### 1.1 Các khái niệm cơ bản trong n8n

Để làm việc với n8n, chúng ta cần nắm một số khái niệm cơ bản sau:

- **Workflow**: Một quy trình tự động hóa gồm các node nối với nhau để xử lý dữ liệu từ đầu đến cuối.
- **Node**: Từng bước trong workflow, có thể là nơi nhận dữ liệu, xử lý logic, hoặc gửi dữ liệu đi.
- **Trigger**: Node khởi đầu, quyết định khi nào workflow chạy.

![Một số loại trigger trong n8n](https://assets.200lab.io/images/2025/03/03/n8n-la-gi-1-a0dc49c013a525edeb3c.webp)

Ảnh trên minh họa một số loại trigger phổ biến: chạy theo lịch (Schedule), nhận tin nhắn Chat, lắng nghe Kafka, nhận Webhook, được gọi từ workflow khác, và bắt lỗi (Error).

- **Item**: Đơn vị dữ liệu chạy qua các node, bọc dưới dạng JSON.
- **Execution**: Một lượt chạy hoàn chỉnh của workflow từ lúc kích hoạt đến khi kết thúc.
- **Credential**: Nơi lưu thông tin xác thực như API key hay token, được mã hóa riêng khỏi workflow.
- **Expression**: Đoạn biểu thức đặt trong cặp ngoặc nhọn `{{ }}` giúp lấy và biến đổi dữ liệu động giữa các node.

Từ bản n8n 2.0, nút bật/tắt "Active" quen thuộc đã được thay bằng nút "Publish". Bạn có thể sửa nháp workflow thoải mái, còn các lượt chạy thật ở chế độ tự động sẽ luôn dùng phiên bản đã publish.

## 2. n8n dùng để làm gì?

Một số việc developers hay giao cho n8n: đồng bộ dữ liệu hai chiều giữa các hệ thống nội bộ và CRM, hoặc nhận webhook từ cổng thanh toán rồi cập nhật trạng thái đơn hàng. Ngoài ra còn có luồng gửi cảnh báo hệ thống về Slack, gom số liệu từ nhiều nguồn để xuất báo cáo định kỳ, và gần đây là xây dựng các workflow có kết hợp AI.

### 2.1 n8n có làm được AI Agent không?

Câu trả lời là có. n8n cung cấp sẵn "AI Agent" node: bạn chỉ cần nối một chat model và ít nhất một tool sub-node vào, agent sẽ tự quyết định gọi tool nào dựa trên yêu cầu đầu vào. Nếu bạn mới tiếp cận khái niệm này, bạn có thể đọc lại [bài AI Agent là gì](https://200lab.io/blog/ai-agent-la-gi) trên blog 200Lab.

Ngoài ra, n8n hỗ trợ [Model Context Protocol (MCP)](https://200lab.io/blog/model-context-protocol-mcp-la-gi) theo cả hai chiều. Bạn có thể dùng "MCP Server Trigger" để biến workflow của n8n thành một MCP server cho phép MCP client bên ngoài gọi vào dùng tool, hoặc gắn "MCP Client Tool" để agent trong n8n lấy tool từ một MCP server khác.

Với những thao tác nhạy cảm, n8n hỗ trợ bước người duyệt (human-in-the-loop) trước khi agent thực sự chạy tool. Hiện tại, n8n cũng đang thử nghiệm tính năng Agents riêng ở dạng Preview.

## 3. n8n có miễn phí không? n8n Cloud và bản self-host

Về chi phí, bản Community khi bạn tự self-host là miễn phí theo Sustainable Use License. Bạn cũng có thể đăng ký email để nâng lên bản Registered Community miễn phí nhằm mở khóa thêm một số tính năng. Nếu có nhu cầu quản trị lớn hơn, n8n cung cấp hai bản trả phí là Business và Enterprise.

Với những ai không muốn tự vận hành hạ tầng, n8n Cloud là phiên bản được host sẵn, tính phí theo từng plan và có cho dùng thử miễn phí 14 ngày. Theo [trang giá của n8n](https://n8n.io/pricing/), n8n Cloud tính phí dựa trên số lượng execution: mỗi execution được tính là một lần chạy trọn vẹn workflow, bất kể bên trong có bao nhiêu bước.

Tài liệu chính thức của n8n cũng lưu ý rằng việc self-host dành cho người đã có kinh nghiệm kỹ thuật, vì bạn phải tự lo liệu chuyện cấu hình server, bảo mật, backup dữ liệu và cập nhật phiên bản mới. Theo mình, nếu bạn muốn thử nghiệm nhanh thì cứ đăng ký n8n Cloud, còn khi cần giữ toàn bộ dữ liệu nằm trong hạ tầng nội bộ thì hãy chọn self-host.

## 4. Hướng dẫn cài đặt n8n với Docker

Docker là cách nhanh chóng để chạy n8n, từ môi trường development cho tới production. Nếu chưa quen làm việc với container, bạn nên xem qua [bài hướng dẫn cài đặt và sử dụng Docker](https://200lab.io/blog/huong-dan-cai-dat-va-su-dung-docker) trước khi bắt đầu. Lưu ý là từ bản n8n 3.0 trở đi, việc self-host sẽ chỉ hỗ trợ Docker, cách cài trực tiếp qua `npm` hay chạy `npx n8n` như trước đây không còn được hỗ trợ nữa.

### 4.1 Cài nhanh bằng one-line setup

Để dựng nhanh trên máy mới cài lần đầu, bạn có thể chạy lệnh sau theo [hướng dẫn one-line setup](https://docs.n8n.io/deploy/host-n8n/install-options/one-line-setup) của n8n:

```bash
curl -fsSL https://get.n8n.io | sh
```

Cách này yêu cầu máy bạn đã có sẵn Docker cùng plugin Docker Compose v2. Nếu bạn muốn tự tay kiểm soát chi tiết cấu hình ngay từ đầu thì hãy làm theo mục 4.2 bên dưới.

### 4.2 Tạo file Docker Compose

Đầu tiên, bạn tạo một file có tên `docker-compose.yml` ở thư mục hiện tại với nội dung như sau:

```yaml
services:
  n8n:
    image: n8nio/n8n
    restart: always
    ports:
      - "5678:5678"
    environment:
      - GENERIC_TIMEZONE=Asia/Ho_Chi_Minh
      - TZ=Asia/Ho_Chi_Minh
      - N8N_ENFORCE_SETTINGS_FILE_PERMISSIONS=true
      - WEBHOOK_URL=http://localhost:5678/
      # Database (mặc định dùng SQLite). Production nên dùng PostgreSQL:
      # - DB_TYPE=postgresdb
      # - DB_POSTGRESDB_HOST=postgres
      # - DB_POSTGRESDB_PORT=5432
      # - DB_POSTGRESDB_DATABASE=n8n
      # - DB_POSTGRESDB_USER=postgres
      # - DB_POSTGRESDB_PASSWORD=postgres
    volumes:
      - n8n_data:/home/node/.n8n

volumes:
  n8n_data:
```

Ở file trên, chúng ta bỏ dòng khai báo `version` cũ vì Docker Compose v2 không còn cần đến nó. Cấu hình dùng named volume `n8n_data` để giữ lại database SQLite cùng encryption key ngay cả khi bạn xóa container. Hai biến `GENERIC_TIMEZONE` và `TZ` giúp các node hẹn giờ chạy đúng theo múi giờ Việt Nam.

### 4.3 Khởi động n8n

Chạy lệnh sau trong thư mục chứa file `docker-compose.yml`:

```bash
docker compose up -d
docker compose logs -f n8n
```

Bạn chú ý dùng lệnh `docker compose` của bản v2 thay vì lệnh `docker-compose` có dấu gạch ngang cũ. Lệnh đầu tải image của n8n, khởi động container và để nó chạy ở chế độ background; lệnh sau để xem log, bấm Ctrl+C để thoát mà container vẫn chạy. Sau khi hoàn tất, bạn mở trình duyệt truy cập `http://localhost:5678`, ở lần đầu tiên n8n sẽ yêu cầu bạn tạo tài khoản owner.

### 4.4 Cấu hình cho môi trường Production

Nếu bạn triển khai n8n trong môi trường production, hãy lưu ý một số thiết lập quan trọng để đảm bảo an toàn dữ liệu:

1. Thay đổi `WEBHOOK_URL` thành domain thực tế của bạn.
2. Đặt đúng `N8N_HOST` và chuyển `N8N_PROTOCOL=https`.
3. Cấu hình database PostgreSQL thay vì dùng SQLite mặc định.
4. Khai báo biến môi trường `N8N_ENCRYPTION_KEY` cố định.

```yaml
# Thêm vào phần environment của service n8n khi chạy production
environment:
  - N8N_ENCRYPTION_KEY=your-secure-encryption-key
  - N8N_HOST=n8n.your-domain.com
  - N8N_PROTOCOL=https
  - WEBHOOK_URL=https://n8n.your-domain.com/
  - N8N_EMAIL_MODE=smtp
  - N8N_SMTP_HOST=smtp.example.com
  - N8N_SMTP_PORT=587
  - N8N_SMTP_USER=your-smtp-user
  - N8N_SMTP_PASS=your-smtp-password
```

Bạn nhớ cất giữ thật kỹ giá trị `N8N_ENCRYPTION_KEY` này. Nếu làm mất key khi chuyển server, n8n sẽ không thể giải mã được các credential mà bạn đã lưu trong database.

## 5. Hướng dẫn xây dựng Workflow đơn giản với n8n

Chúng ta sẽ bắt đầu xây dựng một workflow thực tế: tự động kiểm tra GitHub repository và gửi thông báo qua Slack khi có commit mới.

### 5.1 Tổng quan về workflow

Workflow này gồm 4 bước nối tiếp nhau:

1. "Schedule Trigger" tự động kích hoạt mỗi 15 phút.
2. "HTTP Request" gọi GitHub API để lấy danh sách commit trong 15 phút gần nhất.
3. "Code" node lọc và định dạng lại thông tin commit cho dễ đọc.
4. "Slack" node gửi tin nhắn thông báo vào channel của nhóm.

![Workflow kiểm tra commit GitHub và gửi thông báo qua Slack](https://assets.200lab.io/sha256/40/401b8062b10a427c37544cb990a82723729596ca24b6078603cb8c0865e7fe63/generations/1c6e4a75117fdfddba2e818a1699cc147ed50de7f52d9edad8b2b6ff3e7f8492)

Ở phiên bản trước của bài viết, chúng ta dùng GitHub node để polling trực tiếp. Tuy nhiên, "GitHub Trigger" hiện tại hoạt động dựa trên cơ chế webhook, đòi hỏi n8n phải có một URL public để GitHub gọi tới.

Khi bạn đang chạy thử n8n ở `localhost`, việc chủ động polling qua HTTP Request API sẽ dễ làm hơn; sau này đã đưa lên server có domain thật, bạn có thể đổi sang dùng "GitHub Trigger" bắt sự kiện `push`.

### 5.2 Thiết lập workflow

#### 5.2.1 Tạo workflow mới

1. Mở n8n UI tại `http://localhost:5678`.
2. Tạo một workflow mới.
3. Đặt tên cho workflow là "GitHub Commit Monitor".

#### 5.2.2 Thêm Schedule Trigger

1. Bấm dấu cộng để thêm node đầu tiên, tìm và chọn "Schedule Trigger".
2. Thiết lập "Trigger Interval" chạy theo phút (Minutes), đặt khoảng cách là 15 phút.

#### 5.2.3 Lấy commit bằng HTTP Request node

1. Thêm node "HTTP Request" nối tiếp sau trigger và cấu hình các thông số như dưới đây:

```text
Method: GET
URL: https://api.github.com/repos/n8n-io/n8n/commits
Query Parameters:
  since    = {{ $now.minus({ minutes: 15 }).toISO() }}
  per_page = 20
```

Tham số query `since` sẽ lấy các commit tính từ 15 phút trước thời điểm chạy, vừa khớp với chu kỳ của trigger. Ở mục Authentication, bạn chọn Predefined Credential Type là "GitHub API" rồi điền Personal Access Token vào; dù repo public vẫn gọi không cần token được nhưng sẽ bị giới hạn số request thấp hơn nhiều.

Khi GitHub trả về một mảng JSON các commit, n8n sẽ tự tách mỗi commit thành một item riêng. Trường hợp 15 phút qua không có commit nào mới, node này trả về 0 item và các node phía sau sẽ tự dừng không chạy, nhờ vậy Slack không bị nhận tin nhắn rỗng.

Một điểm nhỏ cần lưu ý là tham số `since` của GitHub lọc theo thời gian ghi trong commit, nên một commit có ngày cũ nhưng mới được push lên có thể bị sót; nếu cần bắt chính xác theo thời điểm push thì bạn nên dùng webhook.

#### 5.2.4 Định dạng dữ liệu bằng Code node

Trong bản bài viết cũ, bước này dùng "Function" node, nhưng từ bản 0.198.0 trở đi thì node đó đã được thay thế bằng "Code" node.

1. Thêm một "Code" node nằm sau "HTTP Request", giữ nguyên mode mặc định là "Run Once for All Items".
2. Chọn ngôn ngữ JavaScript và dán đoạn code sau vào:

```javascript
// Định dạng thông tin commit để hiển thị trong Slack
const repo = 'n8n-io/n8n';

return $input.all().map(item => {
  const c = item.json;
  return {
    json: {
      commitInfo: {
        message: c.commit.message.split('\n')[0],
        author: c.commit.author.name,
        date: c.commit.author.date,
        url: c.html_url,
        repo,
        sha: c.sha.substring(0, 7),
      },
    },
  };
});
```

Hàm `$input.all()` giúp chúng ta lấy toàn bộ danh sách item từ node trước chuyển sang. Bạn nhớ đảm bảo mỗi phần tử trong mảng trả về đều phải bọc trong cấu trúc `{ json: ... }` theo chuẩn của n8n.

#### 5.2.5 Gửi thông báo bằng Slack node

1. Thêm "Slack" node vào cuối luồng và tạo credential kết nối tới workspace của bạn.
2. Chọn Resource là "Message", Operation là "Send", sau đó chọn channel nhận tin như `#dev-updates`.
3. Ở ô Message Text, chuyển sang chế độ Expression và điền nội dung sau:

```text
New commit in {{ $json.commitInfo.repo }} by {{ $json.commitInfo.author }}:

*{{ $json.commitInfo.message }}*

🔗 <{{ $json.commitInfo.url }}|View on GitHub>
```

Cuối cùng, bạn kiểm tra lại toàn bộ luồng rồi bấm nút "Publish" ở góc trên để workflow bắt đầu chạy tự động theo lịch (thay cho nút "Active" ở các bản cũ).

### 5.3 Phân loại commit bằng Code node

Để xử lý dữ liệu chi tiết hơn, chúng ta có thể chèn thêm một "Code" node nữa nằm giữa node định dạng và Slack node nhằm tự động gắn nhãn phân loại cho từng commit:

```javascript
// Phân tích commit message để tìm từ khóa và phân loại commit
return $input.all().map(item => {
  const commitInfo = item.json.commitInfo;
  const message = commitInfo.message.toLowerCase();

  // Phân loại commit dựa trên nội dung message
  let type = "other";
  let priority = "normal";
  let emoji = "🔄";

  if (message.includes("fix") || message.includes("bug") || message.includes("issue")) {
    type = "bugfix";
    emoji = "🐛";
  } else if (message.includes("feat") || message.includes("feature") || message.includes("add")) {
    type = "feature";
    emoji = "✨";
  } else if (message.includes("docs") || message.includes("documentation")) {
    type = "docs";
    emoji = "📚";
  } else if (message.includes("refactor")) {
    type = "refactor";
    emoji = "♻️";
  } else if (message.includes("test")) {
    type = "test";
    emoji = "🧪";
  }

  // Xác định priority
  if (message.includes("urgent") || message.includes("critical") || message.includes("important")) {
    priority = "high";
    emoji = "🚨 " + emoji;
  }

  // Cập nhật commitInfo với phân loại
  commitInfo.type = type;
  commitInfo.priority = priority;
  commitInfo.emoji = emoji;

  return { json: { commitInfo } };
});
```

Sau đó, bạn cập nhật lại nội dung ô Message Text bên trong Slack node để hiển thị thêm loại commit và mức độ ưu tiên:

```text
{{ $json.commitInfo.emoji }} *[{{ $json.commitInfo.type }}]* New commit in {{ $json.commitInfo.repo }} by {{ $json.commitInfo.author }}:

*{{ $json.commitInfo.message }}*

Priority: {{ $json.commitInfo.priority }}
🔗 <{{ $json.commitInfo.url }}|View on GitHub>
```

Mình đã chạy thử workflow này trên n8n 2.45.7 với repo `n8n-io/n8n`: thay Schedule Trigger bằng Manual Trigger, bỏ bước Slack, lấy commit trong 24 giờ với `per_page` là 5. Node HTTP Request trả về 5 item, hai Code node phía sau chạy qua cả 5 item, gắn đúng nhãn `bugfix` cho commit có message bắt đầu bằng `fix:` và gắn nhãn `feature` cho commit bắt đầu bằng `feat(core):`.

## 6. Tạo Custom Node cho n8n bằng n8n-node CLI

Một trong những điểm mạnh của n8n là khả năng tạo custom node để đóng gói logic riêng hoặc tích hợp những hệ thống nội bộ mà n8n chưa có sẵn. Cách làm ở bản trước của bài là clone cả repo n8n về máy rồi chạy bootstrap, cách này không còn là cách n8n hướng dẫn. Hiện tại, chúng ta dùng công cụ CLI chính thức tên là `n8n-node` theo [tài liệu n8n-node](https://docs.n8n.io/connect/create-nodes/build-your-node/using-the-n8n-node-tool).

### 6.1 Tạo project với n8n-node

Bạn mở terminal và chạy cụm lệnh dưới đây để khởi tạo project:

```bash
npm create @n8n/node@latest n8n-nodes-commit-tag -- --template programmatic/example
cd n8n-nodes-commit-tag
npm run dev
```

Lệnh `npm create` tạo khung project từ template `programmatic/example` và cài sẵn dependencies. Khi chạy `npm run dev`, công cụ sẽ tự build node rồi khởi động một bản n8n local tại `http://localhost:5678` có cài sẵn node của bạn, hễ sửa code lưu lại là nó tự build lại ngay. Lưu ý là từ bản n8n 3.0, lệnh `dev` sẽ chạy n8n bên trong container nên máy bạn bắt buộc phải có Docker hoặc Podman.

### 6.2 Viết code cho custom node

Nối tiếp bài toán ở phần 5, chúng ta sẽ viết một custom node tên là "Commit Tag" làm nhiệm vụ gắn nhãn phân loại commit. Bạn tạo file `nodes/CommitTag/CommitTag.node.ts` với nội dung như sau:

```ts
import type {
	IExecuteFunctions,
	INodeExecutionData,
	INodeType,
	INodeTypeDescription,
} from 'n8n-workflow';
import { NodeConnectionTypes } from 'n8n-workflow';

export class CommitTag implements INodeType {
	description: INodeTypeDescription = {
		displayName: 'Commit Tag',
		name: 'commitTag',
		icon: { light: 'file:commitTag.svg', dark: 'file:commitTag.dark.svg' },
		group: ['transform'],
		version: 1,
		subtitle: 'Phân loại commit',
		description: 'Gắn loại commit dựa trên commit message',
		defaults: {
			name: 'Commit Tag',
		},
		inputs: [NodeConnectionTypes.Main],
		outputs: [NodeConnectionTypes.Main],
		usableAsTool: true,
		properties: [
			{
				displayName: 'Commit Message',
				name: 'commitMessage',
				type: 'string',
				default: '',
				placeholder: '{{ $json.commitInfo.message }}',
				description: 'Commit message cần phân loại, thường kéo từ node trước bằng expression',
			},
		],
	};

	async execute(this: IExecuteFunctions): Promise<INodeExecutionData[][]> {
		const items = this.getInputData();
		const returnData: INodeExecutionData[] = [];

		for (let i = 0; i < items.length; i++) {
			const message = (this.getNodeParameter('commitMessage', i) as string).toLowerCase();

			let type = 'other';
			if (message.startsWith('fix')) type = 'bugfix';
			else if (message.startsWith('feat')) type = 'feature';
			else if (message.startsWith('docs')) type = 'docs';

			returnData.push({
				json: { ...items[i].json, type },
				pairedItem: { item: i },
			});
		}

		return [returnData];
	}
}
```

Nhìn vào đoạn code trên, có 3 điểm thay đổi so với cách viết ở các phiên bản cũ mà bạn cần để ý:

1. Toàn bộ các kiểu dữ liệu như `IExecuteFunctions` hiện đều được import chung từ gói `n8n-workflow`, không còn lấy từ `n8n-core` nữa.
2. Cấu hình `inputs` và `outputs` sử dụng hằng số `NodeConnectionTypes.Main` thay vì viết chuỗi `'main'` trực tiếp.
3. Thuộc tính `usableAsTool: true` giúp báo cho n8n biết node này có thể gắn thẳng vào "AI Agent" node để làm một tool cho agent gọi.

Viết code xong, bạn đừng quên mở file `package.json` và khai báo đường dẫn sau khi build là `dist/nodes/CommitTag/CommitTag.node.js` vào trong mảng `n8n.nodes`.

### 6.3 Build, lint và release custom node

Để kiểm tra và đóng gói node, bạn sử dụng các lệnh đi kèm của CLI:

```bash
npm run build
npm run lint
npm run release
```

Lệnh `lint` sẽ rà soát code của bạn theo bộ quy chuẩn community node của n8n. Lúc mình chạy thử lần đầu trên project mới tạo, trình lint đã báo lỗi ngay vì trong `package.json` vẫn còn để trống các thông tin mẫu như `author`, `description`, `repository` và trong mô tả node bị thiếu trường `subtitle`; sau khi điền đủ các mục này thì lệnh lint chạy qua.

Khi mọi thứ đã ổn, lệnh `release` sẽ build, lint và publish package lên npm. Trước đó bạn cần chạy `npm login`.

## 7. Best Practices khi làm việc với n8n

### 7.1 Xử lý lỗi bằng Error Trigger

Thay vì để workflow âm thầm chết khi gặp sự cố, bạn nên tạo một workflow riêng bắt đầu bằng node "Error Trigger" rồi nối vào Slack hoặc Email để nhận cảnh báo ngay. Sau đó, bạn mở workflow chính lên, vào phần Settings và chọn workflow báo lỗi vừa tạo ở ô "Error workflow".

Một lưu ý quan trọng là Error workflow chỉ tự động kích hoạt đối với các lượt chạy tự động (production executions) bị lỗi, còn khi bạn đang bấm chạy thử bằng tay trên UI thì nó sẽ không gọi. Bên cạnh đó, bên trong các "Code" node, bạn vẫn nên chủ động bọc `try/catch` cho những đoạn xử lý dễ phát sinh ngoại lệ.

### 7.2 Quản lý credential và biến môi trường

Với các thông tin xác thực như API key hay token, bạn nên lưu vào mục Credential của n8n để chúng được mã hóa bằng encryption key thay vì viết cứng trong node. Bản trước của bài hướng dẫn đọc secret từ biến môi trường bằng `$env`, nhưng từ bản n8n 2.0, biến `N8N_BLOCK_ENV_ACCESS_IN_NODE` đã được bật mặc định thành `true` theo [ghi chú breaking change của n8n 2.0](https://docs.n8n.io/changelog/v20-breaking-changes) nhằm chặn các node đọc biến môi trường.

Khi mình thử viết expression `{{ $env.MY_TEST_VAR }}` trên bản n8n 2.45.7, workflow lập tức dừng lại và trả về lỗi:

```text
Execution error:
access to env vars denied
```

Nếu thực sự cần đọc biến môi trường cấu hình chung, bạn phải khai báo `N8N_BLOCK_ENV_ACCESS_IN_NODE=false` cho container. Tuy vậy, những dữ liệu nhạy cảm thì vẫn nên cất vào Credential để đảm bảo an toàn.

### 7.3 Xử lý dữ liệu lớn

Khi workflow phải xử lý danh sách hàng nghìn record, bạn hãy áp dụng các kinh nghiệm sau:

- Node "Split In Batches" trước đây nay đã được đổi tên thành "Loop Over Items".
- Phần lớn các node trong n8n vốn đã tự động lặp qua toàn bộ danh sách item đầu vào, nên bạn chỉ cần dùng đến "Loop Over Items" khi muốn chia nhỏ request để tránh bị dính rate limit của API bên thứ ba, hoặc khi gặp một số ít node không tự lặp.
- Luôn giới hạn số lượng item lấy về trong mỗi lần chạy, tương tự như cách chúng ta đặt `per_page` ở phần 5.
- Chuyển sang dùng database PostgreSQL cho môi trường production thay vì để SQLite mặc định.

### 7.4 Backup workflow và quản lý phiên bản

Để tránh mất mát công sức thiết kế luồng, bạn nên duy trì thói quen sao lưu định kỳ:

- Xuất toàn bộ workflow ra file JSON bằng lệnh CLI bên dưới. Cờ `--backup` sẽ giúp tách riêng mỗi workflow thành một file độc lập, rất tiện để bạn đưa vào Git quản lý lịch sử thay đổi:

```bash
docker compose exec n8n n8n export:workflow --backup --output=/home/node/.n8n/backups/
```

- Thiết lập lịch backup định kỳ cho database của n8n và lưu trữ `N8N_ENCRYPTION_KEY` ở một nơi an toàn tách biệt với server.

### 7.5 Chuẩn bị nâng cấp lên n8n 3.0

Phiên bản n8n 3.0.0 đã ra mắt bản early access vào ngày 27/10/2026 với một số thay đổi lớn mà bạn cần lưu tâm nếu định nâng cấp:

- Việc self-host chỉ còn hỗ trợ qua Docker.
- Hai node hẹn giờ cũ là "Cron" và "Interval" bị loại bỏ, chuyển hẳn sang dùng "Schedule Trigger".
- Thời gian timeout mặc định của "Code" node (`N8N_RUNNERS_TASK_TIMEOUT`) giảm mạnh từ 5 phút (300 giây) xuống còn 1 phút (60 giây); các đoạn script chạy lâu hơn mức này sẽ bị ngắt báo lỗi.
- Các gói community node chưa được verified sẽ bị tắt mặc định (`N8N_UNVERIFIED_PACKAGES_ENABLED=false`).
- Credential của Slack API sẽ yêu cầu phải có thêm thông tin signing secret.

Trước khi bấm nâng cấp, bạn nên mở mục Settings > Migration Report ngay trên n8n UI để rà soát xem có workflow nào đang dùng tính năng sắp bị gỡ hay không, đồng thời tạo một bản backup đầy đủ.

## 8. Kết luận

n8n là một công cụ đáng giá cho các bạn developer muốn tự động hóa các tác vụ và quy trình làm việc thay vì phải tốn thời gian viết code kết nối từ đầu. Với khả năng tùy biến linh hoạt, triển khai bằng Docker và hỗ trợ sẵn JavaScript/TypeScript lẫn Python, n8n giúp chúng ta tiết kiệm được rất nhiều công sức khi làm các bài toán tích hợp hệ thống.

Điểm mạnh nhất của n8n là khả năng mở rộng: bạn tự host để giữ dữ liệu trong hạ tầng của mình, viết custom node khi thiếu integration, và gắn AI Agent vào workflow khi cần.

Khi workflow tự động hóa bắt đầu có agent tham gia, khóa miễn phí dưới đây giúp bạn chia việc và giao việc cho agent rõ ràng hơn.

![Làm chủ tư duy trong thời đại AI - tư duy hệ thống, cách giao việc và quản lý agent](https://assets.200lab.io/sha256/09/092dd9c43004cac7d19f1267acbd8322d5ead2f5c02fd424f8dc5a9329247dc6/generations/cbea2773e2faed86628455883c5f6f5232ca0843b18c8351b9ba5c629c615717)

- [Làm chủ tư duy trong thời đại AI - tư duy hệ thống, cách giao việc và quản lý agent](https://200lab.io/courses/lam-chu-tu-duy-trong-thoi-dai-ai-tu-duy-he-thong-cach-giao-viec-va-quan-ly-agent.md)

## Đọc thêm

- [Transaction là gì, isolation level là gì? Chạy thử trên PostgreSQL, MySQL](https://200lab.io/blog/transaction-isolation-level-la-gi.md): Transaction là gì mà trừ kho vẫn sai? Chạy thử 4 mức isolation level trên PostgreSQL, MySQL để hiểu dirty read, phantom read, MVCC và cách chặn lost update.
- [Reverse proxy là gì? Cách cấu hình Nginx và phân biệt với API gateway](https://200lab.io/blog/reverse-proxy-la-gi.md): Reverse proxy là gì, khác forward proxy và API gateway ra sao? Cấu hình reverse proxy Nginx với proxy_pass, upstream, X-Forwarded-For và cách xử lý lỗi 502.
- [Idempotency là gì? Idempotency key chống trừ tiền hai lần khi retry](https://200lab.io/blog/idempotency-key-la-gi.md): Idempotency key giúp POST retry an toàn khi timeout. Middleware Go chạy thật: giữ chỗ atomic, 409 khi trùng đồng thời, replay đúng response cũ.
- [Game System Design GonnaCrash được làm như thế nào](https://200lab.io/blog/game-system-design-gonnacrash-duoc-lam-nhu-the-nao.md): Kéo thả một hệ thống rồi nhìn nó trụ hay sập: cách mình dựng GonnaCrash, từ server làm trọng tài, con bot giải đề đến skin whiteboard.
- [Architecture Pattern - Phần 7: Hexagonal Architecture](https://200lab.io/blog/hexagonal-architecture-la-gi.md): Hexagonal Architecture là pattern kiến trúc mà mọi tương tác từ bên ngoài đều phải đi qua một Adapter để kết nối vào một Port của Application
- [Mọi bài viết chủ đề Backend](https://200lab.io/blog/tags/backend.md)

Toàn bộ nội dung: https://200lab.io/llms.txt
