Debian 12 安装 Inboxen 2026.2.22.0.27.49 指南

在 Debian 12 从零安装 Inboxen 2026.2.22.0.27.49 全流程实测:编译 Python 3.12、pip 双源安装、PostgreSQL/Celery/Salmon/Gunicorn+Nginx 部署、Postfix 收信链路对接与 telnet SMTP 端到端验证。

InboxenInboxen邮件服务2026/10/26

Debian 12 安装 Inboxen 2026.2.22.0.27.49 指南

目标:在一台 Debian 12 服务器上从零安装 Inboxen 2026.2.22.0.27.49,打通「Postfix 收信 → Salmon 收信管道 → Celery 异步处理 → PostgreSQL 落库 → Nginx+Gunicorn Web 展示」的完整闭环,并用 telnet SMTP 会话投递一封真实信件完成端到端验证。本文全部命令与输出均为实测记录。

一、环境信息

项目值
操作系统Debian GNU/Linux 12.15 (bookworm)
内核6.1.0-37-amd64
Inboxen 版本2026.2.22.0.27.49(Codeberg 官方 PyPI 索引最新包)
Python3.12.12(源码编译,/usr/local/python3.12)
PostgreSQL15.19 (Debian 15.19-0+deb12u1)
Redis7.0.15
Nginx1.22.1
Postfix3.7.11
服务器名/IPmail.inboxen.lan / 10.89.57.2(局域网测试域)
收信域名inboxen.lan
管理员账号admin / q1w2e3r4
实测日期2026-10-26

⚠️ 版本前提:Inboxen 2026.2.22.0.27.49 要求 Python ≥ 3.12,而 Debian 12 自带 Python 3.11,因此本文第 4 节先编译 Python 3.12。若使用 Debian 13 (trixie,自带 3.13) 可跳过编译直接 apt install python3-venv python3-dev。

二、前置准备

2.1 配置 APT 源

Debian 12 默认源可用。若在境内网络环境,建议先切 HTTP 形态的镜像源(不要用 HTTPS 源:系统此时还没有 ca-certificates,HTTPS 握手会报 No system certificates available,这是实测踩过的第一个坑):

# 实测:切换镜像源(/etc/apt/sources.list.d/debian.sources)
sed -i 's|http://deb.debian.org/debian|http://mirrors.aliyun.com/debian|g; s|http://deb.debian.org/debian-security|http://mirrors.aliyun.com/debian-security|g' /etc/apt/sources.list.d/debian.sources
apt-get update

2.2 安装编译依赖与服务组件

# 实测(输出末行 EXIT=0)
export DEBIAN_FRONTEND=noninteractive
apt-get install -y -qq build-essential wget ca-certificates curl libssl-dev zlib1g-dev \
  libbz2-dev libreadline-dev libsqlite3-dev libffi-dev liblzma-dev libncursesw5-dev \
  tk-dev uuid-dev xz-utils pkg-config \
  postgresql redis-server nginx postfix libpq-dev
echo EXIT=$?

实测回显(节选):

EXIT=0

实测安装版本核查:

psql --version
nginx -v
postconf mail_version
psql (PostgreSQL) 15.19 (Debian 15.19-0+deb12u1)
nginx version: nginx/1.22.1
mail_version = 3.7.11

💡 libpq-dev 必须装:Inboxen 依赖 psycopg2(源码构建版),没有 pg_config 时 pip 安装会在最后一步报 ERROR: Failed building wheel for psycopg2,前功尽弃(实测踩坑 2)。

2.3 服务组件就位

systemctl start postgresql redis-server nginx postfix
systemctl is-active postgresql redis-server nginx postfix

实测回显:

active
active
active
active

监听端口核查(PostgreSQL/Redis 默认只听本机回环,Postfix 听 25,Nginx 听 80):

0.0.0.0:25
0.0.0.0:80
127.0.0.1:5432
127.0.0.1:6379

三、创建 PostgreSQL 数据库与用户

# 实测
cat > /tmp/init.sql <<'EOF'
CREATE USER inboxen WITH PASSWORD 'q1w2e3r4';
CREATE DATABASE inboxen OWNER inboxen;
EOF
su postgres -c "psql -f /tmp/init.sql"

实测回显:

CREATE ROLE
CREATE DATABASE

四、编译安装 Python 3.12.12

# 实测:下载约 27 MB
cd /usr/local/src
wget https://www.python.org/ftp/python/3.12.12/Python-3.12.12.tgz
tar xf Python-3.12.12.tgz && cd Python-3.12.12
./configure --prefix=/usr/local/python3.12
make -j$(nproc)
make install
/usr/local/python3.12/bin/python3.12 -V

实测回显:

Python 3.12.12

