Postal 快速入门与实战示例

Postal 3.3.7 快速入门实战:登录控制台、创建组织与邮件服务器、发信域名与 DKIM/DNS 记录、SMTP/HTTP API 两种发信、入站路由接收、消息队列与日志查看,全部命令与输出经 3.3.7 实测

Postal邮件服务器2026/10/19

Postal 快速入门与实战示例

目标:在已部署的 Postal 3.3.7 上完成从登录到发信收信的完整入门链路——创建组织与邮件服务器、配置发信域名与 DKIM 记录、生成 SMTP 与 API 凭据、用 SMTP 和 HTTP API 两种方式发信、配置入站路由接收邮件、查看消息队列与投递日志。已在 Postal 3.3.7(Ubuntu 22.04,部署形态见《Postal Docker Compose 部署指南》)实测通过,全部命令与回显为真实执行结果。

一、环境信息

本节列出本文所有实测所依赖的环境基准,读者可据此判断自己环境的差异项。

  • Postal 版本:3.3.7(postal version 实测输出 3.3.7)
  • 操作系统:Ubuntu 22.04.4 LTS(x86_64)
  • Web 界面与 HTTP API:http://postal.example.com:8124(hosts 本地解析 127.0.0.1)
  • SMTP 端口:25(监听全部接口)
  • 数据库:MariaDB 11.4,宿主端口 13306,演示账号 root / q1w2e3r4
  • 全局管理员:admin@postal.example.com / q1w2e3r4(部署篇 4.7 节创建)
  • 发信域名:example.com;收件测试域名:recv.test(本地模拟口径,出站经内部中继 127.0.0.1:2525 投递)
  • 客户端工具:swaks(SMTP 测试)、curl(HTTP API)、mysql(数据库核对)

二、前置条件

本节确认入门操作开始前必须就绪的三件事,均给出验证命令与实测回显。

2.1 服务在运行

三个常驻进程(web / smtp / worker)处于 Up 状态:

postal status
NAME                IMAGE                              COMMAND                  SERVICE             CREATED             STATUS
postal-smtp-1       ghcr.io/postalserver/postal:3.3.7  "docker-entrypoint.p…"   smtp                2 hours ago         Up 2 hours
postal-worker-1     ghcr.io/postalserver/postal:3.3.7  "docker-entrypoint.p…"   worker              2 hours ago         Up 2 hours
postal-web-1        ghcr.io/postalserver/postal:3.3.7  "docker-entrypoint.p…"   web                 2 hours ago         Up 2 hours

2.2 Web 可达

curl -s -o /dev/null -w '%{http_code}\n' http://postal.example.com:8124/login
200

2.3 域名解析就绪

安装域名 postal.example.com 必须能解析到本机(内网环境用 /etc/hosts,公网环境用 DNS A 记录):

getent hosts postal.example.com
127.0.0.1       postal.example.com

三、登录控制台与界面导览

本节用浏览器完成首次登录,并说明控制台的页面结构,后续章节的操作都在这些页面上。

浏览器打开 http://postal.example.com:8124,页面标题为 Login - Postal,输入管理员邮箱 admin@postal.example.com 与密码 q1w2e3r4 登录。首次登录后系统会要求创建组织,对应第四章步骤。控制台左侧导航结构如下:

  • Organizations:组织列表与组织内邮件服务器
  • Messages:消息队列(Outgoing/Incoming)、垃圾箱、已挂起邮件
  • Servers:当前组织的邮件服务器设置(域名、凭据、路由、Webhook 等入口)
  • All Users:安装级用户管理(仅全局管理员可见)

验证登录会话真实生效(用 curl 带 Cookie 走一遍登录表单,实测回显):

curl -s http://postal.example.com:8124/login | grep -oE '<title>[^<]*</title>'
<title>Login - Postal</title>

四、创建组织与邮件服务器

本节创建一个组织(Organization)和它下面的第一台邮件服务器(Mail Server)。组织是资源与配额的顶层归属,邮件服务器是发信/收信的独立工作单元,各自持有独立的域名、凭据与消息库。

4.1 界面路径

登录后点击左侧 Organizations → New Organization,填写:

  • Name:Example Organization
  • Permalink:example(URL 中的组织标识,创建后不可改)

