n8n-self-hosting

Compare original and translation side by side

🇺🇸

Original

English
🇨🇳

Translation

Chinese

Deploying self-hosted n8n

部署自托管n8n

This skill takes a fresh Linux VM (Ubuntu/Debian, root or sudo SSH) to a running, HTTPS, production n8n via Docker Compose behind Caddy (automatic Let's Encrypt TLS). It is for self-hosted n8n on Docker — not n8n Cloud, and not for building workflows (that's the rest of this pack).
Two deployment modes. The architectures differ, so pick the mode before doing anything.
You drive this end-to-end over SSH: preflight → install Docker → lay down the project → generate secrets → launch → verify TLS → hand off. The template files live in
assets/
; the per-mode and security depth live in the reference files named below.
本技能可将一台全新Linux虚拟机(Ubuntu/Debian系统,支持root或sudo权限SSH)通过Docker Compose搭配Caddy(自动Let's Encrypt TLS)部署为可运行、带HTTPS的生产级n8n。本技能针对的是Docker上的自托管n8n——而非n8n Cloud,也不涉及工作流构建(工作流构建属于该工具包的其他内容)。
支持两种部署模式。两种架构不同,因此在操作前请先选择模式
整个流程通过SSH端到端完成:预检 → 安装Docker → 部署项目 → 生成密钥 → 启动服务 → 验证TLS → 交付。模板文件存放在
assets/
目录下;各模式的细节和安全配置在下文提及的参考文件中。

Rule 0 — choose the mode (ask the user)

规则0 — 选择模式(询问用户)

Do not guess. Ask, then commit to one:
Single / regularQueue
Processesone n8nmain + N workers
Extra servicesnone (SQLite)Redis (queue) + Postgres (DB)
Executes workflowsin the main processon workers, in parallel
Good for1 user, light/moderate load, simplest opshigh volume, heavy/long executions, horizontal scale
Compose
assets/docker-compose.single.yml
assets/docker-compose.queue.yml
Deep dive
SINGLE_MODE.md
QUEUE_MODE.md
If unsure, start single — it's the simplest correct thing and covers most needs. Moving to queue later means swapping the compose file and migrating SQLite→Postgres, so if the user already expects real volume, start queue.
请勿猜测。先询问用户,再确定其中一种:
单实例/常规模式队列模式
进程单个n8n进程主进程 + N个工作节点
额外服务无(使用SQLite)Redis(队列) + Postgres(数据库)
工作流执行在主进程中执行在工作节点上并行执行
适用场景单用户、轻/中等负载、运维最简单高流量、重负载/长时执行、横向扩展
Compose文件
assets/docker-compose.single.yml
assets/docker-compose.queue.yml
详细指南
SINGLE_MODE.md
QUEUE_MODE.md
若不确定,建议先选择单实例模式——这是最简单的正确方案,能满足大多数需求。后续若要切换到队列模式,需要替换Compose文件并将SQLite迁移到Postgres;因此如果用户已明确需要处理大量流量,建议直接选择队列模式

Rule 1 — secret hygiene (non-negotiable)

规则1 — 密钥安全(必须遵守)

A misstep here leaks client credentials. Be diligent:
  1. Generate every secret fresh, on the target box. Never copy an encryption key, DB password, or
    .env
    from another n8n instance into this one. See
    SECURITY.md
    for the
    openssl
    commands.
  2. Secrets live only in
    .env
    (mode 600), referenced by the compose as
    ${VAR}
    . Never inline a secret into
    docker-compose.yml
    , the Caddyfile, or anything you commit.
  3. The
    N8N_ENCRYPTION_KEY
    is sacred.
    It encrypts every stored credential. If it's lost or changes, all saved credentials become undecryptable. Set it explicitly, and tell the user to back it up off the box. Don't echo it into long-lived logs or chat history beyond what's needed to hand it over.
  4. Never expose internal services. Only Caddy (80/443) is public. n8n (5678), Postgres (5432), Redis (6379) stay on the private Docker network — the templates already omit their host port mappings. Don't add them.
  5. .env
    and Caddy's
    caddy_data
    volume (the issued certs + ACME account key) are not artifacts to share.
    If you're working inside a git repo, confirm
    .env
    is git-ignored before any commit.
此处操作失误会导致客户端凭证泄露,请务必谨慎:
  1. 在目标设备上生成所有新密钥。切勿从其他n8n实例复制加密密钥、数据库密码或
    .env
    文件到本实例。
    SECURITY.md
    中提供了
    openssl
    命令示例。
  2. 密钥仅存储在
    .env
    文件中
    (权限设为600),Compose文件通过
    ${VAR}
    引用。切勿将密钥直接写入
    docker-compose.yml
    、Caddyfile或任何会被提交的文件中。
  3. N8N_ENCRYPTION_KEY
    至关重要
    。它用于加密所有存储的凭证。如果该密钥丢失或变更,所有已保存的凭证将无法解密。请显式设置该密钥,并告知用户将其备份到设备外的安全位置。除必要的交付环节外,切勿将其输出到长期日志或聊天记录中。
  4. 切勿暴露内部服务。仅将Caddy(端口80/443)对外开放。n8n(5678)、Postgres(5432)、Redis(6379)应保留在私有Docker网络中——模板已省略它们的主机端口映射,请勿添加。
  5. .env
    文件和Caddy的
    caddy_data
    卷(包含已颁发的证书 + ACME账户密钥)不可共享
    。如果在git仓库中操作,请确认
    .env
    已被git忽略后再进行提交。

Inputs to collect up front

需提前收集的信息

  • SSH target
    user@host
    and how you authenticate (key path or the user confirms the agent already has access). Root or a sudo user.
  • Domain — the full hostname n8n will live at, e.g.
    n8n.example.com
    (→
    SUBDOMAIN=n8n
    ,
    DOMAIN_NAME=example.com
    ). The user must control its DNS.
  • TLS email — for Let's Encrypt (
    SSL_EMAIL
    ).
  • Timezone — IANA name for Schedule/Cron nodes (e.g.
    Europe/Warsaw
    ), else
    Etc/UTC
    .
  • Mode — single or queue (Rule 0). Queue → confirm the box has enough RAM (rough floor ~4 GB; each worker wants ~1–2 GB).
  • SSH目标
    user@host
    以及认证方式(密钥路径或用户确认Agent已拥有访问权限)。需使用root或拥有sudo权限的用户。
  • 域名 — n8n将使用的完整主机名,例如
    n8n.example.com
    (对应
    SUBDOMAIN=n8n
    DOMAIN_NAME=example.com
    )。用户必须拥有该域名的DNS控制权。
  • TLS邮箱 — 用于Let's Encrypt证书申请(对应
    SSL_EMAIL
    )。
  • 时区 — Schedule/Cron节点使用的IANA时区名称(例如
    Europe/Warsaw
    ),默认使用
    Etc/UTC
  • 模式 — 单实例或队列模式(规则0)。若选择队列模式,请确认设备拥有足够内存(最低约4GB;每个工作节点需约1–2GB)。