五、安装 Inboxen 2026.2.22.0.27.49

5.1 建立虚拟环境

# 实测
/usr/local/python3.12/bin/python3.12 -m venv /opt/inboxen/env

5.2 pip 安装(双源)

Inboxen 官方包发布在项目自建的 Codeberg PyPI 索引,依赖包来自公共 PyPI,因此 pip 需要同时配置两个源:

# 实测
/opt/inboxen/env/bin/pip 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/

实测回显(关键行):

Successfully installed Django-5.2.18 Faker-40.43.0 Markdown-3.11.2 amqp-5.4.1 asgiref-3.13.0
billiard-4.3.1 cachetools-7.2.5 celery-5.6.3 ... inboxen-2026.2.22.0.27.49 kombu-5.6.2 ...
psycopg2-2.9.13 ... salmon-mail-3.3.0 ...

5.3 补齐三个运行时缺口(实测必踩)

  1. setuptools(Python 3.12 移除了 distutils,Inboxen 依赖链里的 django-bootstrap-form 仍 from distutils.version import StrictVersion,不补则在首次 manage.py 调用时崩出 ModuleNotFoundError: No module named 'distutils'):

    /opt/inboxen/env/bin/pip install setuptools redis gunicorn django-debug-toolbar
    

    其中 redis 是 Celery Redis broker 必需的传输包(缺它则 celery 启动即报 'NoneType' object has no attribute 'Redis' 并退出),gunicorn 是 9 节的 WSGI 服务器,django-debug-toolbar 是 debug 模式(本教程用 HTTP 部署时)必需项。

  2. 安装完成后 venv 里自带两个入口命令:inboxen(等价 manage.py)与 salmon(收信进程)。pip 包已内置编译好的静态资源,无需 Node.js/npm。

六、编写配置文件 inboxen.config

pip 安装形态下,inboxen 命令以当前工作目录的 inboxen.config 为配置源,先建应用目录:

# 实测
mkdir -p /opt/inboxen/app && cd /opt/inboxen/app
cat > inboxen.config <<'EOF'
debug: true
secret_key: "b1-experiment-only-026-1nboxen-test-key"
database:
    engine: "django.db.backends.postgresql"
    host: "127.0.0.1"
    name: "inboxen"
    user: "inboxen"
    password: "q1w2e3r4"
    port: "5432"
tasks:
    broker_url: "redis://127.0.0.1:6379/0"
enable_registration: true
allowed_hosts:
    - "*"
EOF

字段说明:

字段作用备注
secret_keyDjango 会话/CSRF 签名密钥生产环境用 openssl rand -hex 32 生成并妥善保管
database.*PostgreSQL 连接五要素对应第三节创建的库与用户
tasks.broker_urlCelery broker默认 amqp://(RabbitMQ),本教程用单机 Redis
enable_registration是否开放公网注册默认 false;内网部署可开,公网建议关
allowed_hostsDjango ALLOWED_HOSTS"*" 仅限测试;生产写实际域名
debugDjango DEBUGtrue 时关闭 HTTPS 强制跳转,适合纯 HTTP 实验;生产置 false 并上 TLS

⚠️ HTTPS 重定向坑:生产模式(debug 关闭)下 Inboxen 内置 SECURE_SSL_REDIRECT = True,纯 HTTP 访问会 301 跳 https 并打不开站点(实测踩坑 3)。实验环境请保持 debug: true;生产环境应配置 Nginx TLS 终结,让 Django 收到 X-Forwarded-Proto: https。

七、初始化数据库与基础数据

依次执行迁移、静态资源收集、管理员创建、收信域名登记:

cd /opt/inboxen/app
/opt/inboxen/env/bin/inboxen migrate

实测回显(尾部):

  Applying otp_totp.0003_add_timestamps... OK
  Applying search.0001_initial_squashed... OK
  Applying sessions.0001_initial... OK
  Applying tickets.0001_initial_squashed... OK
  Applying two_factor.0001_squashed_0008_delete_phonedevice... OK
/opt/inboxen/env/bin/inboxen collectstatic --noinput

实测回显:


26 static files copied to '/opt/inboxen/app/static_content'.
System check identified some issues:

WARNINGS:
?: (caches.W003) Your 'default' cache LOCATION path is relative. Use an absolute path instead.

💡 caches.W003 是 FileBasedCache 相对路径的提示性告警,不影响功能;在 inboxen.config 的 cache.location 写绝对路径即可消除。

DJANGO_SUPERUSER_PASSWORD=q1w2e3r4 /opt/inboxen/env/bin/inboxen createsuperuser --noinput --username admin --email admin@inboxen.lan
/opt/inboxen/env/bin/inboxen createdomain inboxen.lan

实测回显:

Superuser created successfully.
inboxen.lan created!