进入组织后点击 Create a new mail server,填写:

  • Name:Main Server
  • Permalink:main-server
  • Mode:Live(正式发信模式;Development 模式会把全部出站邮件挂起待审,见《Postal 日常运维与排障手册》的队列管理章节)

创建完成后,界面的服务器设置页顶部会显示服务器 Token。

4.2 用 postal console 核对与脚本化创建

postal console 是官方文档收录的 Rails 控制台入口,除了核对界面创建结果,也可以直接脚本化创建整套资源。核对当前组织与服务器(实测回显):

postal console
irb(main):001:0> puts "ORG_COUNT=#{Organization.count}"
ORG_COUNT=1
irb(main):002:0> puts "SERVERS=#{Server.all.map(&:permalink).join(",")}"
SERVERS=main-server
irb(main):003:0> exit

按界面路径创建后,服务器属性核对(实测回显):

irb(main):001:0> s = Server.find_by(permalink: 'main-server')
irb(main):002:0> puts "SERVER: id=#{s.id} name=#{s.name} token=#{s.token} mode=#{s.mode}"
SERVER: id=1 name=Main Server token=ctrqof mode=Live
irb(main):003:0> exit

💡 说明:一个 Postal 安装可承载多个组织,每个组织下多台邮件服务器相互隔离(独立域名、凭据、路由、消息库)。消息库按服务器编号自动建库(prefix-server-<编号>,本例为 postal-server-1),因此每台邮件服务器的大消息量互不影响。

4.3 全脚本化创建(可复用方式)

如果需要批量初始化资源,可在 console 中一次完成组织、服务器、域名、凭据与路由的创建,本节给出与 4.1~4.5 等价的可脚本化写法(实测回显):

irb(main):001:0> user = User.find_by(email_address: 'admin@postal.example.com')
irb(main):002:0> org = Organization.create!(name: 'Example Organization', permalink: 'example', owner: user, time_zone: 'UTC')
irb(main):003:0> puts "ORG: id=#{org.id} name=#{org.name} permalink=#{org.permalink}"
ORG: id=1 name=Example Organization permalink=example
irb(main):004:0> exit

五、发信域名与 DKIM/DNS 记录

本节把 example.com 注册为发信域名,拿到 Postal 要求的全部 DNS 记录,并展示 DKIM 密钥的生成与本地模拟验证口径。

5.1 添加发信域名

界面路径:进入邮件服务器 → Domains → Add Domain,Name 填 example.com,Owner 保持服务器级,勾选自动生成 DKIM 密钥。添加后 Postal 立即为该域名生成 1024 位 RSA DKIM 密钥对。

用 console 核对域名与 DKIM 记录(实测回显):

irb(main):001:0> d = Domain.find_by(name: 'example.com')
irb(main):002:0> puts "DOMAIN: id=#{d.id} name=#{d.name} verified=#{d.verified?}"
DOMAIN: id=1 name=example.com verified=true
irb(main):003:0> puts "DKIM_SELECTOR: #{d.dkim_identifier_string}"
DKIM_SELECTOR: j5WBMx
irb(main):004:0> puts "DKIM_RECORD_NAME: #{d.dkim_record_name}"
DKIM_RECORD_NAME: postal-j5WBMx._domainkey
irb(main):005:0> puts "DKIM_RECORD: #{d.dkim_record}"
DKIM_RECORD: v=DKIM1; t=s; h=sha256; p=MIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQCyAbWTqpL/d9c0P8p7MUl6hlvmhZHSZon1edOrUPWUEuYfO4XMTM9rEP/kUXj+/FCoQRvQ3n+MSn8vf7IMco4PhBcWEN1LOpTYTU4b3yyqHTQu6bQIETWAp9hElTu3ShZ4zGUXj+PYwnyrm2KEGgdtbkwLjUSw7/fUpzInCk8+/QIDAQAB;
irb(main):006:0> exit

5.2 需要配置的 DNS 记录全集

以 postal.yml 的 DNS 规划(见《Postal Docker Compose 部署指南》第七章)为基准,example.com 应配置的记录如下表。<域名DKIM选择器> 与 <域名DKIM记录值> 即 5.1 节 console 输出的 DKIM_RECORD_NAME 与 DKIM_RECORD。