The deploy flow

部署流程

Work through these in order.
SINGLE_MODE.md
/
QUEUE_MODE.md
give the mode-specific command detail;
SECURITY.md
covers secret generation and hardening;
DAY2.md
covers update/backup/restore.
请按以下顺序执行。
SINGLE_MODE.md
/
QUEUE_MODE.md
提供了各模式的具体命令细节;
SECURITY.md
涵盖密钥生成和加固操作;
DAY2.md
涵盖更新/备份/恢复操作。

1. Preflight (the cheapest failure is the one you catch here)

1. 预检(提前发现问题成本最低)

  • SSH in; confirm the OS is Debian/Ubuntu-like (
    . /etc/os-release
    ).
  • DNS must already point at the box. Compare the box's public IP (
    curl -s ifconfig.me
    ) with
    dig +short <fqdn>
    (run it from the box AND ideally your laptop). If they don't match, stop — Caddy's ACME challenge will fail. Have the user create the A record, wait for it to propagate, then continue.
  • Ports 80 and 443 must be reachable from the internet. Check the host firewall AND any cloud security group / network firewall (Hetzner Cloud, AWS SG, etc.) — these are outside the box and a common silent blocker.
  • 登录SSH;确认系统为Debian/Ubuntu系列(执行
    . /etc/os-release
    )。
  • DNS必须已指向目标设备。对比设备的公网IP(执行
    curl -s ifconfig.me
    )与
    dig +short <fqdn>
    的结果(在设备和本地笔记本上分别执行)。如果两者不匹配,立即停止操作——Caddy的ACME验证会失败。请让用户创建A记录,等待DNS生效后再继续。
  • 端口80和443必须能从互联网访问。检查主机防火墙以及任何云安全组/网络防火墙(Hetzner Cloud、AWS安全组等)——这些位于设备外部,是常见的隐性障碍。