八、启动 Celery 与 Salmon 收信进程

准备运行目录,然后分别拉起 Celery worker、beat 与 Salmon SMTP 收信进程:

# 实测
cd /opt/inboxen/app
mkdir -p run logs

export DJANGO_SETTINGS_MODULE=inboxen.settings
nohup /opt/inboxen/env/bin/celery -A inboxen worker -l warning --events -Ofair \
  -f logs/celery-worker.log >/dev/null 2>&1 &
nohup /opt/inboxen/env/bin/celery -A inboxen beat -l warning \
  -f logs/celery-beat.log >/dev/null 2>&1 &

export SALMON_SETTINGS_MODULE=inboxen.router.config.settings
/opt/inboxen/env/bin/salmon start --pid run/router.pid --boot inboxen.router.config.boot
sleep 5
/opt/inboxen/env/bin/salmon status --pid run/router.pid

实测回显:

Salmon running with PID 21618

监听端口确认:

ss -ltn | grep 8823
LISTEN 0      5              [::1]:8823          [::]:*

⚠️ Salmon 默认只监听 IPv6 回环 [::1]:8823(官方设置项 SALMON_SERVER),Postfix 侧转发目标必须写 [::1]:8823,写 127.0.0.1 会连不上。

💡 生产环境建议把以上三个进程写成 systemd 单元(inboxen-celery-worker.service、inboxen-celery-beat.service、inboxen-salmon.service),WorkingDirectory=/opt/inboxen/app、Environment=DJANGO_SETTINGS_MODULE=inboxen.settings,实现开机自启与崩溃拉起。本文为保持与实测一致,正文采用 nohup 方式。

九、部署 Gunicorn 并配置 Nginx 反代

# 实测:venv 内自带 gunicorn(5.3 节安装),版本 26.2.0
cd /opt/inboxen/app
nohup /opt/inboxen/env/bin/gunicorn inboxen.wsgi -b 127.0.0.1:8000 --workers 2 \
  --access-logfile logs/gunicorn-access.log --error-logfile logs/gunicorn-error.log >/dev/null 2>&1 &

Nginx 站点配置(替换 /etc/nginx/sites-available/default):

# 实测配置
server {
    listen 80 default_server;
    listen [::]:80 default_server;
    server_name _;

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

    location / {
        proxy_pass http://127.0.0.1:8000;
        proxy_set_header Host $host;
        proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
        proxy_set_header X-Forwarded-Proto $scheme;
    }
}
nginx -t && systemctl reload nginx
curl -s -o /dev/null -w "%{http_code}\n" -L http://127.0.0.1/

实测回显:

nginx: configuration file /etc/nginx/nginx.conf test is successful
200

浏览器访问 http://<服务器IP>/,应看到 Inboxen 欢迎页,右上角可进入 /user/account/login/ 用 admin 登录。

十、配置 Postfix 收信链路

Inboxen 自己不做 MX,公网信件由 Postfix 接收后转发给 Salmon。三个关键参数:

# 实测
# 1) 声明本机为 inboxen.lan 的收信域(允许外部来源投递,否则外网信件 554 Relay access denied)
postconf -e "relay_domains = inboxen.lan"
# 2) transport 表:该域全部转交 Salmon(注意 [::1])
echo "inboxen.lan smtp:[::1]:8823" >> /etc/postfix/transport
postconf -e "transport_maps = hash:/etc/postfix/transport"
postmap /etc/postfix/transport
# 3) 开启 Postfix 自写日志(Debian 12 默认无 rsyslog 时排障必备,Postfix ≥3.4 支持)
touch /var/log/mail.log && chmod 644 /var/log/mail.log
postconf -e "maillog_file = /var/log/mail.log"
systemctl reload postfix
postconf relay_domains

实测回显:

relay_domains = inboxen.lan

配置生效核查:

postmap -q inboxen.lan hash:/etc/postfix/transport
smtp:[::1]:8823

💡 DNS 前提:生产环境需把 inboxen.lan 的 MX 记录指向本服务器,并放行公网 25 端口入站;局域网/测试环境可由上游 MTA 直接把该域路由到本机 25 端口。

十一、创建收件箱并完成端到端验证

11.1 创建第一个收件箱

Inboxen 的收件箱本地部份由系统按 inbox_length(默认 6 位小写字母)随机生成,走标准的 QuerySet 创建入口:

# 实测
cd /opt/inboxen/app
/opt/inboxen/env/bin/python - <<'EOF'
import django, os
os.environ.setdefault("DJANGO_SETTINGS_MODULE", "inboxen.settings")
django.setup()
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

实测回显:

INBOX-ADDR: shiunb@inboxen.lan