类型主机记录记录值用途
TXTexample.comv=spf1 include:spf.postal.example.com ~all发信域 SPF
TXTspf.postal.example.comv=spf1 ip4:<服务器IP> ~all安装级 SPF
TXT_dmarc.example.comv=DMARC1; p=none; rua=mailto:dmarc@example.comDMARC 策略(可选但建议)
TXTpostal-j5WBMx._domainkey.example.comv=DKIM1; t=s; h=sha256; p=MIGfMA0…QIDAQAB;域级 DKIM 公钥
CNAMErp.example.comrp.postal.example.com回程路径域(处理退信)
MXrp.postal.example.commx.postal.example.com(优先级 10)安装域回程路径
TXTrp.postal.example.comv=spf1 a mx include:spf.postal.example.com ~all回程路径 SPF
CNAMEpsrp.postal.example.compostal.example.com邮件服务器级回程路径
MXexample.commx.postal.example.com(优先级 10)入站邮件接收

安装级 DKIM 签名密钥(非域级)也可用命令直接输出,用于 rp.postal.example.com 域的 postal._domainkey 记录:

postal default-dkim-record
DKIM Selector: postal
DKIM Record: v=DKIM1; t=s; h=sha256; p=MIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQDKAdq7Ykbpu8HQ5gdU8Nzmz9MG1RfaUcBaMRxucrvMIEVzQ1zl0IHhVtg4QcFAFzsu1bdOgaOohdf+p4AZHT1pJL4IbE7kerqE/rLqyBVqbyPmjuhPlEz/hxaVneI/FYJedwBpPreg83NhmtuL0alGCrBZpDdPgaPQU2GY8QY6DwIDAQAB;

5.3 本地模拟验证口径

本环境为内网模拟,example.com 无公网 DNS 权威,因此:

  • 域名的 DNS 校验(SPF/DKIM/回程路径检查)无法真实通过。界面域名页会显示校验未通过项,属于预期现象
  • 全局管理员在界面上添加的域名会立即标记为 verified(官方行为:全局管理员跳过 DNS 校验直接生效),本文 5.1 节 verified=true 即此口径
  • 实测该域名的发信完全可用:未启用域级 DKIM 时,Postal 按官方设计回落为安装级签名(d=rp.postal.example.com; s=postal),见第七章 7.4 节原始邮件头证据
  • 生产环境必须真实配置 5.2 节记录并通过界面校验后再发信,本文记录内容可直接照抄使用

六、创建发送凭据

本节为邮件服务器创建 SMTP 凭据与 API 凭据各一份,这是发信的钥匙。

界面路径:邮件服务器 → Credentials → New Credential,Type 分别选 SMTP 与 API,Name 分别填 app-smtp、app-api。创建后凭据 key 只完整显示一次,可再次进入查看。

用 console 核对两份凭据(实测回显):