2. Install Docker (if absent)

2. 安装Docker(若未安装)

  • Check
    docker --version
    and
    docker compose version
    . If missing, install Docker Engine + the Compose plugin (Docker's official
    get.docker.com
    script on Ubuntu/Debian is fine). Re-check
    docker compose version
    before proceeding.
  • 检查
    docker --version
    docker compose version
    。若未安装,安装Docker Engine + Compose插件(Ubuntu/Debian系统可使用Docker官方的
    get.docker.com
    脚本)。继续操作前请重新检查
    docker compose version

3. Lay down the project

3. 部署项目

  • Pick
    DATA_FOLDER
    — an absolute path, e.g.
    /opt/n8n
    . The
    DATA_FOLDER
    value in
    .env
    must equal this exact directory (the compose mounts
    ${DATA_FOLDER}/caddy_config/Caddyfile
    , and
    init-data.sh
    is mounted via a relative
    ./
    path), so always run
    docker compose
    from here. Create it, plus
    caddy_config/
    and
    local_files/
    inside.
  • Get the template files onto the box. They live in this skill's
    assets/
    on your machine, not on the server — transfer each one. Either
    scp
    them up, or (no local copy needed) write each file's contents over SSH, e.g.
    ssh <target> 'cat > <DATA_FOLDER>/docker-compose.yml' < assets/docker-compose.single.yml
    . Land them with these exact names:
    • the chosen compose →
      <DATA_FOLDER>/docker-compose.yml
      (rename it to exactly this)
    • Caddyfile
      <DATA_FOLDER>/caddy_config/Caddyfile
    • queue only:
      init-data.sh
      <DATA_FOLDER>/init-data.sh
      , then
      chmod +x
      it
    • the matching
      .env.*.example
      <DATA_FOLDER>/.env
  • 选择
    DATA_FOLDER
    ——绝对路径,例如
    /opt/n8n
    .env
    文件中的
    DATA_FOLDER
    必须与该路径完全一致(Compose文件挂载
    ${DATA_FOLDER}/caddy_config/Caddyfile
    init-data.sh
    通过相对路径
    ./
    挂载),因此请始终在该目录下执行
    docker compose
    命令。创建该目录,并在其中创建
    caddy_config/
    local_files/
    子目录。
  • 将模板文件传输到目标设备。这些文件存放在本地的本技能
    assets/
    目录中,而非服务器上——请逐个传输。可使用
    scp
    上传,或(无需本地副本)通过SSH写入文件内容,例如:
    ssh <target> 'cat > <DATA_FOLDER>/docker-compose.yml' < assets/docker-compose.single.yml
    。 文件命名必须完全一致:
    • 选择的Compose文件 →
      <DATA_FOLDER>/docker-compose.yml
      (重命名为此名称)
    • Caddyfile
      <DATA_FOLDER>/caddy_config/Caddyfile
    • 仅队列模式:
      init-data.sh
      <DATA_FOLDER>/init-data.sh
      ,然后执行
      chmod +x
      赋予执行权限
    • 对应的
      .env.*.example
      文件 →
      <DATA_FOLDER>/.env

4. Fill
.env
+ generate secrets

