InvenTree 1.6.1 Docker Compose 部署指南

InvenTree 1.6.1 官方五服务栈(PostgreSQL 17 + Redis 7 + Caddy)Compose 生产部署实测:镜像选型、数据持久化、初始化、销毁重建恢复闭环与排障实录。

InventreeInvenTree库存管理Docker Compose2026/10/23

InvenTree 1.6.1 Docker Compose 部署指南

目标:使用官方生产形态(inventree/inventree:1.6.1 + PostgreSQL 17 + Redis 7 + Caddy 反代 + django-q2 工作进程,五服务栈)以 Docker Compose 从零部署 InvenTree,覆盖镜像选型、数据持久化、初始化、销毁重建恢复闭环、升级与排障。全部命令与回显在 Ubuntu 22.04(x86_64,内核 6.8.0-138-generic)实测通过,实测时点 2026-10-23;官方 compose 基线取自仓库 1.6.1 tag 的 contrib/container/docker-compose.yml。

一、环境信息

  • 操作系统:Ubuntu 22.04(x86_64)
  • 内核:6.8.0-138-generic
  • Docker Engine:29.8.1;Docker Compose:v5.5.1(docker compose v2 子命令形态)
  • InvenTree 版本:1.6.1(2026-10-21 发布,官方最新稳定版,版本核实见 《InvenTree 1.6.1 介绍与选型指南》)
  • 部署目录:/opt/inventree(compose 项目文件与 .env 所在目录)
  • 数据目录:/opt/inventree/data(PostgreSQL 数据、静态/媒体文件、备份、Redis、Caddy 状态全部在内)
  • Web 端口:宿主机 8146(Caddy HTTP 发布端口)
  • compose 项目名:inventree-161-stack(显式声明,避免与其他 compose 项目同名冲突)
  • 栈内网络:inventree-161-stack_net,显式网段 10.89.68.0/24

二、镜像选型说明

2.1 官方五服务栈

InvenTree 官方生产 compose(1.6.1 tag 的 contrib/container/docker-compose.yml)定义了五个服务,本指南完全沿用该形态:

服务镜像角色实测镜像 ID / 体积(docker compose images)
inventree-dbpostgres:17PostgreSQL 数据库b8faeff77126 / 640MB
inventree-cacheredis:7-alpine缓存(官方显式关闭磁盘持久化,仅作缓存)57e4255cc971 / 57.3MB
inventree-serverinventree/inventree:1.6.1gunicorn Web 服务(API + 前端页面)7c1e251a6c52 / 2.07GB
inventree-workerinventree/inventree:1.6.1django-q2 后台任务工作进程(invoke worker)7c1e251a6c52 / 2.07GB
inventree-proxycaddy:alpine反向代理 + 静态/媒体文件分发 + TLSeb4d35b7452d / 92.8MB

选型解读:

  • inventree/inventree 是官方镜像(Docker Hub 官方账号 inventree),标签策略为 stable(最新稳定版)/latest(开发版)/具体版本号;生产锁定具体版本号 1.6.1,避免 stable 浮动升级。
  • server 与 worker 共用同一镜像:worker 只是启动命令不同(command: invoke worker),保证两个进程版本严格一致。
  • Redis 在官方 1.6.1 栈中定位是「缓存管理器」,compose 注释明确说明其缓存数据属临时性(--save "" --appendonly no),不承担持久化职责——真正的状态全在 PostgreSQL 与数据卷里。
  • Caddy 承担静态文件(/static/)与媒体文件(/media/)的直接分发(媒体文件默认强制 Content-Disposition: attachment 下载并经 Django 鉴权),这是官方栈把「应用进程」与「文件服务」分离的设计。

2.2 版本锁定方式

版本号由 .env 的 INVENTREE_TAG 控制,compose 中引用为 image: inventree/inventree:${INVENTREE_TAG:-stable}。本指南锁定 INVENTREE_TAG=1.6.1;升级时只改这个值(见第十章)。

三、前置条件

  1. 服务器已安装 Docker Engine 与 Docker Compose v2:
docker --version && docker compose version
Docker version 29.8.1, build 4a63305
Docker Compose version v5.5.1
  1. 端口未占用(本指南用 8146;如用默认 80/443 则跳过):
