Postal 快速入门与实战示例
Postal 3.3.7 快速入门实战:登录控制台、创建组织与邮件服务器、发信域名与 DKIM/DNS 记录、SMTP/HTTP API 两种发信、入站路由接收、消息队列与日志查看,全部命令与输出经 3.3.7 实测
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。
| 类型 | 主机记录 | 记录值 | 用途 |
|---|---|---|---|
| TXT | example.com | v=spf1 include:spf.postal.example.com ~all | 发信域 SPF |
| TXT | spf.postal.example.com | v=spf1 ip4:<服务器IP> ~all | 安装级 SPF |
| TXT | _dmarc.example.com | v=DMARC1; p=none; rua=mailto:dmarc@example.com | DMARC 策略(可选但建议) |
| TXT | postal-j5WBMx._domainkey.example.com | v=DKIM1; t=s; h=sha256; p=MIGfMA0…QIDAQAB; | 域级 DKIM 公钥 |
| CNAME | rp.example.com | rp.postal.example.com | 回程路径域(处理退信) |
| MX | rp.postal.example.com | mx.postal.example.com(优先级 10) | 安装域回程路径 |
| TXT | rp.postal.example.com | v=spf1 a mx include:spf.postal.example.com ~all | 回程路径 SPF |
| CNAME | psrp.postal.example.com | postal.example.com | 邮件服务器级回程路径 |
| MX | example.com | mx.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_body | HTML 正文,与 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 重查即可。
十二、参考链接
- 官方文档站(Getting Started):https://docs.postalserver.io/
- API 文档(v1 send/message 全参数):https://docs.postalserver.io/developer/api
- 路由与入站处理:https://docs.postalserver.io/features/routing-incoming-email
- 域名与 DNS:https://docs.postalserver.io/getting-started/dns-configuration
- swaks 官方:https://www.jetmore.org/john/code/swaks/