4. 填充
.env
+ 生成密钥

  • Set
    DATA_FOLDER
    ,
    DOMAIN_NAME
    ,
    SUBDOMAIN
    ,
    SSL_EMAIL
    ,
    GENERIC_TIMEZONE
    .
  • Generate each secret on the box with
    openssl
    (
    SECURITY.md
    has the commands) and write it into
    .env
    , replacing the matching
    REPLACE_WITH_…
    placeholder
    :
    N8N_ENCRYPTION_KEY
    ; queue also
    POSTGRES_PASSWORD
    +
    POSTGRES_NON_ROOT_PASSWORD
    .
  • Before launching, confirm none are left unset:
    grep REPLACE_WITH_ .env
    must return nothing — a leftover placeholder becomes the literal password and Postgres/n8n fail to connect.
  • chmod 600 .env
    . Record the encryption key so the user can back it up off-box.
  • 设置
    DATA_FOLDER
    DOMAIN_NAME
    SUBDOMAIN
    SSL_EMAIL
    GENERIC_TIMEZONE
  • 在目标设备上使用
    openssl
    生成每个密钥(
    SECURITY.md
    提供了命令),并写入
    .env
    文件,替换对应的
    REPLACE_WITH_…
    占位符
    N8N_ENCRYPTION_KEY
    ;队列模式还需生成
    POSTGRES_PASSWORD
    +
    POSTGRES_NON_ROOT_PASSWORD
  • 启动前请确认所有占位符已替换:执行
    grep REPLACE_WITH_ .env
    应无输出——若残留占位符,它会被当作明文密码,导致Postgres/n8n无法连接。
  • 执行
    chmod 600 .env
    修改文件权限。记录加密密钥,以便用户备份到设备外。

5. Firewall

5. 防火墙配置

  • ufw
    : allow OpenSSH + 80 + 443, then enable. Do not open 5678/5432/6379.
  • 使用
    ufw
    :允许OpenSSH + 80 + 443,然后启用防火墙。请勿开放5678/5432/6379端口。

6. Launch

6. 启动服务

  • cd <DATA_FOLDER> && docker compose up -d
    .
  • Queue mode brings up Redis + Postgres + main + workers (workers via
    replicas
    ). To add capacity:
    docker compose up -d --scale n8n-worker=N
    .
  • 执行
    cd <DATA_FOLDER> && docker compose up -d
  • 队列模式会启动Redis + Postgres + 主进程 + 工作节点(工作节点数量由
    replicas
    指定)。如需扩容:执行
    docker compose up -d --scale n8n-worker=N