ss -ltn | grep 8146 || echo "port 8146 free"
port 8146 free
  1. 磁盘余量:四个镜像压缩拉取约 1.4GB、解压后约 2.9GB,加上数据库初始化与媒体文件,预留 8GB 以上。
  2. 可访问 Docker Hub(离线环境可先在联网机器 docker pull + docker save 迁入)。

四、部署步骤

4.1 创建部署与数据目录

sudo mkdir -p /opt/inventree/data
sudo chmod 777 /opt/inventree/data   # 首次启动由栈内进程写入,先放开权限
cd /opt/inventree

⚠️ 注意:INVENTREE_EXT_VOLUME 指向的目录会被 PostgreSQL(容器内 UID 999)、InvenTree、Caddy 多方写入。首次启动前放开权限是最省事的做法,初始化完成后可按 ls -l 结果收敛属主。

4.2 编写 .env

在 /opt/inventree 下创建 .env(变量集与官方 contrib/container/.env 模板一致,按本机实际调整):

sudo tee /opt/inventree/.env > /dev/null <<'EOF'
# compose 项目名(compose 文件内已显式 name,此处同步声明)
COMPOSE_PROJECT_NAME=inventree-161-stack

# InvenTree 版本标签(生产锁定具体版本号)
INVENTREE_TAG=1.6.1

# 站点对外访问地址(含端口,与用户实际访问 URL 一致)
INVENTREE_SITE_URL="http://192.168.146.128:8146"

# InvenTree 服务在栈内监听端口(官方默认 8000,不建议修改)
INVENTREE_WEB_PORT=8000

# Caddy 反代发布端口(宿主机与栈内一致,见第十一章排障说明)
INVENTREE_HTTP_PORT=8146

# 反代转发头
INVENTREE_USE_X_FORWARDED_HOST=True
INVENTREE_USE_X_FORWARDED_PORT=True
INVENTREE_USE_X_FORWARDED_PROTO=True

# 外部数据卷路径
INVENTREE_EXT_VOLUME=/opt/inventree/data

# 日志级别
INVENTREE_LOG_LEVEL=WARNING

# 启用插件
INVENTREE_PLUGINS_ENABLED=True

# 自动执行数据库迁移(不取代手动 invoke update)
INVENTREE_AUTO_UPDATE=True

# 首次启动自动创建管理员账号
INVENTREE_ADMIN_USER=admin
INVENTREE_ADMIN_EMAIL=admin@localhost
INVENTREE_ADMIN_PASSWORD=q1w2e3r4

# PostgreSQL 连接(指向栈内服务名 inventree-db)
INVENTREE_DB_ENGINE=postgresql
INVENTREE_DB_NAME=inventree
INVENTREE_DB_HOST=inventree-db
INVENTREE_DB_PORT=5432
INVENTREE_DB_USER=pguser
INVENTREE_DB_PASSWORD=q1w2e3r4

# Redis 缓存(指向栈内服务名 inventree-cache)
INVENTREE_CACHE_ENABLED=True
INVENTREE_CACHE_HOST=inventree-cache
INVENTREE_CACHE_PORT=6379

# gunicorn
INVENTREE_GUNICORN_TIMEOUT=90
EOF

关键项说明:

  • INVENTREE_SITE_URL:写用户实际访问的完整 URL(含端口)。它同时决定 Django 的 CSRF_TRUSTED_ORIGINS(源码 settings.py:if SITE_URL and SITE_URL not in CSRF_TRUSTED_ORIGINS: CSRF_TRUSTED_ORIGINS.append(SITE_URL),1.6.1 tag 实读)与 Caddy 的站点监听地址。
  • INVENTREE_DB_*:数据库凭据不是 InvenTree 登录账号,它们是 PostgreSQL 的连接凭据(官方 .env 注释原文)。演示环境用 pguser/q1w2e3r4,生产请换成强口令。
  • INVENTREE_ADMIN_*:三项齐全时首次启动自动创建超级管理员;缺任一项则不创建(源码 apps.py 校验 INVENTREE_ADMIN_USER/INVENTREE_ADMIN_EMAIL/INVENTREE_ADMIN_PASSWORD 三项齐备,1.6.1 tag 实读)。
  • INVENTREE_AUTO_UPDATE=True:启动时自动跑数据库迁移;官方注释说明它不取代手动 invoke update(静态文件收集、包安装等仍由 update 流程覆盖)。