(也可登录 Web 后在 /inbox/add/ 页面点击创建,效果一致。)

11.2 从外部 telnet SMTP 会话投递一封信

在另一台机器上(或本机回环亦可)执行 telnet 会话,完整交互实录如下(每步等待前一响应后再发下一条):

telnet <服务器IP> 25

实测回显(逐行):

220 mail.inboxen.lan ESMTP Postfix (Debian/GNU)
250 mail.inboxen.lan
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 8AC83124B50B
221 2.0.0 Bye

对应的输入序列:

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

This message was delivered with a plain telnet session.
.
QUIT

⚠️ 时序坑:DATA 正文必须以单独一行 . 结束,且必须等到 250 ... Ok: queued as ... 响应出现后再发 QUIT。实测中若 telnet 会话在 DATA 结束前断开,Postfix 会直接丢弃信件(队列空、无投递日志、Inboxen 无感知),极易误判为链路故障。

11.3 三重验证

① Postfix 投递日志(信件确实转交 Salmon):

grep "8AC83124B50B" /var/log/mail.log

实测回显:

Oct 26 05:27:25 mail postfix/smtp[22504]: 8AC83124B50B: to=<shiunb@inboxen.lan>, relay=::1[::1]:8823, delay=4.1, delays=4/0/0.04/0.01, dsn=2.0.0, status=sent (250 OK)
Oct 26 05:27:25 mail postfix/qmgr[22490]: 8AC83124B50B: removed

② Web 端可见:浏览器登录 admin 后打开 http://<服务器IP>/inbox/shiunb@inboxen.lan/,信件列表出现主题 Hello From Telnet。用命令行验证(先走 /user/account/login/ 表单登录拿会话 Cookie,登录表单是 two_factor 向导,字段为 csrfmiddlewaretoken + login_view-current_step=auth + auth-username + auth-password):

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

实测回显:

Hello From Telnet

③ 数据库落库(可选):

cd /opt/inboxen/app
/opt/inboxen/env/bin/python -c "import django,os;os.environ.setdefault('DJANGO_SETTINGS_MODULE','inboxen.settings');django.setup();from inboxen.models import Email;print('EMAIL-TOTAL:', Email.objects.count())"

实测回显:

EMAIL-TOTAL: 3

至此「SMTP 投递 → Inboxen 收信 → Web 可见」闭环全部打通。

十二、常见排障

现象根因处置
pip 报 ERROR: Failed building wheel for psycopg2缺 pg_configapt install libpq-dev 后重跑 pip
首次执行 inboxen 命令报 No module named 'distutils'Python 3.12 无 distutils,依赖链需 setuptools 兼容层pip install setuptools
celery 秒退、日志 0 字节,status 报 'NoneType' object has no attribute 'Redis'用 Redis broker 但未装 redis 传输包pip install redis
纯 HTTP 访问 301 跳 https生产模式内置 SECURE_SSL_REDIRECT=True实验环境 config 里 debug: true;生产上 TLS
telnet 投信后队列空、无日志、Web 无信DATA 未以单独 . 行结束即断开,Postfix 丢弃会话按响应节奏发送,等 250 ... queued as ... 再 QUIT
Postfix 日志无处可寻Debian 12 默认无 rsyslogpostconf -e "maillog_file=/var/log/mail.log" 后 reload
外部投递报 Relay access denied域未登记为本机收信域postconf -e "relay_domains=inboxen.lan"
Postfix 转 Salmon 连接失败Salmon 只听 [::1]transport 目标写 smtp:[::1]:8823
缓存告警 caches.W003FileBasedCache 相对路径cache.location 改绝对路径

十三、卸载

# 停进程
pkill -f 'celery -A inboxen'
/opt/inboxen/env/bin/salmon stop --pid /opt/inboxen/app/run/router.pid
pkill -f gunicorn
# 撤 Nginx 站点并恢复默认
rm -f /etc/nginx/sites-enabled/default && systemctl reload nginx
# 撤 Postfix 对接
postconf -X transport_maps -X relay_domains; systemctl reload postfix
# 删库删用户
su postgres -c "dropdb inboxen && dropuser inboxen"
# 删应用目录与 Python
rm -rf /opt/inboxen /usr/local/python3.12 /usr/local/src/Python-3.12.12*

十四、参考链接

  • 官方 Getting Started(make salmon-start 等原始流程):https://inboxen.readthedocs.io/en/latest/getting_started.html(2026-10-26 查询)
  • 官方配置文档:https://inboxen.readthedocs.io/en/latest/settings.html(2026-10-26 查询)
  • 官方代码仓:https://codeberg.org/Inboxen/Inboxen(2026-10-26 查询)
  • 站内关联:《Inboxen 2026.2.22.0.27.49 介绍与选型指南》