在 Mailcow 管理后台中逐个创建邮箱账号并不困难,但当一次需要创建十几个甚至更多账号时,重复填写用户名、密码、配额和启用状态会变得繁琐,也容易出现输入错误。
Mailcow 提供了管理 API,可以通过脚本批量创建邮箱。即使 Mailcow 使用 Docker 部署,调用方也不需要进入容器,只需通过 Mailcow 对外提供的 HTTPS 地址访问 API。
本文介绍一种相对安全、简单且容易检查的批量创建方法。
一、Mailcow API 是什么
Mailcow 的网页管理后台适合人工操作,而 API 适合程序化管理。
例如,创建邮箱通常使用类似下面的接口:
POST https://mail.example.com/api/v1/add/mailbox
请求中包含:
- 邮箱所属域名
- 邮箱用户名
- 显示名称
- 密码
- 邮箱配额
- 是否启用
- TLS 等附加设置
调用成功后,Mailcow 会像在网页后台手动创建一样,在系统中生成邮箱账号。
API 客户端与 Docker 容器之间没有直接关系,实际通信过程如下:
运行脚本的电脑
│
│ HTTPS + API Key
▼
Mailcow 管理 API
│
▼
Mailcow 内部完成邮箱创建
因此,脚本可以运行在:
- Mailcow 所在服务器
- 同一局域网内的管理电脑
- 其他能够访问 Mailcow 域名的可信主机
不需要进入 Docker 容器,也不需要直接操作数据库。
二、在 Mailcow 后台启用 API
登录 Mailcow 管理后台,进入系统配置中的 API 页面。
通常可以看到两类权限:
- Read-Only Access:只读 API
- Read-Write Access:读写 API
创建邮箱属于写操作,因此需要临时启用 Read-Write API。
建议遵循以下原则:
- 只在批量操作期间启用读写 API。
- 尽量限制允许访问 API 的来源 IP。
- 不要长期启用“跳过 IP 检查”。
- API Key 不要写入脚本。
- 操作完成后关闭读写 API,必要时重新生成密钥。
API Key 的权限通常接近管理员权限,应当按照高敏感凭据处理。
三、为什么使用 Python 脚本
理论上,可以直接使用 curl 调用 Mailcow API。
例如:
curl -X POST \
-H "X-API-Key: API_KEY" \
-H "Content-Type: application/json" \
-d '{"domain":"example.com","local_part":"user01"}' \
https://mail.example.com/api/v1/add/mailbox
但真实密码通常包含:
$
&
#
%
!
(
)
_
-
=
如果直接把大量复杂密码写进 Shell 命令,很容易遇到:
- Shell 变量展开
- 引号提前结束
- JSON 转义错误
- 反斜杠被错误处理
- 实际密码与原始密码不一致
Python 自带的 json 模块可以自动处理 JSON 转义,因此适合批量提交包含复杂密码的数据。
这并不表示 Mailcow 依赖 Python。Python 只是 API 客户端,真正执行邮箱创建的仍然是 Mailcow API。
四、准备邮箱账号数据
假设需要创建三个邮箱:
account01@example.com
account02@example.com
account03@example.com
每个邮箱使用不同的随机密码,并分配相同的小容量配额。
建议密码只使用兼容性较好的 ASCII 字符,例如:
abcdefghijklmnopqrstuvwxyz
ABCDEFGHIJKLMNOPQRSTUVWXYZ
0123456789
!@#$%^&*()-_=+
应避免使用容易混淆或可能在不同环境中被转换的字符,例如:
- 中文或全角标点
- 弯引号
“ ” - 不可见字符
- 换行符
- 制表符
ASCII 单引号、双引号和反斜杠并非不能作为密码字符,但会增加脚本和配置文件中的转义复杂度。普通用途下,没有必要刻意使用这些字符。
五、批量创建脚本示例
下面是一份简化后的 Python 示例。
其中的域名、邮箱名和密码均为演示数据,实际使用时应替换。
#!/usr/bin/env python3
import json
import os
import sys
import urllib.error
import urllib.request
MAILCOW_URL = "https://mail.example.com"
DOMAIN = "example.com"
QUOTA_MB = "20"
API_KEY = os.environ.get("MAILCOW_API_KEY")
if not API_KEY:
print("错误:未设置 MAILCOW_API_KEY", file=sys.stderr)
sys.exit(1)
mailboxes = [
("account01", "Example-Pass_01!Ab"),
("account02", "Example-Pass_02!Cd"),
("account03", "Example-Pass_03!Ef"),
]
endpoint = f"{MAILCOW_URL.rstrip('/')}/api/v1/add/mailbox"
success_count = 0
failed_count = 0
for local_part, password in mailboxes:
address = f"{local_part}@{DOMAIN}"
payload = {
"active": "1",
"domain": DOMAIN,
"local_part": local_part,
"name": local_part,
"password": password,
"password2": password,
"quota": QUOTA_MB,
"force_pw_update": "0",
"tls_enforce_in": "1",
"tls_enforce_out": "1",
}
request = urllib.request.Request(
endpoint,
data=json.dumps(payload).encode("utf-8"),
method="POST",
headers={
"X-API-Key": API_KEY,
"Content-Type": "application/json",
"Accept": "application/json",
},
)
try:
with urllib.request.urlopen(request, timeout=30) as response:
body = response.read().decode("utf-8", errors="replace")
result = json.loads(body)
entries = result if isinstance(result, list) else [result]
succeeded = any(
isinstance(entry, dict)
and entry.get("type") == "success"
for entry in entries
)
if succeeded:
print(f"[成功] {address}")
success_count += 1
else:
print(f"[失败] {address}")
print(json.dumps(result, ensure_ascii=False, indent=2))
failed_count += 1
except urllib.error.HTTPError as error:
body = error.read().decode("utf-8", errors="replace")
print(f"[HTTP {error.code}] {address}")
print(body)
failed_count += 1
except Exception as error:
print(f"[异常] {address}: {error}")
failed_count += 1
print()
print(f"完成:成功 {success_count} 个,失败 {failed_count} 个")
sys.exit(1 if failed_count else 0)
六、运行脚本
将脚本保存为:
create_mailboxes.py
先限制文件权限:
chmod 600 create_mailboxes.py
然后通过隐藏输入的方式读取 API Key:
read -rsp '请输入 Mailcow 读写 API Key: ' MAILCOW_API_KEY
echo
export MAILCOW_API_KEY
执行脚本:
python3 create_mailboxes.py
执行完成后立即清除环境变量:
unset MAILCOW_API_KEY
成功时会看到类似输出:
[成功] account01@example.com
[成功] account02@example.com
[成功] account03@example.com
完成:成功 3 个,失败 0 个
退出状态为 0,通常表示整批创建成功。
七、为什么不把 API Key 写进脚本
不应采用下面这种写法:
API_KEY = "真实密钥"
原因包括:
- 脚本可能被同步到云盘
- 文件可能进入备份
- 可能被提交到 Git 仓库
- 其他本地用户可能读取
- 后续分享脚本时容易忘记删除密钥
通过环境变量临时注入,可以让脚本本身不包含 API Key。
操作完成后执行:
unset MAILCOW_API_KEY
即可清除当前 Shell 环境中的变量。
需要注意的是,邮箱密码仍然写在批量创建脚本中。因此脚本使用完毕后,应当删除或转移到受保护的加密存储中。
rm -f create_mailboxes.py
八、常见失败原因
1. API Key 权限不足
只读 API Key 无法创建邮箱。
应使用临时启用的 Read-Write API Key。
2. 来源 IP 不在允许列表
Mailcow 可以限制哪些 IP 有权调用 API。
如果调用方不在允许范围内,请求会被拒绝。
3. 邮箱已经存在
同一个邮箱地址不能重复创建。
脚本会把该账号记录为失败,但一般不会影响其他账号。
4. 域名配额不足
每个邮箱的配额会计入域名总配额。
例如创建十个每个 20 MB 的邮箱,最多会占用:
10 × 20 MB = 200 MB
如果域名剩余配额不足,部分邮箱可能创建失败。
5. 密码不符合策略
Mailcow 可能要求密码满足长度或复杂度规则。
使用长度足够、包含大小写字母、数字和常用符号的随机密码,一般可以避免这一问题。
6. 地址配置错误
脚本中的:
MAILCOW_URL = "https://mail.example.com"
应当只填写 Mailcow 的基础域名。
不要填写:
https://mail.example.com/admin/system
因为 /admin/system 是网页后台路径,而不是 API 基础地址。
九、执行后的安全收尾
创建完成后,应当完成以下收尾工作:
- 在 Mailcow 后台确认所有邮箱都已出现。
- 检查邮箱配额是否正确。
- 关闭 Read-Write API。
- 必要时重新生成 API Key。
- 清除终端中的环境变量。
- 删除包含明文密码的批量脚本。
- 将账号和密码保存到密码管理器。
- 不要把账号密码保存到公开文档或普通聊天记录。
十、总结
Mailcow 的 Docker 部署方式不会妨碍远程 API 管理。只要 Mailcow 的 HTTPS 地址可以访问,并且调用方持有有效的读写 API Key,就可以从任意可信设备批量创建邮箱。
整个过程的核心是:
准备账号数据
↓
临时启用 Read-Write API
↓
通过环境变量提供 API Key
↓
Python 将账号数据转换为 JSON
↓
调用 Mailcow add/mailbox API
↓
逐个检查成功或失败
↓
关闭 API 并清理敏感文件
与直接修改数据库相比,使用 Mailcow API 更安全,也更符合系统本身的管理方式。对于需要批量创建独立邮箱账号的场景,这是一种简单、可控且便于审计的方法。