4.3 编写 docker-compose.yml

以官方 contrib/container/docker-compose.yml(1.6.1 tag)为基线,做两处显式化调整:顶部声明项目名、末尾声明网络与网段。完整文件如下(官方文件本身注释丰富,此处保留服务定义主体):

sudo tee /opt/inventree/docker-compose.yml > /dev/null <<'EOF'
name: inventree-161-stack

services:
    inventree-db:
        image: postgres:17
        container_name: inventree-db
        expose:
            - ${INVENTREE_DB_PORT:-5432}/tcp
        environment:
            - PGDATA=/var/lib/postgresql/data/pgdb
            - POSTGRES_USER=${INVENTREE_DB_USER:?You must provide the 'INVENTREE_DB_USER' variable in the .env file}
            - POSTGRES_PASSWORD=${INVENTREE_DB_PASSWORD:?You must provide the 'INVENTREE_DB_PASSWORD' variable in the .env file}
            - POSTGRES_DB=${INVENTREE_DB_NAME:?You must provide the 'INVENTREE_DB_NAME' variable in the .env file}
        volumes:
            - ${INVENTREE_EXT_VOLUME:?You must specify the 'INVENTREE_EXT_VOLUME' variable in the .env file!}:/var/lib/postgresql/data/:z
        healthcheck:
            test: ['CMD-SHELL', 'pg_isready -U "$${POSTGRES_USER}" -d "$${POSTGRES_DB}"']
            interval: 15s
            timeout: 5s
            retries: 10
            start_period: 20s
        restart: unless-stopped

    inventree-cache:
        image: redis:7-alpine
        container_name: inventree-cache
        command: redis-server --save "" --appendonly no
        env_file:
            - .env
        expose:
            - ${INVENTREE_CACHE_PORT:-6379}
        volumes:
            - ${INVENTREE_EXT_VOLUME}/redis:/data
        healthcheck:
            test: ['CMD', 'redis-cli', 'ping']
            interval: 15s
            timeout: 5s
            retries: 10
            start_period: 10s
        restart: always

    inventree-server:
        image: inventree/inventree:${INVENTREE_TAG:-stable}
        container_name: inventree-server
        expose:
            - ${INVENTREE_WEB_PORT:-8000}
        depends_on:
            inventree-db:
                condition: service_healthy
            inventree-cache:
                condition: service_healthy
        env_file:
            - .env
        environment:
          INVENTREE_SERVER: http://inventree-server:${INVENTREE_WEB_PORT}
        volumes:
            - ${INVENTREE_EXT_VOLUME}:/home/inventree/data:z
        healthcheck:
            test:
                [
                    'CMD-SHELL',
                    'invoke server-health --address "http://localhost:$${INVENTREE_WEB_PORT:-8000}"',
                ]
            interval: 20s
            timeout: 5s
            retries: 10
            start_period: 60s
        restart: unless-stopped

    inventree-worker:
        image: inventree/inventree:${INVENTREE_TAG:-stable}
        container_name: inventree-worker
        command: invoke worker
        depends_on:
            inventree-server:
                condition: service_healthy
        env_file:
            - .env
        volumes:
            - ${INVENTREE_EXT_VOLUME}:/home/inventree/data:z
        healthcheck:
            test: ['CMD-SHELL', 'invoke worker-health']
            interval: 60s
            timeout: 10s
            retries: 3
            start_period: 180s
        restart: unless-stopped

    inventree-proxy:
        container_name: inventree-proxy
        image: caddy:alpine
        restart: always
        depends_on:
            inventree-server:
                condition: service_healthy
            inventree-worker:
                condition: service_healthy
        ports:
            - ${INVENTREE_HTTP_PORT:-80}:${INVENTREE_HTTP_PORT:-80}
        env_file:
            - .env
        healthcheck:
            test: ['CMD', 'wget', '--spider', '-q', 'http://127.0.0.1:9090/api/system/health/']
            interval: 20s
            timeout: 5s
            retries: 10
            start_period: 20s
        volumes:
            - ./Caddyfile:/etc/caddy/Caddyfile:ro,z
            - ${INVENTREE_EXT_VOLUME}/static:/var/www/static:z
            - ${INVENTREE_EXT_VOLUME}/media:/var/www/media:z
            - ${INVENTREE_EXT_VOLUME}:/var/log:z
            - ${INVENTREE_EXT_VOLUME}:/data:z
            - ${INVENTREE_EXT_VOLUME}:/config:z