irb(main):001:0> Credential.all.each { |c| puts "CRED: name=#{c.name} type=#{c.type} key=#{c.key}" }
CRED: name=app-smtp type=SMTP key=xo5g1Opn3yZYSgAXTsMOJMJQ
CRED: name=app-api type=API key=4e1cEP1oJNUuqqe7RF6QAuWI
=> [#<Credential id: 1, ...>, #<Credential id: 2, ...>]
irb(main):002:0> exit

💡 技术拓展:Postal 的发送鉴权有三种可选形态——SMTP 认证(用户名 x + 密码=SMTP 凭据 key,走 25 端口会话)、HTTP API(X-Server-API-Key 头携带 API 凭据 key)、IP 池 SMTP(SMTP-IP 类型凭据,按来源 IP 鉴权)。本文实际演示前两种:程序内联发送优先 API,邮件客户端/传统系统对接用 SMTP。

七、SMTP 发信实战

本节用 swaks 走真实 SMTP 会话完成认证与发信,并展示 Postal 侧的队列处理与 DKIM 签名证据。

7.1 认证发信

SMTP 认证规则:用户名固定为 x,密码为 SMTP 凭据 key(见第六章)。

swaks --server 127.0.0.1:25 \
  --auth PLAIN --auth-user x --auth-password xo5g1Opn3yZYSgAXTsMOJMJQ \
  --from sender@example.com --to rcpt2@recv.test \
  --h-Subject 'Postal SMTP test' --body 'Hello Postal via SMTP.'
 === Trying 127.0.0.1:25...
 === Connected to 127.0.0.1.
<-  220 postal.example.com ESMTP Postal/IM76066A
 -> EHLO ops
<-  250-My capabilities are
<-  250 AUTH CRAM-MD5 PLAIN LOGIN
 -> AUTH PLAIN AHgAeG81ZzFPcG4zeVpZU2dBWFRzTU9KTUpR
<-  235 Granted for example/main-server
 -> MAIL FROM:<sender@example.com>
<-  250 OK
 -> RCPT TO:<rcpt2@recv.test>
<-  250 OK
 -> DATA
<-  354 Go ahead
 -> Date: Mon, 19 Oct 2026 19:18:43 +0800
 -> To: rcpt2@recv.test
 -> From: sender@example.com
 -> Subject: Postal SMTP test
 -> Message-Id: <20261019191843.4184898@ops>
 -> X-Mailer: swaks v20201014.0 jetmore.org/john/code/swaks/
 ->
 -> Hello Postal via SMTP.
 ->
 ->
 -> .
<-  250 OK
 -> QUIT
<-  221 Closing Connection
=== Connection closed with remote host.

⚠️ 注意:会话里出现 250-STARTTLS 时,如需强制 TLS 可在客户端启用 STARTTLS;Postal 3.3.7 的 SMTP 端点存在 STARTTLS ssl_mode 选项因源码拼写错误而失效的已知问题(官方文档标注),配置服务端强制 STARTTLS 时请用 Auto/TLS 方式替代。

7.2 队列处理与投递日志

发信即入队,worker 从队列取出投递到出站中继(本环境 smtp_relays 指向 127.0.0.1:2525,生产环境无中继时为直投对方 MX)。实时观察 worker 投递日志:

postal logs worker 2>&1 | grep -v warning | tail -4
worker-1  | 2026-10-19 11:18:44 +0000 INFO   starting message unqueue component=worker thread=work0 original_queued_message=10
worker-1  | 2026-10-19 11:18:44 +0000 INFO   queue latency is 0.04435849189758301s component=worker thread=work0 original_queued_message=10
worker-1  | 2026-10-19 11:18:44 +0000 INFO   Connected to 127.0.0.1:2525 (127.0.0.1) log_id=RKJL6YCJ component=worker thread=work0 original_queued_message=10 queued_message=10
worker-1  | 2026-10-19 11:18:44 +0000 INFO   Sending message 1::9 to rcpt2@recv.test log_id=RKJL6YCJ component=worker thread=work0 original_queued_message=10 queued_message=10
worker-1  | 2026-10-19 11:18:44 +0000 INFO   Accepted by 127.0.0.1:2525 (127.0.0.1) for rcpt2@recv.test log_id=RKJL6YCJ component=worker thread=work0 original_queued_message=10 queued_message=10

投递结果在消息库 deliveries 表核对(实测回显):

mysql -h127.0.0.1 -P13306 -uroot -pq1w2e3r4 -e \
  "SELECT id,message_id,status,details FROM \`postal-server-1\`.deliveries WHERE message_id=9;"
id      message_id      status  details
11      9               Sent    Message for rcpt2@recv.test accepted by 127.0.0.1:2525 (127.0.0.1)

7.3 消息记录与原始邮件

mysql -h127.0.0.1 -P13306 -uroot -pq1w2e3r4 -e \
  "SELECT id,token,scope,rcpt_to,mail_from,subject,status FROM \`postal-server-1\`.messages WHERE id=9;"
id      token           scope           rcpt_to         mail_from               subject                 status
9       n4NYciSSiv40zOAj        outgoing        rcpt2@recv.test  sender@example.com      Postal SMTP test        Sent

7.4 DKIM 签名实证

从消息库当日 raw 表提取原始邮件头(实测回显):

mysql -h127.0.0.1 -P13306 -uroot -pq1w2e3r4 -e \
  "SELECT LEFT(data, 700) AS head FROM \`postal-server-1\`.\`raw-2026-10-19\` WHERE data LIKE '%DKIM-Signature%' ORDER BY id DESC LIMIT 1\G" \
  2>&1 | grep -v Warning
*************************** 1. row ***************************
  id: 17
head: DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed;
        d=rp.postal.example.com;
        s=postal; t=1792408724;
        bh=bS0uLIsvpRNef6pBNfvxwo5h+rRUod7/FgzdMCbQHVg=;
        h=date:to:from:subject:message-id;
        b=ohLXYBNoUt4ws/RnjY6AjJYFFbrYXlr3Ux9eLwPo06CgldGG2fvjB8UoNhg8ekCPM2ObCRqj
        1eHCzVour8rzefMmz6HQ0yn18ucWVSJla1YCi8B8MU9y9dpEbPD7X0o92AMi0c1OI5/Qllro
        dJPcnc60AjQYVzBbKNQOQIvvBE8=
X-Postal-MsgID: n4NYciSSiv40zOAj
Received: from ops (localhost [127.0.0.1]) by postal.example.com with SMTP; Mon, 19 Oct 2026 11:18:43 -0000
Date: Mon, 19 Oct 2026 19:18:43 +0800
To: rcpt2@recv.test
From: sender@example.com
Subject: Postal SMTP test
Message-Id: <20261019191843.4184898@ops>
X-Mailer: swaks v202010

💡 说明:d=rp.postal.example.com; s=postal 是安装级签名(选择器 postal),对应 5.2 节 postal default-dkim-record 输出的记录。当 5.2 节域级 DKIM 记录真实配置并通过校验后,签名会切换为域级(d=example.com; s=postal-j5WBMx)。本内网环境无公网 DNS,域级签名不生效属预期,发信链路本身不受影响。

八、HTTP API 发信实战

本节用 curl 调用官方 v1 发信 API,适合程序集成场景。API 与 Web 界面同端口(8124),鉴权用 API 凭据 key。

8.1 发送请求与响应

curl -s -X POST http://postal.example.com:8124/api/v1/send/message \
  -H "X-Server-API-Key: 4e1cEP1oJNUuqqe7RF6QAuWI" \
  -H "Content-Type: application/json" \
  -d '{
    "to": ["api-rcpt@recv.test"],
    "from": "sender@example.com",
    "subject": "API test from Postal",
    "plain_body": "Hello via HTTP API.",
    "tag": "api-demo"
  }' | python3 -m json.tool
{
    "status": "success",
    "time": 0.052,
    "flags": {},
    "data": {
        "message_id": "470d6694-70ec-47f0-a6de-60aea7b1354b@rp.postal.example.com",
        "messages": {
            "api-rcpt@recv.test": {
                "id": 10,
                "token": "AOkLHWWuTwjiWEP0"
            }
        }
    }
}