7. Verify (don't declare success without this)

7. 验证(未完成验证请勿宣布部署成功)

  • docker compose ps
    — every service
    Up
    /healthy (queue: postgres & redis
    healthy
    first).
  • n8n itself up (internal):
    docker compose exec n8n wget -qO- http://localhost:5678/healthz
    {"status":"ok"}
    . This separates "n8n is running" from "TLS isn't ready yet."
  • Cert issued:
    docker compose logs caddy | grep -i 'certificate obtained'
    . First-boot ACME can take a minute or two; until it finishes, a public
    https://
    request fails TLS — that means the cert is still pending, not that n8n is down.
  • Public reachability (with retry):
    curl -fsS --retry 5 --retry-delay 10 https://<fqdn>/healthz
    {"status":"ok"}
    . (
    /healthz
    only proves the process is reachable;
    /healthz/readiness
    additionally confirms the DB is connected and migrated — use it when debugging a boot loop.)
  • Open
    https://<fqdn>
    → the owner setup screen. Whoever completes that signup form first claims the instance — an exposed un-owned instance is a race, so create the owner account immediately, before sharing the URL. Enable 2FA. (Automated deploys can pre-provision the owner via env vars instead — see the owner row in
    SECURITY.md
    .)
  • 执行
    docker compose ps
    ——所有服务状态应为
    Up
    /healthy(队列模式:postgres和redis需先变为
    healthy
    )。
  • n8n内部服务正常运行:执行
    docker compose exec n8n wget -qO- http://localhost:5678/healthz
    → 返回
    {"status":"ok"}
    。这可区分“n8n已运行”和“TLS尚未就绪”两种状态。
  • 证书已颁发:执行
    docker compose logs caddy | grep -i 'certificate obtained'
    。首次启动时ACME验证可能需要1-2分钟;在此之前,公网
    https://
    请求会TLS失败——这表示证书仍在申请中,并非n8n服务故障。
  • 公网可达性(带重试):执行
    curl -fsS --retry 5 --retry-delay 10 https://<fqdn>/healthz
    → 返回
    {"status":"ok"}
    。(
    /healthz
    仅验证进程可达;
    /healthz/readiness
    还会确认数据库已连接并完成迁移——调试启动循环时可使用该接口。)
  • 打开
    https://<fqdn>
    → 进入所有者设置界面。第一个完成注册表单的用户将获得实例所有权——暴露的未归属实例存在被抢占的风险,因此请立即创建所有者账户,再分享URL。启用双因素认证(2FA)。(自动化部署可通过环境变量预配置所有者账户——详见
    SECURITY.md
    中的所有者相关内容。)

8. Hand off

8. 交付

  • Give the user: the URL, where the project lives, the encryption key to store safely, and the Day-2 basics (update / backup / restore) from
    DAY2.md
    .
  • 向用户提供:访问URL、项目存储路径、需安全备份的加密密钥,以及**
    DAY2.md
    **中的日常运维基础知识(更新/备份/恢复)。

What NOT to do

禁止操作

  • Don't skip the DNS/ports preflight. A wrong A record or a closed cloud firewall is the #1 reason Caddy can't get a cert and n8n looks "broken."
  • Don't publish 5678/5432/6379 to the host. Caddy reaches n8n over the private network.
  • Don't reuse another instance's encryption key or
    .env
    .
    Fresh secrets per box.
  • Don't run queue mode on SQLite. Queue requires Postgres (the template already wires it).
  • Don't put secrets in
    docker-compose.yml
    or the Caddyfile.
    .env
    only.
  • Don't use
    :latest
    blindly.
    Pin
    N8N_IMAGE_TAG
    ; update deliberately (
    DAY2.md
    ).
  • 请勿跳过DNS/端口预检。A记录错误或云防火墙未开放是导致Caddy无法获取证书、n8n看似“故障”的头号原因。
  • 请勿将5678/5432/6379端口暴露到主机。Caddy通过私有网络访问n8n。
  • 请勿复用其他实例的加密密钥或
    .env
    文件
    。每个设备需使用全新密钥。
  • 请勿在SQLite上运行队列模式。队列模式需要Postgres(模板已配置)。
  • 请勿将密钥写入
    docker-compose.yml
    或Caddyfile
    。密钥仅能存放在
    .env
    中。
  • 请勿盲目使用
    :latest
    标签
    。请固定
    N8N_IMAGE_TAG
    ;按计划更新(详见
    DAY2.md
    )。

Reference files

参考文件

  • SINGLE_MODE.md
    — single-instance specifics, SQLite vs Postgres, when to graduate to queue.
  • QUEUE_MODE.md
    — queue architecture, workers/concurrency/scaling, shared encryption key, binary data (
    database
    mode — filesystem is unsupported in queue mode; S3/Azure = Enterprise), webhook processors, multi-main licensing.
  • SECURITY.md
    — generating secrets, the encryption-key rules, the full hardening checklist (telemetry off, env-access block, public API, firewall, secure cookies).
  • DAY2.md
    — updating the image, backing up (encryption key + volume + Postgres), and restoring.
  • assets/
    — the templates:
    docker-compose.single.yml
    ,
    docker-compose.queue.yml
    ,
    Caddyfile
    ,
    .env.single.example
    ,
    .env.queue.example
    ,
    init-data.sh
    .
Authoritative upstream reference: the official hosting docs live at https://docs.n8n.io/deploy/host-n8n (restructured mid-2026 from the old
/hosting/
paths — prefer these URLs). The env-var reference index is at https://docs.n8n.io/deploy/host-n8n/configure-n8n/basic-configuration/use-environment-variables. When this skill and the live docs disagree, trust the docs and tell the user.
  • SINGLE_MODE.md
    — 单实例细节、SQLite与Postgres对比、何时升级到队列模式。
  • QUEUE_MODE.md
    — 队列架构、工作节点/并发/扩展、共享加密密钥、二进制数据(
    database
    模式——队列模式不支持文件系统;S3/Azure为企业版功能)、webhook处理器、多主进程许可。
  • SECURITY.md
    — 密钥生成、加密密钥规则、完整加固清单(关闭遥测、阻止环境变量访问、公网API、防火墙、安全Cookie)。
  • DAY2.md
    — 镜像更新、备份(加密密钥 + 卷 + Postgres)、恢复操作。
  • assets/
    — 模板文件:
    docker-compose.single.yml
    docker-compose.queue.yml
    Caddyfile
    .env.single.example
    .env.queue.example
    init-data.sh
官方权威参考:官方托管文档位于https://docs.n8n.io/deploy/host-n8n(2026年年中从旧路径
/hosting/
重构——请优先使用此URL)。环境变量参考索引位于https://docs.n8n.io/deploy/host-n8n/configure-n8n/basic-configuration/use-environment-variables。若本技能与实时文档存在冲突,请以官方文档为准并告知用户。