networks:
    default:
        name: inventree-161-stack_net
        ipam:
            config:
                - subnet: 10.89.68.0/24
EOF

与官方原文件的差异(仅两处,其余逐行一致):

调整点官方原样本指南原因
项目名未声明(默认取目录名)顶部 name: inventree-161-stack多套栈共存时防止同名项目互删(down 只作用于本项目)
端口映射${INVENTREE_HTTP_PORT:-80}:80 与 ${INVENTREE_HTTPS_PORT:-443}:443${INVENTREE_HTTP_PORT:-80}:${INVENTREE_HTTP_PORT:-80}(单端口)INVENTREE_SITE_URL 含端口时 Caddy 在栈内监听同号端口而非 80(官方 Caddyfile 站点地址直接取 INVENTREE_SITE_URL),发布映射必须对齐,详见第十一章排障实录
网段未显式声明10.89.68.0/24与服务器上其他 compose 网络显式隔离,避免网段冲突

4.4 获取 Caddyfile

官方栈用 ./Caddyfile 相对挂载,需把它与 compose 文件放在一起。两种方式任选:

# 方式一:从官方仓库 1.6.1 tag 下载(联网服务器)
sudo wget -O /opt/inventree/Caddyfile https://raw.githubusercontent.com/inventree/InvenTree/1.6.1/contrib/container/Caddyfile
# 方式二:从本机已克隆的源码复制
sudo cp contrib/container/Caddyfile /opt/inventree/Caddyfile

Caddyfile 的职责(官方文件实读):/static/、/media/ 由 Caddy 直接分发(媒体强制 attachment 下载 + 经 Django /auth/ 鉴权),其余请求反代到 inventree-server:8000;另有内部 :9090 健康检查口。

4.5 首次启动

cd /opt/inventree && docker compose up -d

首次启动会依次拉取四个镜像、按依赖顺序拉起服务(输出节选,实测):

 Image postgres:17 Pulling
 Image redis:7-alpine Pulling
 Image inventree/inventree:1.6.1 Pulling
 Image caddy:alpine Pulling
 Image redis:7-alpine Pulled
 Image postgres:17 Pulled
 Image inventree/inventree:1.6.1 Pulled
 Image caddy:alpine Pulled
 Network inventree-161-stack_net Creating
 Network inventree-161-stack_net Created
 Container inventree-db Created
 Container inventree-cache Created
 Container inventree-server Created
 Container inventree-worker Created
 Container inventree-proxy Created
 Container inventree-cache Starting
 Container inventree-db Starting
 Container inventree-cache Started
 Container inventree-db Started
 Container inventree-db Waiting
 Container inventree-cache Waiting
 Container inventree-cache Healthy
 Container inventree-db Healthy
 Container inventree-server Starting
 Container inventree-server Started
 Container inventree-server Waiting
 Container inventree-server Healthy
 Container inventree-worker Starting
 Container inventree-worker Started
 Container inventree-worker Waiting
 Container inventree-server Waiting
 Container inventree-server Healthy
 Container inventree-worker Healthy
 Container inventree-proxy Starting
 Container inventree-proxy Started

启动顺序由 healthcheck 强约束:db/cache 健康 → server 启动(执行首次初始化)→ server 健康 → worker 启动 → proxy 启动。首次初始化包含 281 条数据库迁移(docker compose logs inventree-server | grep -c Applying 实测 281)、config.yaml 模板复制、静态文件目录创建,约 1~2 分钟。

server 首启日志开头(实测节选):

inventree-server  | Creating directory /home/inventree/data/static
inventree-server  | Creating directory /home/inventree/data/media
inventree-server  | Creating directory /home/inventree/data/backup
inventree-server  | Copying config file from /home/inventree/src/backend/InvenTree/config_template.yml to /home/inventree/data/config.yaml
inventree-server  | INVE-W3: Could not detect git information.
inventree-server  | 2026-10-23 09:24:34,985 WARNING Plugin configuration file does not exist - creating default file

4.6 状态确认与访问验证