响应字段解读:

  • status: success:请求通过校验并入队;多收件人时按 messages.<收件人> 逐个返回
  • data.message_id: Postal 生成的消息标识(形如 UUID@回程路径域),投递日志与消息详情都用它追踪
  • messages.<rcpt>.id/token:消息库中的记录 id 与 token,可用于 Web 界面直达消息详情(/message/<token>)

8.2 常用附加参数

除必填的 to/from/subject/plain_body 外,常用可选参数(v1 send/message):

参数说明示例取值
html_bodyHTML 正文,与 plain_body 同时给时自动做 MIME multipart<p>Hello</p>
tag自定义标签,用于按标签统计/检索api-demo
headers追加自定义邮件头(JSON 对象){"X-Custom":"v1"}
attachments附件数组(name/content_type/data,data 为 Base64)[{"name":"a.txt","content_type":"text/plain","data":"aGVsbG8="}]
bcc密送地址数组["boss@example.com"]
merge收件人变量合并模板(配合 vars_* 参数)true

错误形态实测(key 无效时):

curl -s -X POST http://postal.example.com:8124/api/v1/send/message \
  -H "X-Server-API-Key: wrong-key" \
  -H "Content-Type: application/json" \
  -d '{"to":["a@recv.test"],"from":"sender@example.com","subject":"x","plain_body":"x"}'
{"status":"error","time":0.004,"flags":{},"data":{"message":"The API token provided in X-Server-API-Key was not valid.","token":"wrong-key","code":"InvalidServerAPIKey"}}

8.3 消息库核对

mysql -h127.0.0.1 -P13306 -uroot -pq1w2e3r4 -e \
  "SELECT id,rcpt_to,subject,status,tag FROM \`postal-server-1\`.messages WHERE id=10;"
id      rcpt_to                 subject                 status  tag
10      api-rcpt@recv.test      API test from Postal    Sent    api-demo

九、入站路由与接收

本节验证 Postal 作为收件端的能力:MX 收信 → 路由匹配 → 按路由模式处理。路由(Routes)决定收到的邮件去哪里:Endpoint(HTTP/SMTP/邮箱地址转发)、Accept(入库保存)、Hold(挂起)、Bounce(退信)、Reject(拒收)。

