Keila 0.30.3 Docker Compose 部署指南
使用官方镜像 pentacent/keila:0.30.3 与 Docker Compose 部署 Keila:镜像核对、三服务编排(应用+PostgreSQL+Mailpit)、管理员初始化、登录与发信验证、日常运维。
Keila 0.30.3 Docker Compose 部署指南
一、方案说明
Keila 官方提供打包镜像 pentacent/keila,是除源码构建外唯一的开箱即用部署形态(源码构建路线见《Debian 12 安装 Keila 0.30.3 指南》)。本文用 Docker Compose 编排三个服务完成一次完整部署:
| 服务 | 镜像 | 作用 |
|---|---|---|
| keila | pentacent/keila:0.30.3 | Keila 应用本体(内嵌 Oban 队列) |
| postgres | postgres:16-alpine | 唯一外部依赖,存储全部业务数据与作业队列 |
| mailpit | axllent/mailpit:latest | 测试收信端,验证发信链路 |
Compose 版本提示:本文验证环境的 Compose 为 v2 系列(
docker compose子命令)。老式docker-composev1 不支持 Compose 文件顶层的name字段,建议升级到 v2 后操作。
二、镜像版本核对
写作时官方镜像与源码同步发布的最新 tag 为 0.30.3。拉取并核对:
docker pull pentacent/keila:0.30.3
实测回显(镜像已在本地时状态行简化为镜像全名):
docker.io/pentacent/keila:0.30.3
核对镜像元信息:
docker image ls pentacent/keila --format '{{.Repository}}:{{.Tag}} {{.ID}} {{.Size}}'
docker inspect pentacent/keila:0.30.3 --format 'Created: {{.Created}}{{"\n"}}Entrypoint: {{json .Config.Entrypoint}}{{"\n"}}Cmd: {{json .Config.Cmd}}{{"\n"}}ExposedPort: {{json .Config.ExposedPorts}}'
实测回显:
pentacent/keila:0.30.3 1c880b8946ef 472MB
Created: 2026-09-06T09:29:07.292635837Z
Entrypoint: ["/opt/app/bin/keila"]
Cmd: ["start"]
ExposedPort: {"4000/tcp":{}}
要点:镜像内置入口为 /opt/app/bin/keila,默认命令 start(前台运行并自动执行数据库迁移与初始化);应用监听 4000 端口;镜像基于 Alpine,体积约 472MB(解压后)。
不建议使用
pentacent/keila:latest部署生产:floating tag 会在上游发版后指向未知新版本,升级不可控。固定版本 tag 才能保证重跑docker compose up -d时行为一致。
三、网络与目录准备
实验环境将本栈接入专用网络段(多组件共用时便于互相寻址),并准备数据目录:
docker network create keila-net --subnet 10.89.60.0/24 2>/dev/null; \
mkdir -p /opt/keila/pgdata /opt/keila/uploads
若本栈独占一台主机,可省略自定义网络,让 Compose 自动创建默认网络(删去编排文件中的
networks声明即可)。
四、Compose 编排文件
写入 /opt/keila/docker-compose.yml:
name: keila-0.30.3-stack
services:
keila:
image: pentacent/keila:0.30.3
container_name: keila-0.30.3-web
restart: unless-stopped
ports:
- "8137:4000"
environment:
SECRET_KEY_BASE: "3bYcNOSrXrBns3jcLQv8CXiMHCOB/TPvsEIOOfJy8pJ/HQh3Ty5pyHtsJkbFlF/X"
DB_URL: "postgres://keila:q1w2e3r4@postgres:5432/keila"
URL_HOST: "192.168.146.131:8137"
URL_SCHEMA: "http"
MAILER_SMTP_HOST: "mailpit"
MAILER_SMTP_PORT: "1025"
MAILER_SMTP_FROM_EMAIL: "newsletter@keila.local"
MAILER_SMTP_USER: "newsletter@keila.local"
MAILER_SMTP_PASSWORD: "unused"
MAILER_SMTP_AUTH_METHOD: "none"
MAILER_SMTP_TLS_MODE: "none"
KEILA_USER: "admin@keila.local"
KEILA_PASSWORD: "q1w2e3r4q1"
USER_CONTENT_DIR: "/opt/app/uploads"
volumes:
- ./uploads:/opt/app/uploads
depends_on:
- postgres
networks:
- keila-net
postgres:
image: postgres:16-alpine
container_name: keila-0.30.3-postgres
restart: unless-stopped
environment:
POSTGRES_USER: keila
POSTGRES_PASSWORD: q1w2e3r4
POSTGRES_DB: keila
volumes:
- ./pgdata:/var/lib/postgresql/data
networks:
- keila-net
mailpit:
image: axllent/mailpit:latest
container_name: keila-0.30.3-mailpit
restart: unless-stopped
ports:
- "8138:8025"
networks:
- keila-net
networks:
keila-net:
external: true
环境变量说明(完整清单见《Keila 0.30.3 配置参数详解》):
SECRET_KEY_BASE:至少 64 字符的会话签名密钥,docker run --rm alpine/openssl rand -base64 48或openssl rand -base64 48生成后固化进文件,切勿每次部署更换;DB_URL:指向同网络的postgres服务。POSTGRES_USER/PASSWORD/DB三个变量与之一一对应;URL_HOST:对外访问地址(用于生成邮件中的链接)。宿主机映射端口为 8137 时要写成IP:8137全拼;MAILER_SMTP_*:系统邮件通道。本文指向同网络的 mailpit(AUTH_METHOD=none+TLS_MODE=none);生产环境改为真实 SMTP 中继;KEILA_USER/KEILA_PASSWORD:首次启动自动创建的实例管理员(密码最少 10 个字符,故用q1w2e3r4q1);USER_CONTENT_DIR:联系人头像、Campaign 附件等用户内容的存储目录,映射到宿主机持久化。
name: keila-0.30.3-stack 显式声明栈名。注意 Compose v2 会把名字规范化(点号转为连字符),docker compose config 显示的实际栈名为 keila-0303-stack。
五、部署与初始化
cd /opt/keila && docker compose up -d
首次执行会拉取 postgres:16-alpine 与 axllent/mailpit:latest 后启动三个服务。确认状态:
docker compose ps --format '{{.Name}}\t{{.Image}}\t{{.Status}}'
实测回显:
keila-0.30.3-mailpit axllent/mailpit:latest Up 11 minutes (healthy)
keila-0.30.3-postgres postgres:16-alpine Up 11 minutes
keila-0.30.3-web pentacent/keila:0.30.3 Up 11 minutes
Keila 容器首次启动会自动完成两件事:数据库迁移(约 30 张表)与管理员创建。观察日志:
docker logs keila-0.30.3-web 2>&1 | grep -E "Running|create table" | head -8
实测回显:
2026-10-26T06:33:41.490 [info] Loading 119 CA(s) from :otp store
2026-10-26T06:33:42.061 [info] == Running 20201229080000 Keila.Repo.Migrations.Auth.change/0 forward
2026-10-26T06:33:42.062 [info] create table users
2026-10-26T06:33:42.066 [info] create index users_email_index
2026-10-26T06:33:42.068 [info] create table tokens
2026-10-26T06:33:42.071 [info] create table groups
2026-10-26T06:33:42.074 [info] create table roles
2026-10-26T06:33:42.076 [info] create table permissions
日志尾部出现 Created root user with admin@keila.local 即管理员初始化完成。日常重启不会重复初始化(种子只在库为空时执行),KEILA_USER/KEILA_PASSWORD 只在首次启动前有意义。
六、功能验证
6.1 Web 访问与登录
curl -s -o /dev/null -w 'GET http://192.168.146.131:8137/ -> HTTP %{http_code} redirect=%{redirect_url}\n' http://192.168.146.131:8137/
实测回显:
GET http://192.168.146.131:8137/ -> HTTP 302 redirect=http://192.168.146.131:8137/auth/login
浏览器打开 http://192.168.146.131:8137,用 admin@keila.local / q1w2e3r4q1 登录(密码重置邮件等系统邮件由 MAILER_SMTP_* 通道发出)。命令行等价验证:
curl -s -c ck.txt -o /dev/null http://localhost:8137/auth/login
CSRF=$(curl -s -b ck.txt -c ck.txt http://localhost:8137/auth/login \
| grep -oE 'name="_csrf_token"[^>]*' | grep -oE 'value="[^"]+"' | sed 's/value="//;s/"//')
curl -s -b ck.txt -c ck.txt -o /dev/null \
-w 'POST /auth/login -> %{http_code} redirect=%{redirect_url}\n' \
--data-urlencode "user[email]=admin@keila.local" \
--data-urlencode "user[password]=q1w2e3r4q1" \
--data-urlencode "_csrf_token=$CSRF" \
http://localhost:8137/auth/login
实测回显:
POST /auth/login -> 302 redirect=http://localhost:8137/
6.2 发信链路验证
Keila 的系统邮件(注册激活、密码重置)走 MAILER_SMTP_* 通道。用未登录会话提交一次密码重置表单触发一封系统邮件:
curl -s -c rk.txt -o /dev/null http://localhost:8137/auth/reset
CSRF=$(curl -s -b rk.txt -c rk.txt http://localhost:8137/auth/reset \
| grep -oE 'name="_csrf_token"[^>]*' | grep -oE 'value="[^"]+"' | sed 's/value="//;s/"//')
curl -s -b rk.txt -o /dev/null -w 'POST /auth/reset -> %{http_code}\n' \
--data-urlencode "user[email]=admin@keila.local" \
--data-urlencode "_csrf_token=$CSRF" \
http://localhost:8137/auth/reset
实测回显:
POST /auth/reset -> 200
随后查询 mailpit 的 HTTP API(Web 界面为 http://192.168.146.131:8138):
curl -s http://localhost:8138/api/v1/messages \
| python3 -c "import json,sys; d=json.load(sys.stdin); print('mailpit total:', d['total']); [print(m['From']['Address'], '->', [a['Address'] for a in m['To']], '|', m['Subject']) for m in d['messages'][:3]]"
实测回显(执行两次重置后共 2 封):
mailpit total: 2
newsletter@keila.local -> ['admin@keila.local'] | Your Account Reset Link
newsletter@keila.local -> ['admin@keila.local'] | Your Account Reset Link
重置邮件经 Keila → mailpit(SMTP 1025)完整送达,发信链路验证通过。生产环境中把 MAILER_SMTP_* 换成真实中继后,联系人收到的 Campaign 邮件走同一条链路。
七、日常管理
# 查看应用日志(Keila 使用标准输出,docker logs 即可)
docker compose logs -f keila
# 重启单个服务
docker compose restart keila
实测回显:
Container keila-0.30.3-web Restarting
Container keila-0.30.3-web Started
# 修改编排文件后重新应用(仅重建配置变化的容器,数据不受影响)
docker compose up -d
实测回显(配置无变化时全部保持 Running):
Container keila-0.30.3-mailpit Running
Container keila-0.30.3-web Running
Container keila-0.30.3-postgres Running
升级版本:把编排文件中的 pentacent/keila:0.30.3 换成新 tag 后执行 docker compose up -d,Keila 启动时会自动跑增量迁移。先备份 pgdata 再升级(见下)。
数据备份:需要备份的是宿主机上的 ./pgdata/(业务数据)与 ./uploads/(用户内容)两个目录。数据库建议逻辑备份替代文件拷贝,避免备份时刻数据不一致:
docker exec keila-0.30.3-postgres pg_dump -U keila keila > keila_$(date +%F).sql
彻底卸载:
docker compose down # 停止并删除容器与默认网络(external 网络保留)
rm -rf /opt/keila/pgdata /opt/keila/uploads # 删除数据(先备份)
docker network rm keila-net # 移除自定义网络
八、常见问题
8.1 容器反复重启,日志报 You must provide the DB_URL
DB_URL 未设置或拼写错误。Keila 的 runtime 配置在启动时强校验必填变量(DB_URL、SECRET_KEY_BASE、MAILER_SMTP_HOST、MAILER_SMTP_FROM_EMAIL),缺任何一项直接 System.halt(1) 退出,表现为容器反复重启。
8.2 启动报 random password 但不知道密码
首次启动时未设置 KEILA_USER/KEILA_PASSWORD,种子以 root@localhost + 随机密码创建了管理员(随机密码打印在启动日志的 warning 行)。清空 pgdata 目录重建即可让种子按新环境变量重新执行。
8.3 页面能打开但样式全丢
官方镜像内置了构建好的静态资源,正常不会出现该问题;若自行 build 过本地镜像,确认构建阶段执行了 mix assets.deploy。
8.4 登录后邮件里的链接指向 localhost
URL_HOST 未正确设置。Keila 用它生成邮件内链接与站点 URL,须写服务器对外可达的 IP:端口 或域名(含映射端口),不能只写容器内地址。
8.5 mailpit 健康检查失败 / 收不到测试邮件
mailpit 的 Web 界面端口是 8025(本文映射到宿主机 8138),SMTP 端口是 1025(仅容器网络内可用,未对外映射)。确认 MAILER_SMTP_HOST 写的是服务名 mailpit 且两者在同一网络。
8.6 SECRET_KEY_BASE 更换后所有会话失效
正常现象。该密钥参与会话签名,更换后旧 Cookie 全部失效,用户需要重新登录。它也派生 Hashid 盐(未显式设置 HASHID_SALT 时),中途更换会导致已有联系人的外部 ID 全部变化,务必首次部署就固化。
九、参考链接
- Keila 官方仓库(含 ops/docker-compose.yml 参考):https://github.com/pentacent/keila
- 官方镜像发布页:https://hub.docker.com/r/pentacent/keila
- Keila 官方网站与文档:https://www.keila.io
- 本专栏其他篇目:《Keila 0.30.3 介绍与选型指南》《Debian 12 安装 Keila 0.30.3 指南》