cd /opt/inventree && docker compose ps
NAME               SERVICE            STATUS
inventree-cache    inventree-cache    Up 2 minutes (healthy)
inventree-db       inventree-db       Up 2 minutes (healthy)
inventree-proxy    inventree-proxy    Up About a minute (healthy)
inventree-server   inventree-server   Up 2 minutes (healthy)
inventree-worker   inventree-worker   Up About a minute (healthy)

五个服务全部 healthy 后,逐层验证:

curl -s http://192.168.146.128:8146/api/system/health/
{"status": "ok"}
curl -s -o /dev/null -w '%{http_code}\n' http://192.168.146.128:8146/
302
  • 健康接口返回 {"status": "ok"};首页 302 跳转到前端入口。
  • 浏览器打开 http://192.168.146.128:8146 即进入 InvenTree 界面,用 .env 里配置的 admin / q1w2e3r4 登录。

验证管理员账号确实已自动创建(Basic 认证实测):

curl -s -u admin:q1w2e3r4 http://192.168.146.128:8146/api/user/me/ | head -c 120
{"pk":1,"username":"admin","first_name":"","last_name":"","email":"admin@localhost","groups":[],"is_staff":true,"is_supe

五、数据持久化说明

5.1 持久化设计

全部状态都落在 INVENTREE_EXT_VOLUME 指向的宿主机目录 /opt/inventree/data,五个服务的数据卷映射:

服务栈内路径宿主机路径内容
inventree-db/var/lib/postgresql/data//opt/inventree/data/pgdb/PostgreSQL 全部数据(PGDATA 指向 pgdb/ 子目录)
inventree-server / worker/home/inventree/data/opt/inventree/data配置、静态/媒体文件、备份、插件清单
inventree-proxy/var/www/static、/var/www/media、/var/log、/data、/config同上各子目录静态文件分发、媒体分发、Caddy 日志与状态

5.2 数据目录清单(部署并初始化后实测)

ls -la /opt/inventree/data
total 60
drwxrwxrwx  9 root root  4096 Oct 23 22:25 .
drwxr-xr-x  5 root root  4096 Oct 23 22:36 ..
drwxr-xr-x  2 root root  4096 Oct 23 22:24 backup
drwx------  3 root root  4096 Oct 23 22:25 caddy
-rw-r--r--  1 root root 10201 Oct 23 22:24 config.yaml
drwxr-xr-x  3 root root  4096 Oct 23 22:25 media
-rw-r--r--  1 root root  3243 Oct 23 22:24 oidc.pem
drwx------ 19  999 root  4096 Oct 23 22:30 pgdb
drwxr-xr-x  3 root root  4096 Oct 23 22:24 plugins
-rw-r--r--  1 root root    53 Oct 23 22:24 plugins.txt
drwxr-xr-x  2  999 root  4096 Oct 23 22:24 redis
-rw-r--r--  1 root root    50 Oct 23 22:24 secret_key.txt
drwxr-xr-x  2 root root  4096 Oct 23 22:24 static
du -sh /opt/inventree/data/* 2>/dev/null
4.0K    backup
32K     caddy
12K     config.yaml
88K     media
4.0K    oidc.pem
71M     pgdb
16K     plugins
4.0K    plugins.txt
4.0K    redis
4.0K    secret_key.txt
4.0K    static

关键文件/目录解读:

  • pgdb/:PostgreSQL 数据目录(属主 UID 999),整个系统最核心的数据。
  • config.yaml:首次启动由镜像内模板自动复制生成(见 4.5 节日志),是 InvenTree 的主配置文件,改它 + 重启即生效;逐节详解见本专栏配置篇。
  • secret_key.txt:Django SECRET_KEY,首次启动自动生成——丢失它会导致会话/令牌全部失效,务必纳入备份。
  • backup/:invoke backup 的默认备份落点(官方 config 模板 backup_dir 指向 /home/inventree/data/backup)。
  • media/、static/:上传附件与前端静态资源;plugins/、plugins.txt:插件目录与清单;caddy/、redis/:两个附属服务的状态。

5.3 备份要点

  • 数据库:docker compose exec inventree-db pg_dump -U pguser inventree > inventree-$(date +%F).sql,或用 InvenTree 自带 invoke backup(落 backup/ 目录,用法见本专栏命令篇)。
  • 配置与凭据:config.yaml、secret_key.txt、.env 三个文本文件单独留份。

六、环境变量表

.env 全量变量(含官方默认值)速查(来源:官方 contrib/container/.env 模板 + compose 文件默认值,1.6.1 tag 实读;实测部署值见 4.2 节):

变量默认值说明
INVENTREE_TAGstable镜像版本标签;生产建议锁具体版本(如 1.6.1)
INVENTREE_SITE_URLhttp://localhost对外访问 URL;决定 CSRF 白名单与 Caddy 监听地址
INVENTREE_WEB_PORT8000server 栈内监听端口,勿改
INVENTREE_HTTP_PORT80Caddy HTTP 发布端口
INVENTREE_HTTPS_PORT443Caddy HTTPS 发布端口(SITE_URL 用 https:// 时 Caddy 自动启用 Automatic HTTPS)
INVENTREE_USE_X_FORWARDED_HOST/PORT/PROTOTrue反代转发头开关
INVENTREE_EXT_VOLUME./inventree-data外部数据卷路径
INVENTREE_LOG_LEVELWARNING日志级别
INVENTREE_PLUGINS_ENABLEDFalse是否启用插件(官方 .env 模板示例置 True)
INVENTREE_AUTO_UPDATEFalse启动时自动迁移
INVENTREE_ADMIN_USER/EMAIL/PASSWORD注释态首启自动建管理员,三项须齐备
INVENTREE_DB_ENGINEpostgresql数据库引擎(栈内固定 postgresql)
INVENTREE_DB_NAMEinventree数据库名
INVENTREE_DB_HOSTinventree-db数据库地址(栈内服务名)
INVENTREE_DB_PORT5432数据库端口
INVENTREE_DB_USER/PASSWORDpguser/pgpassword数据库凭据(非登录账号)
INVENTREE_CACHE_ENABLEDTrue启用 Redis 缓存
INVENTREE_CACHE_HOST/PORTinventree-cache/6379缓存地址端口
INVENTREE_GUNICORN_TIMEOUT90gunicorn 请求超时(秒)
INVENTREE_GUNICORN_WORKERSCPU 核数×2+1gunicorn 进程数(gunicorn.conf.py 实读:未设置时按 CPU 自动计算)
COMPOSE_PROJECT_NAMEinventreecompose 项目名

💡 说明:除上表外,InvenTree 全部 INVENTREE_* 配置项(数据库、邮件、SSO、后台线程数等)同样可通过 .env 注入并优先于 config.yaml,完整覆盖机制见本专栏配置篇。

七、配置参数说明

栈内三个关键配置面的分工:

  • .env(本指南 4.2 节):compose 栈自身的编排参数(端口、路径、凭据)+ InvenTree 应用配置的环境变量形态。改动后需要 docker compose up -d 重建对应服务生效。
  • config.yaml(/opt/inventree/data/config.yaml):InvenTree 应用主配置,首启自动生成。改文件后重启 server/worker 生效;优先级低于 INVENTREE_* 环境变量。
  • Caddyfile(/opt/inventree/Caddyfile):反代与文件服务行为,改动后 docker compose restart inventree-proxy 生效。

八、配置文件挂载

8.1 config.yaml 挂载

config.yaml 无需在 compose 里显式挂载:它位于数据卷内(/home/inventree/data/config.yaml),server 与 worker 共享同一数据卷,天然同步。直接编辑宿主机 /opt/inventree/data/config.yaml 后:

cd /opt/inventree && docker compose restart inventree-server inventree-worker
 Restarting inventree-server ... done
 Restarting inventree-worker ... done

8.2 Caddyfile 挂载

compose 已声明 ./Caddyfile:/etc/caddy/Caddyfile:ro,z。需要自定义反代规则(如外接 TLS、加访问控制)时编辑宿主机文件后:

cd /opt/inventree && docker compose restart inventree-proxy

九、等价 docker run(单进程理解用)

compose 栈的等效单进程形态(仅帮助理解各服务角色,生产请用 compose——依赖健康检查与共享数据卷,手工 docker run 难以复现启动顺序):

# 数据库
docker run -d --name inventree-db \
  -v /opt/inventree/data:/var/lib/postgresql/data/ \
  -e PGDATA=/var/lib/postgresql/data/pgdb \
  -e POSTGRES_USER=pguser -e POSTGRES_PASSWORD=q1w2e3r4 -e POSTGRES_DB=inventree \
  postgres:17

# InvenTree Web 服务(环境变量取自 .env,此处节选核心项)
docker run -d --name inventree-server \
  -v /opt/inventree/data:/home/inventree/data \
  -e INVENTREE_DB_ENGINE=postgresql -e INVENTREE_DB_HOST=inventree-db \
  -e INVENTREE_DB_USER=pguser -e INVENTREE_DB_PASSWORD=q1w2e3r4 \
  -e INVENTREE_SITE_URL="http://192.168.146.128:8146" \
  inventree/inventree:1.6.1

# 后台工作进程(同一镜像,换启动命令)
docker run -d --name inventree-worker \
  -v /opt/inventree/data:/home/inventree/data \
  inventree/inventree:1.6.1 invoke worker

⚠️ 注意:INVENTREE_EXT_VOLUME 挂载到 server 的 /home/inventree/data 是官方镜像的硬约定(compose 注释原文「Data volume must map to /home/inventree/data」),换路径会导致配置与媒体文件丢失。

十、常用运维命令

以下命令均在部署目录内执行(cd /opt/inventree):

docker compose ps                       # 栈状态(五服务 healthy 为正常态)
docker compose logs -f inventree-server # 跟随 Web 服务日志(-n 200 只看最近 200 行)
docker compose stop                     # 停栈(保留容器)
docker compose start                    # 起栈
docker compose restart inventree-server # 单独重启某服务
docker compose down                     # 销毁容器与网络(数据卷不受影响)
docker compose up -d                    # 拉起/重建
docker compose exec inventree-server invoke version   # 查看运行版本

版本与镜像信息实测:

docker compose exec inventree-server invoke version
InvenTree Version Info
InvenTree Version:        1.6.1
...

(完整输出与逐项解读见本专栏命令篇。)

升级流程(以升级到未来版本为例):

cd /opt/inventree
sed -i 's/^INVENTREE_TAG=.*/INVENTREE_TAG=<新版本号>/' .env   # 改版本标签
docker compose pull        # 拉新镜像
docker compose up -d       # 重建;server 启动时自动跑新增迁移(INVENTREE_AUTO_UPDATE=True)

回滚:把 INVENTREE_TAG 改回旧版本号,docker compose up -d 即回退;注意 1.6.0 起数据库迁移有压缩合并(官方 CHANGELOG #12830:1.0.0 之前版本必须先升到 1.0.0),跨大版本回滚前先核对官方升级说明,数据库层面回滚需恢复备份。

十一、销毁重建与恢复闭环(实测)

本节为完整实测闭环:造一条数据 → 销毁栈 → 重建 → 验证数据完好。

第一步,经 API 创建一条测试零件(Basic 认证):

curl -s -u admin:q1w2e3r4 -X POST -H 'Content-Type: application/json' \
  -d '{"name":"R-10K-0603","description":"10kOhm 0605 resistor","active":true}' \
  http://192.168.146.128:8146/api/part/ | head -c 200
{"active":true,"assembly":false,"barcode_hash":"","category":null,"category_detail":null,"category_path":null,"component":true,"creation_date":"2026-10-23","creation_user":1,"default_expiry":0,"default_location":null,"default_location_detail":null,"description":"10kOhm 0605 resistor","full_name":"R-10K-0603","image":null,"IPN":"","is_template":false,"keywords":null,"link":null,"locked":false,"minimum_stock":0.0,"maxi

第二步,销毁栈:

docker compose down
 Container inventree-proxy Stopping
 Container inventree-proxy Stopped
 Container inventree-proxy Removing
 Container inventree-proxy Removed
 Container inventree-worker Stopping
 Container inventree-worker Stopped
 Container inventree-worker Removing
 Container inventree-worker Removed
 Container inventree-server Stopping
 Container inventree-server Stopped
 Container inventree-server Removing
 Container inventree-server Removed
 Container inventree-cache Stopping
 Container inventree-db Stopping
 Container inventree-db Stopped
 Container inventree-db Removing
 Container inventree-cache Removed
 Container inventree-cache Removed
 Network inventree-161-stack_net Removing
 Network inventree-161-stack_net Removed
docker compose ps -a
NAME      STATUS

容器与网络已全部清除,ps -a 无残留;/opt/inventree/data 不受 down 影响(bind mount 设计目的正是如此)。

第三步,重建栈:

docker compose up -d && sleep 75 && docker compose ps
NAME               SERVICE            STATUS
inventree-cache    inventree-cache    Up About a minute (healthy)
inventree-db       inventree-db       Up About a minute (healthy)
inventree-proxy    inventree-proxy    Up About a minute (healthy)
inventree-server   inventree-server   Up About a minute (healthy)
inventree-worker   inventree-worker   Up About a minute (healthy)

第四步,恢复验证——账号与业务数据完好:

curl -s -u admin:q1w2e3r4 http://192.168.146.128:8146/api/user/me/ | head -c 120
{"pk":1,"username":"admin","first_name":"","last_name":"","email":"admin@localhost","groups":[],"is_staff":true,"is_supe
curl -s -u admin:q1w2e3r4 'http://192.168.146.128:8146/api/part/?search=R-10K' \
  | python3 -c "import json,sys; d=json.load(sys.stdin); print(json.dumps([{k: p[k] for k in ('pk','full_name','description')} for p in d], ensure_ascii=False))"
[{"pk": 1, "full_name": "R-10K-0603", "description": "10kOhm 0605 resistor"}]

结论:down + up -d 后管理员账号与零件数据完好,恢复闭环成立。数据库迁移不重跑(库结构已在 pgdb/ 中),重建耗时约 1 分钟。

十二、常见问题(排障实录)

12.1 访问 8146 端口返回 200 但内容为空(或直接拒连)

现象:栈五服务全部 healthy,但 curl http://localhost:8146/api/system/health/ 返回 HTTP/1.1 200 OK 且 Content-Length: 0(改用官方原版 compose 时甚至直接连接拒绝)。

定位:Caddy 日志显示其站点监听地址直接取自 INVENTREE_SITE_URL:

{"level":"warn",...,"msg":"HTTP/2 skipped because it requires TLS","network":"tcp","addr":":8146"}
{"level":"info","logger":"http.log","msg":"server running","name":"srv0","protocols":["h1","h2","h3"]}

官方 Caddyfile 的站点地址是 {$INVENTREE_SITE_URL:...}:当 INVENTREE_SITE_URL="http://192.168.146.128:8146"(含端口)时,Caddy 在栈内监听 8146 端口;而官方 compose 的发布映射是 ${INVENTREE_HTTP_PORT:-80}:80(宿主机 8146 → 栈内 80),两端口号错位,请求落空。

解决:把发布映射两端对齐——compose 的 ports 改为 ${INVENTREE_HTTP_PORT:-80}:${INVENTREE_HTTP_PORT:-80}(即 4.3 节文件),docker compose up -d 重建后验证:

curl -s http://192.168.146.128:8146/api/system/health/
{"status": "ok"}

同理注意 Host 匹配:Caddy 按站点地址匹配请求 Host。用 http://localhost:8146 访问时 Host 为 localhost,与站点地址 192.168.146.128 不匹配,Caddy 返回空的 200——请始终用 INVENTREE_SITE_URL 配置的地址访问,或在 Caddyfile 中追加匹配地址。

12.2 创建网络报 Pool overlaps

现象:docker compose up -d 报 Error response from daemon: invalid pool request: Pool overlaps with other one on this address space。

定位:显式声明的网段(10.89.68.0/24)与服务器上现存 Docker 网络重叠。

解决:docker network ls + docker network inspect 排查已占网段,换一个空闲网段写入 compose 的 networks.default.ipam.config.subnet 后重建。多套 compose 栈共存的服务器建议统一做网段分配规划。

12.3 管理员账号未自动创建

现象:首启后用 admin 登录被拒。

定位:INVENTREE_ADMIN_USER/INVENTREE_ADMIN_EMAIL/INVENTREE_ADMIN_PASSWORD 三项必须同时存在才会自动建号(源码 apps.py 缺一即跳过创建并告警)。

解决:补齐三项后重建 server;或手动建号:docker compose exec inventree-server invoke superuser(交互式,用法见本专栏命令篇)。

12.4 数据目录权限问题导致首启失败

现象:server 日志报写入 /home/inventree/data/... 失败。

定位:INVENTREE_EXT_VOLUME 指向的宿主机目录对栈内进程(PostgreSQL UID 999、InvenTree)不可写。

解决:按 4.1 节放开目录权限(chmod 777)或提前 chown 999:999 数据目录。

十三、参考链接