9.1 创建通配路由

界面路径:邮件服务器 → Routes → New Route,Name 填 *(匹配 example.com 全部地址),Domain 选 example.com,Mode 选 Accept,Spam Mode 保持 Mark。

用 console 核对(实测回显):

irb(main):001:0> r = Route.find(1)
irb(main):002:0> puts "ROUTE: name=#{r.name} domain=#{r.domain.name} mode=#{r.mode} token=#{r.token}"
ROUTE: name=* domain=example.com mode=Accept token=jttcO2cK
irb(main):003:0> exit

⚠️ 注意:通配路由必须绑定域名(Name 与 Domain 同时给出);只填 Name 会报 Validation failed: Domain can't be blank。除通配 * 外,也可以按 mailbox@domain、*@domain 粒度建多条路由,匹配优先级为精确地址 > 通配地址。

9.2 SMTP 收信实测

从外部地址向 example.com 发信(不需要认证——入站是 MX 行为,不是提交):

swaks --server 127.0.0.1:25 \
  --from outside@example.org --to test@example.com \
  --h-Subject 'Incoming route test' --body 'Hello Postal inbound.'
 === Trying 127.0.0.1:25...
 === Connected to 127.0.0.1.
<-  220 postal.example.com ESMTP Postal/G26GJIK9
 -> EHLO ops
<-  250-My capabilities are
<-  250 AUTH CRAM-MD5 PLAIN LOGIN
 -> MAIL FROM:<outside@example.org>
<-  250 OK
 -> RCPT TO:<test@example.com>
<-  250 OK
 -> DATA
<-  354 Go ahead
 -> Date: Mon, 19 Oct 2026 19:19:55 +0800
 -> To: test@example.com
 -> From: outside@example.org
 -> Subject: Incoming route test
 -> Message-Id: <20261019191955.4192284@ops>
 -> X-Mailer: swaks v20201014.0 jetmore.org/john/code/swaks/
 ->
 -> Hello Postal inbound.
 ->
 ->
 -> .
<-  250 OK
 -> QUIT
<-  221 Closing Connection
=== Connection closed with remote host.

9.3 接收结果核对

mysql -h127.0.0.1 -P13306 -uroot -pq1w2e3r4 -e \
  "SELECT id,scope,rcpt_to,mail_from,subject,status FROM \`postal-server-1\`.messages WHERE scope='incoming'\G" \
  2>&1 | grep -v Warning
*************************** 1. row ***************************
       id: 3
    scope: incoming
  rcpt_to: test@example.com
mail_from: outside@example.org
  subject: Incoming route test
   status: Processed
mysql -h127.0.0.1 -P13306 -uroot -pq1w2e3r4 -e \
  "SELECT id,message_id,status,details FROM \`postal-server-1\`.deliveries WHERE message_id=3;"
id      message_id      status          details
3       3               Processed       Message has been accepted but not sent to any endpoints.

💡 说明:Accept 模式把邮件存入消息库并标记 Processed;若把路由 Mode 改为 HTTP endpoint 并填一个 URL,Postal 会把邮件以 JSON POST 到该地址(webhook),改为 SMTP endpoint 则转发到目标 SMTP 服务器。模式切换后无需重启。

十、消息队列与日志查看

本节汇总入门阶段最常用的两类观测手段:界面/数据库里的消息状态,与三个服务进程的日志。

10.1 界面观测

  • 邮件服务器 → Messages → Outgoing:全部出站消息,状态列(Sent/Delivered/SoftFail/Held)与消息详情(投递尝试时间线、原始内容、大小、标签)
  • 邮件服务器 → Messages → Incoming:入站消息与路由处理结果
  • 消息详情页含 Deliveries 时间线(每次投递的状态与详情文本),即 deliveries 表的界面呈现

10.2 消息库常用查询

队列中的消息(含重试计划,实测回显;查询时队列中正有一条入站消息在处理):

mysql -h127.0.0.1 -P13306 -uroot -pq1w2e3r4 -e \
  "SELECT id,message_id,domain,attempts,retry_after,locked_by FROM postal.queued_messages;"
id      message_id      domain          attempts        retry_after     locked_by
12      11              example.com     0               NULL            NULL

最近投递流水(实测回显):

