Inboxen 2026.2.22.0.27.49 Docker Compose 部署指南

Inboxen 2026.2.22.0.27.49 官方无镜像、Docker Compose 自建镜像部署实测:多阶段构建 Web 镜像、五服务栈编排、Postfix 收信网关 chroot 与 transport 排障、telnet SMTP 端到端投信验证。

InboxenInboxen邮件服务2026/10/26

Inboxen 2026.2.22.0.27.49 Docker Compose 部署指南

目标:用 Docker Compose 一键拉起 Inboxen 2026.2.22.0.27.49 的完整服务栈(Web + PostgreSQL + Redis + Nginx + Postfix 收信链路),完成首次启动、管理员登录、收件箱创建与 telnet SMTP 端到端投信验证。本文全部命令与输出均为实测记录。

一、环境信息

项目值
宿主操作系统Debian GNU/Linux 12.15 (bookworm),内核 6.1.0-37-amd64
DockerEngine 29.1.3 + Compose v5.6.1
Inboxen 版本2026.2.22.0.27.49(Codeberg 官方 PyPI 索引最新包)
基础镜像python:3.12-slim(Web)、debian:12-slim(Postfix)
组件镜像postgres:16-alpine、redis:7-alpine、nginx:alpine
Compose 项目名inboxen-2026.2.22-stack(name: 显式声明)
宿主端口8131 → Nginx 80(Web 入口)、8132 → Postfix 25(SMTP 入口)
收信域名inboxen.lan
管理员账号admin / q1w2e3r4
实测日期2026-10-26

💡 端口说明:Inboxen 常规部署约定 Web 走 8128、SMTP 走 8129;本实验宿主机上这两个端口已被其它进程占用,故实测改用 8131/8132。读者环境空闲时可直接改回,仅影响 compose.yaml 里两处 ports: 映射。

二、官方镜像实查结论与自建方案