mysql -h127.0.0.1 -P13306 -uroot -pq1w2e3r4 -e \
  "SELECT id,message_id,status,LEFT(details,62) AS detail FROM \`postal-server-1\`.deliveries ORDER BY id DESC LIMIT 3;"
id      message_id      status  detail
13      11              Processed       Message has been accepted but not sent to any endpoints.
12      10              Sent    Message for api-rcpt@recv.test accepted by 127.0.0.1:2525 (127
11      9               Sent    Message for rcpt2@recv.test accepted by 127.0.0.1:2525 (127.0.

10.3 服务日志

三个服务的日志用 postal logs <服务名> 跟踪:web 记录 HTTP 请求、smtp 记录连接与会话(log_connections: true 时含连接级日志)、worker 记录队列调度与投递。SMTP 会话日志示例(实测回显,trace_id 与 7.1 节会话对应):

postal logs smtp 2>&1 | grep 'trace_id=IM76066A' | head -6
smtp-1  | 2026-10-19 11:18:43 +0000 DEBUG  Connection opened from 127.0.0.1 trace_id=IM76066A component=smtp-server
smtp-1  | 2026-10-19 11:18:43 +0000 DEBUG  Client identified as 127.0.0.1 trace_id=IM76066A component=smtp-server
smtp-1  | 2026-10-19 11:18:43 +0000 DEBUG  <= EHLO ops trace_id=IM76066A component=smtp-server
smtp-1  | 2026-10-19 11:18:43 +0000 DEBUG  => 250-My capabilities are trace_id=IM76066A component=smtp-server
smtp-1  | 2026-10-19 11:18:43 +0000 DEBUG  => 250 AUTH CRAM-MD5 PLAIN LOGIN trace_id=IM76066A component=smtp-server
smtp-1  | 2026-10-19 11:18:43 +0000 DEBUG  <= AUTH PLAIN [redacted] trace_id=IM76066A component=smtp-server

健康指标(Prometheus 格式,worker 9090 / smtp 9091,实测回显节选):

curl -s http://127.0.0.1:9090/metrics | grep '^postal_' | head -6
postal_worker_job_executions{thread="0",job="ProcessQueuedMessagesJob"} 1.0
postal_worker_job_runtime_bucket{thread="1",job="ProcessQueuedMessagesJob",le="0.005"} 0.0
postal_worker_job_runtime_bucket{thread="1",job="ProcessQueuedMessagesJob",le="0.01"} 53.0
postal_worker_job_runtime_bucket{thread="1",job="ProcessQueuedMessagesJob",le="0.025"} 93.0
postal_worker_job_runtime_bucket{thread="1",job="ProcessQueuedMessagesJob",le="0.05"} 93.0
postal_worker_job_runtime_bucket{thread="1",job="ProcessQueuedMessagesJob",le="0.1"} 93.0

十一、常见问题(FAQ)

Q1:SMTP 提交返回 550/From 域名不可用? 发件域名必须先在邮件服务器 → Domains 添加(且 verified)。未注册域名的发件人会被拒收。5.1 节域名验证口径见该节说明。

Q2:API 返回 unauthorized? X-Server-API-Key 用的是 API 类型凭据的 key,不是 SMTP 凭据;两者不通用。核对第六章凭据列表。

Q3:SMTP 认证 535 失败? 用户名必须是 x(固定),密码是 SMTP 凭据 key 本身(无空格)。swaks 用 --auth-user x --auth-password <key>。

Q4:消息一直停在队列不动? worker 未运行(postal status 查看)或出站目标不可达(见《Postal 日常运维与排障手册》投递失败排查章节)。用 10.2 节 queued_messages 查询 attempts/retry_after 判断是否在重试周期内。

Q5:测试邮件能发到任意地址吗? 本环境出站经 smtp_relays 指向的内部中继投递(127.0.0.1:2525),任意收件域都会被中继接收,用于链路验证;生产环境若未配置中继,Postal 直投收件域 MX,不可达域会产生 SoftFail/HardFail 与退信。

Q6:想跳过界面手工建资源可以吗? 可以,4.2/4.3 节的 console 方式支持全部资源对象化创建;postal console 属官方文档收录的命令入口,适合初始化自动化。

Q7:DKIM 校验在域名页一直不通过? 内网/无公网 DNS 环境的预期现象(本地模拟验证口径见 5.3 节)。公网环境按 5.2 节配置记录后,域名页点击 Verify 重查即可。

十二、参考链接