动手前先确认「有没有官方镜像可拉」。2026-10-26 实查结论:

  1. 官方没有发布任何容器镜像:官方代码仓(https://codeberg.org/Inboxen/Inboxen)仓库内无 Dockerfile、无 compose 编排;README 的安装路径只有源码与 pip 包两种;官方文档 Getting Started / The Setting File 两篇均未提及容器部署。
  2. Docker Hub 上的 crazymax/inboxen 是第三方社区镜像,且最后推送停留在 2019-07-15(deploy-20190609 标签,Docker Hub API 实查),与当前版本 2026.2.22.0.27.49 相差近七年,不可用。

因此本文自建镜像:Web 服务用多阶段构建(builder 阶段装编译工具与 Inboxen 包,final 阶段只带运行时依赖),Postfix 收信网关用 Debian 官方包。整栈五个服务全部显式声明在一份 compose.yaml 中。

💡 技术拓展:为什么 Web 镜像要选 python:3.12-slim 而不是 alpine?Inboxen 依赖 psycopg2(PostgreSQL 适配),该包在 PyPI 上以 C 扩展 sdist 形式发布(实测 PyPI 无 cp312 预编译 wheel),安装时需要 gcc + PostgreSQL 开发头文件现场编译;musl libc(alpine)下编译链路差异大、坑多,Debian slim 基因与本文 B1 篇《Debian 12 安装 Inboxen 2026.2.22.0.27.49 指南》实测环境一致,排障结论可直接复用。

三、栈架构与工作目录

五个服务的协作关系(与物理机部署完全同构,只是每块职责各占一个服务):

外部发件方
   │  SMTP (宿主 8132 → postfix:25)
   ▼
postfix 服务(inboxen-2026222-stack-postfix)
   │  transport 表: inboxen.lan → smtp:[web]:8823
   ▼
web 服务(inboxen-2026222-stack-web)
   ├─ Gunicorn :8000          ← Django Web 应用(inboxen.wsgi)
   ├─ Salmon :8823            ← SMTP 收信管道(inboxen.router)
   ├─ Celery worker + beat    ← 异步任务(渲染/搜索/清理)
   │      │
   ▼          ▼
postgres 服务   redis 服务
(业务数据)    (Celery broker)
   ▲
   │  HTTP (宿主 8131 → nginx:80 → web:8000)
nginx 服务(反代 + /static/ 静态资源)

工作目录组织(本实验位于 /opt/inboxen-compose/,读者可任选):

/opt/inboxen-compose/
├── compose.yaml          # 栈编排:5 服务 + 4 卷
├── Dockerfile            # Web 镜像(多阶段构建)
├── settings_prod.py      # Django settings 覆盖模块(Salmon 监听地址)
├── entrypoint.sh         # web 首启初始化 + 进程编排
├── inboxen.config        # Inboxen 主配置(挂载进 web)
├── nginx.conf            # Nginx 反代配置
└── postfix/
    ├── Dockerfile        # Postfix 网关镜像
    └── start.sh          # Postfix 收信域/transport 启动脚本

四、Web 镜像:Dockerfile 与配套文件

4.1 Dockerfile(多阶段构建)

# syntax=docker/dockerfile:1
# 多阶段构建:builder 阶段装编译工具并安装 Inboxen,final 阶段只带运行时依赖
FROM python:3.12-slim AS builder

# gcc 编译 psycopg2(PyPI 上为纯 sdist,无预编译 wheel)需要三件套:
# gcc + libpq-dev(pg_config/头文件)+ libc6-dev(gcc 的 Recommends 依赖,
# --no-install-recommends 下必须显式装,否则报 stdlib.h: No such file or directory)
RUN apt-get update \
 && apt-get install -y --no-install-recommends gcc libpq-dev libc6-dev \
 && rm -rf /var/lib/apt/lists/*

# Inboxen 官方包发布在项目自建的 Codeberg PyPI 索引,依赖来自公共 PyPI,双源安装
RUN pip install --prefix=/install "inboxen==2026.2.22.0.27.49" \
      --index-url https://pypi.org/simple \
      --extra-index-url https://codeberg.org/api/packages/inboxen/pypi/simple/ \
 && pip install --prefix=/install setuptools redis gunicorn django-debug-toolbar

FROM python:3.12-slim

RUN apt-get update \
 && apt-get install -y --no-install-recommends libpq5 \
 && rm -rf /var/lib/apt/lists/*

COPY --from=builder /install /usr/local

WORKDIR /app
COPY settings_prod.py entrypoint.sh /app/
RUN chmod +x /app/entrypoint.sh

# DJANGO_SETTINGS_MODULE 指向覆盖模块(官方 settings.py 头部注释认可的定制方式):
# 仅把 Salmon 收信监听地址从 localhost 改为 0.0.0.0,供栈内 Postfix 服务转发
ENV DJANGO_SETTINGS_MODULE=settings_prod \
    SALMON_SETTINGS_MODULE=inboxen.router.config.settings \
    PYTHONPATH=/app

EXPOSE 8000 8823
ENTRYPOINT ["/app/entrypoint.sh"]

三个实测要点:

  1. libc6-dev 必须显式安装:--no-install-recommends 会跳过 gcc 的 Recommends 依赖,实测首次构建在编译 psycopg2 时报 fatal error: stdlib.h: No such file or directory,补上后一次通过。
  2. 四个补装包各有用途:setuptools(Python 3.12 无 distutils,依赖链 django-bootstrap-form 需要)、redis(Celery Redis broker 传输包,缺了 worker 起不来)、gunicorn(WSGI 服务器)、django-debug-toolbar(本教程 HTTP 模式下 debug 模式必需)。
  3. 双源 pip 安装:Inboxen 官方包在 Codeberg 自建 PyPI 索引,依赖在公共 PyPI,缺一不可。

4.2 settings_prod.py:让 Salmon 监听对外网卡

Inboxen 默认 SALMON_SERVER = {"host": "localhost", "port": 8823},单机物理部署够用;compose 栈里 Postfix 在另一个服务里,必须监听 0.0.0.0。官方 settings.py 头部注释明确支持「导入后覆盖」的定制方式:

# 按 Inboxen 官方 settings.py 头部注释建议:导入默认 settings 后按需覆盖。
# 本模块只做一件事:让 Salmon SMTP 收信进程监听 0.0.0.0:8823(默认 localhost,
# 在单机部署里足够;compose 栈中 Postfix 在另一个服务里,必须监听对外网卡)。
from inboxen.settings import *  # noqa

SALMON_SERVER = {"host": "0.0.0.0", "port": 8823, "type": "smtp"}

生效验证(入栈后执行,实测回显):

from django.conf import settings
print(settings.SALMON_SERVER)
# {'host': '0.0.0.0', 'port': 8823, 'type': 'smtp'}

4.3 entrypoint.sh:首启初始化与进程编排

#!/bin/sh
set -e
cd /app

echo "== [1/3] 初始化数据库与静态资源 =="
inboxen migrate
inboxen collectstatic --noinput

echo "== [2/3] 引导管理员与收信域名(幂等,已存在则跳过) =="
DJANGO_SUPERUSER_PASSWORD=q1w2e3r4 inboxen createsuperuser --noinput \
  --username admin --email admin@inboxen.lan || echo "superuser admin 已存在,跳过"
inboxen createdomain inboxen.lan || echo "domain inboxen.lan 已存在,跳过"

echo "== [3/3] 启动 Celery worker / beat / Salmon,随后前台运行 Gunicorn =="
mkdir -p run logs
celery -A inboxen worker -l warning --events -Ofair -f logs/celery-worker.log &
celery -A inboxen beat -l warning -f logs/celery-beat.log &
# 放后台启动:salmon 的 daemonize 行为随运行环境不稳定,后台方式保证不阻塞 gunicorn
salmon start --pid run/router.pid --boot inboxen.router.config.boot &
sleep 3

exec gunicorn inboxen.wsgi -b 0.0.0.0:8000 --workers 2 --access-logfile - --error-logfile -

⚠️ 实测踩坑:salmon start 阻塞入口脚本。Salmon 的 --daemon/--no-daemon 虽默认 daemon,但实测在同一命令行下其 daemonize 行为随环境不稳定——本栈首次启动时入口脚本停在 salmon start 一行、Gunicorn 始终未拉起(Nginx 返回 502)。改为后台启动(&)后无论其是否内部守护都不阻塞,问题消失。这是自建镜像编排 Inboxen 的关键一坑。

五、Inboxen 主配置 inboxen.config

pip 包形态下 inboxen 命令以工作目录下的 inboxen.config 为配置源,本栈把它以只读卷挂进 web 服务的 /app:

debug: true
secret_key: "b2-compose-experiment-026-1nboxen-key"
database:
    host: "postgres"
    name: "inboxen"
    user: "inboxen"
    password: "q1w2e3r4"
    port: "5432"
tasks:
    broker_url: "redis://redis:6379/0"
enable_registration: true
enable_inbox_creation: true
allowed_hosts:
    - "*"
cache:
    location: "/app/cache"
字段本栈取值说明
database.hostpostgrescompose 服务名即 DNS 名,服务间互访直接用服务名
tasks.broker_urlredis://redis:6379/0同上;单机栈用 Redis 兼任 broker 最简
debugtrue生产模式内置 SECURE_SSL_REDIRECT=True,纯 HTTP 入口会 301 跳 https 导致打不开站点;实验栈保持 true,生产请置 false 并上 TLS
cache.location/app/cache(绝对路径)默认值是相对路径,Django 启动告警 caches.W003;挂了命名卷后写绝对路径消除告警
allowed_hosts["*"]仅限实验;生产写实际域名

六、Postfix 收信网关镜像

6.1 postfix/Dockerfile

FROM debian:12-slim

RUN apt-get update \
 && DEBIAN_FRONTEND=noninteractive apt-get install -y --no-install-recommends postfix \
 && rm -rf /var/lib/apt/lists/*

COPY start.sh /start.sh
RUN chmod +x /start.sh

EXPOSE 25
ENTRYPOINT ["/start.sh"]

6.2 postfix/start.sh

#!/bin/sh
set -e

# 收信域:inboxen.lan(允许任意外部来源投往该域,缺省会被 554 Relay access denied 拒收)
postconf -e "relay_domains = ${RELAY_DOMAINS}"

# transport 表:该域全部转交 web 服务的 Salmon 收信进程(8823 端口)。
# 注意 [web] 必须加方括号:不带括号 Postfix 会按 MX 查询主机名,容器内必失败
printf '%s smtp:[web]:8823\n' "${RELAY_DOMAINS}" > /etc/postfix/transport
postmap /etc/postfix/transport
postconf -e "transport_maps = hash:/etc/postfix/transport"

# 日志直写 stdout,docker compose logs postfix 即可排障(Postfix >= 3.4 支持)
postconf -e "maillog_file = /dev/stdout"

# Debian 的 Postfix smtp 投递进程默认 chroot 到 /var/spool/postfix 运行,
# 必须把 DNS/名称解析配置拷进 chroot,否则 transport 目标主机名解析失败(deferred 4.4.3)
mkdir -p /var/spool/postfix/etc
cp /etc/resolv.conf /etc/services /var/spool/postfix/etc/
[ -f /etc/nsswitch.conf ] && cp /etc/nsswitch.conf /var/spool/postfix/etc/

exec postfix start-fg

两个实测踩坑都集中在这份脚本里:

  1. transport 目标必须写 smtp:[web]:8823(带方括号)。实测写成 smtp:web:8823 时 Postfix 对 web 做 MX 查询,投递报 status=deferred (Name service error for name=web type=MX: Host not found);加方括号后走 A/AAAA 直查。
  2. Debian Postfix 的 chroot DNS 坑。即使加了方括号,实测仍报 type=AAAA: Host not found——根因是 Debian 的 smtp 投递进程默认 chroot 到 /var/spool/postfix,而 Docker 注入的 /etc/resolv.conf(nameserver 127.0.0.11)没有同步进 chroot。补 cp /etc/resolv.conf ... /var/spool/postfix/etc/ 后立刻恢复。修正后的投递日志:
postfix-1  | Oct 26 06:13:05 localhost postfix/smtp[112]: 04016208745: to=<fmcvdl@inboxen.lan>, relay=web[172.19.0.4]:8823, delay=3.1, delays=3/0.01/0.04/0.04, dsn=2.0.0, status=sent (250 OK)

七、compose.yaml 栈编排

name: inboxen-2026.2.22-stack

services:
  postgres:
    image: postgres:16-alpine
    environment:
      POSTGRES_DB: inboxen
      POSTGRES_USER: inboxen
      POSTGRES_PASSWORD: q1w2e3r4
    volumes:
      - pgdata:/var/lib/postgresql/data
    healthcheck:
      test: ["CMD-SHELL", "pg_isready -U inboxen -d inboxen"]
      interval: 5s
      timeout: 3s
      retries: 20

  redis:
    image: redis:7-alpine
    healthcheck:
      test: ["CMD", "redis-cli", "ping"]
      interval: 5s
      timeout: 3s
      retries: 20

  web:
    build: .
    volumes:
      - ./inboxen.config:/app/inboxen.config:ro
      - static_content:/app/static_content
      - inboxen_media:/app/media_content
      - inboxen_cache:/app/cache
    depends_on:
      postgres:
        condition: service_healthy
      redis:
        condition: service_healthy

  nginx:
    image: nginx:alpine
    volumes:
      - ./nginx.conf:/etc/nginx/conf.d/default.conf:ro
      - static_content:/app/static_content:ro
    ports:
      - "8131:80"
    depends_on:
      - web

  postfix:
    build: ./postfix
    environment:
      RELAY_DOMAINS: inboxen.lan
    ports:
      - "8132:25"
    depends_on:
      - web

volumes:
  pgdata:
  static_content:
  inboxen_media:
  inboxen_cache:

配套 nginx.conf(静态资源由命名卷直接服务,动态请求反代到 web):

server {
    listen 80;
    server_name _;

    location /static/ {
        alias /app/static_content/;
    }

    location / {
        proxy_pass http://web:8000;
        proxy_set_header Host $host;
        proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
        proxy_set_header X-Forwarded-Proto $scheme;
    }
}

编排要点:

设计说明
name: inboxen-2026.2.22-stack显式项目名,栈资源(网络/服务名/镜像名前缀)全部挂在该名字下;compose 会把名称中的 . 规范化,实际镜像名为 inboxen-2026222-stack-web/inboxen-2026222-stack-postfix(见第八节 docker compose images 实测回显)
postgres/redis 带 healthcheckweb 的 depends_on.condition: service_healthy 保证数据库就绪后才执行 migrate,避免首启竞态
static_content 命名卷web 内 collectstatic 写入,nginx 只读挂载同一卷直接对外服务
Salmon 端口 8823 不对外发布收信管道只接受栈内 postfix 转发,不暴露宿主
Gunicorn 前台运行exec 作为 1 号进程,docker compose stop web 能正确收尾

八、启动与首启验证

8.1 构建镜像

cd /opt/inboxen-compose
docker compose build

实测回显(尾部):

 Image inboxen-2026222-stack-postfix Built
 Image inboxen-2026222-stack-web Built

镜像体积实测(docker images):

inboxen-2026222-stack-web     390MB
inboxen-2026222-stack-postfix 188MB

8.2 拉起全栈

docker compose up -d

实测回显(尾部):

 Container inboxen-2026222-stack-postgres-1 Healthy
 Container inboxen-2026222-stack-redis-1 Healthy
 Container inboxen-2026222-stack-web-1 Started
 Container inboxen-2026222-stack-nginx-1 Started
 Container inboxen-2026222-stack-postfix-1 Started

首次冷启动时 web 会执行完整迁移,docker compose logs web 关键回显(节选):

web-1  | == [1/3] 初始化数据库与静态资源 ==
web-1  | Operations to perform:
web-1  |   Apply all migrations: auth, blog, cms, contenttypes, django_celery_results, inboxen, monitor, otp_static, otp_totp, search, sessions, tickets, two_factor
web-1  | Running migrations:
web-1  |   Applying contenttypes.0001_initial... OK
web-1  |   ...
web-1  |   Applying two_factor.0001_squashed_0008_delete_phonedevice... OK
web-1  |
web-1  | 57 static files copied to '/app/static_content'.
web-1  | == [2/3] 引导管理员与收信域名(幂等,已存在则跳过) ==
web-1  | Superuser created successfully.
web-1  | inboxen.lan created!
web-1  | == [3/3] 启动 Celery worker / beat / Salmon,随后前台运行 Gunicorn ==

💡 幂等性说明:entrypoint.sh 里 migrate/createsuperuser/createdomain 都可重复执行——重建 web 服务时迁移为空操作、管理员与域名提示「已存在,跳过」,数据卷不受影响。

8.3 栈状态核查

docker compose ps
docker compose images

实测回显:

SERVICE    IMAGE                           STATUS                   PORTS
nginx      nginx:alpine                    Up 3 minutes             0.0.0.0:8131->80/tcp, [::]:8131->80/tcp
postfix    inboxen-2026222-stack-postfix   Up 44 seconds            0.0.0.0:8132->25/tcp, [::]:8132->25/tcp
postgres   postgres:16-alpine              Up 7 minutes (healthy)   5432/tcp
redis      redis:7-alpine                  Up 7 minutes (healthy)   6379/tcp
web        inboxen-2026222-stack-web       Up 3 minutes             8000/tcp, 8823/tcp
CONTAINER                          REPOSITORY                      TAG                 PLATFORM            IMAGE ID            SIZE                CREATED
inboxen-2026222-stack-nginx-1      nginx                           alpine              linux/amd64         a3554c0d0b48        93.6MB              4 days ago
inboxen-2026222-stack-postfix-1    inboxen-2026222-stack-postfix   latest              linux/amd64         30771574da02        188MB               3 minutes ago
inboxen-2026222-stack-postgres-1   postgres                        16-alpine           linux/amd64         f46107955874        419MB               4 days ago
inboxen-2026222-stack-redis-1      redis                           7-alpine            linux/amd64         57e4255cc971        57.3MB              4 days ago
inboxen-2026222-stack-web-1        inboxen-2026222-stack-web       latest              linux/amd64         2f97f2faf8a3        390MB               6 minutes ago

Web 入口可达性:

curl -s -o /dev/null -w '%{http_code}\n' -L http://127.0.0.1:8131/
200

浏览器打开 http://<宿主IP>:8131/,进入 /user/account/login/ 用 admin / q1w2e3r4 登录(登录表单是两步验证向导,第一步只需账号密码)。

九、端到端收信验证

9.1 创建收件箱

登录 Web 后在首页点击 Create an Inbox 亦可;命令行方式(在 web 服务里进 Django shell):

docker compose exec -T web inboxen shell <<'EOF'
from django.contrib.auth.models import User
from inboxen.models import Inbox, Domain
u = User.objects.get(username="admin")
d = Domain.objects.get(domain="inboxen.lan")
i = Inbox.objects.create(domain=d, user=u)
print("INBOX-ADDR:", i.inbox + "@" + d.domain)
EOF

实测回显:

39 objects imported automatically (use -v 2 for details).

INBOX-ADDR: fmcvdl@inboxen.lan

💡 收件箱本地部份由系统按 inbox_length(默认 6 位小写字母)随机生成,无须也不能指定名字;Inbox.objects.create(domain=…, user=…) 是官方 QuerySet 管理器提供的标准入口。

9.2 telnet SMTP 投信

在宿主机(或任一能访问宿主 8132 端口的机器)执行,实测完整会话(每步等待上一响应再发下一条):

220 localhost.localdomain ESMTP Postfix (Debian/GNU)
250 localhost.localdomain
250 2.1.0 Ok
250 2.1.5 Ok
354 End data with <CR><LF>.<CR><LF>
250 2.0.0 Ok: queued as 04016208745
221 2.0.0 Bye

对应输入序列:

HELO client.example.net
MAIL FROM:<sender@example.net>
RCPT TO:<fmcvdl@inboxen.lan>
DATA
From: Sender <sender@example.net>
To: fmcvdl@inboxen.lan
Subject: Hello From Compose

This message arrived through the Docker Compose stack.
.
QUIT

⚠️ 与 B1 篇同一时序坑:DATA 正文必须以单独一行 . 结束,且等 250 ... queued as ... 出现后再发 QUIT,否则 Postfix 丢弃会话、信件无痕消失。

9.3 三重验证

① Postfix 投递日志(栈内日志直写 stdout):

docker compose logs postfix | grep 04016208745

实测回显(关键行):

postfix-1  | Oct 26 06:13:05 localhost postfix/smtp[112]: 04016208745: to=<fmcvdl@inboxen.lan>, relay=web[172.19.0.4]:8823, delay=3.1, delays=3/0.01/0.04/0.04, dsn=2.0.0, status=sent (250 OK)

relay=web[172.19.0.4]:8823 说明信件已从 postfix 服务转交 web 服务内的 Salmon。

② Web 端可见:登录后打开 http://<宿主IP>:8131/inbox/fmcvdl@inboxen.lan/,信件列表出现主题 Hello From Compose。命令行验证(先取 CSRF token 走登录表单,再带 Cookie 访问收件箱页):

curl -s -b cookies.txt -L http://127.0.0.1:8131/inbox/fmcvdl@inboxen.lan/ | grep -o "Hello From Compose"

实测回显:

Hello From Compose

③ 数据库落库:

docker compose exec -T web inboxen shell <<'EOF'
from inboxen.models import Email
print("EMAIL-TOTAL:", Email.objects.count())
EOF

实测回显:

EMAIL-TOTAL: 1

十、日常运维操作

# 查看各服务日志(postfix 日志直写 stdout,收信排障首选)
docker compose logs -f web
docker compose logs -f postfix

# 重启单个服务(数据卷保留,重跑幂等初始化)
docker compose restart web

# 升级 Inboxen 版本:改 Dockerfile 里的 inboxen== 版本号后重建
docker compose build web && docker compose up -d web

# 备份数据库
docker compose exec postgres pg_dump -U inboxen inboxen > inboxen-backup.sql

# 停栈(保留数据卷)
docker compose down

# 停栈并删除全部数据卷(彻底清空,含 PostgreSQL 业务数据)
docker compose down -v

十一、常见排障

现象根因处置
web 构建报 fatal error: stdlib.h: No such file or directory(编译 psycopg2 时)--no-install-recommends 跳过了 gcc 的 libc6-devbuilder 阶段显式 apt-get install libc6-dev
Nginx 返回 502,web 日志停在 salmon start 无 Gunicorn 输出salmon 的 daemonize 行为随环境不稳定,阻塞了入口脚本entrypoint 里 salmon start … & 后台启动
投递报 status=deferred (… name=web type=MX: Host not found)transport 目标 smtp:web:8823 未加方括号,Postfix 按 MX 查询写成 smtp:[web]:8823
投递报 status=deferred (… name=web type=AAAA: Host not found),方括号已加Debian Postfix smtp 进程 chroot 运行,chroot 内无 resolv.confstart.sh 里把 /etc/resolv.conf、/etc/services、/etc/nsswitch.conf 拷入 /var/spool/postfix/etc/
纯 HTTP 访问 301 跳 https生产模式(debug 关闭)内置 SECURE_SSL_REDIRECT=True实验栈保持 debug: true;生产上 TLS 终结
重建 postfix 后旧信一直不投deferred 队列在容器文件系统里,未持久化数据面在 web 侧无影响;让发件方重投即可,或在 start.sh 给 /var/spool/postfix 挂卷持久化
ports: 报 port is already allocated宿主端口被占用换空闲端口(本文实测即因 8128/8129 被占改用 8131/8132)
web 起不来,celery worker 报 'NoneType' object has no attribute 'Redis'Redis broker 传输包未装镜像内 pip install redis(本文 Dockerfile 已内置)
页面样式全丢、/static/ 404static_content 卷未挂到 nginx,或 collectstatic 未执行核对两个服务的卷挂载与 entrypoint 的 collectstatic 步骤

十二、卸载

cd /opt/inboxen-compose
docker compose down -v          # 停栈、删网络与数据卷
docker rmi inboxen-2026222-stack-web inboxen-2026222-stack-postfix   # 删自建镜像
# postgres:16-alpine / redis:7-alpine / nginx:alpine / python:3.12-slim / debian:12-slim
# 为基础镜像,若其它组件仍使用则保留

十三、参考链接