No description
  • Dockerfile 82%
  • Shell 18%
Find a file
2026-11-03 12:42:30 +08:00
radicale init 2026-11-03 12:42:30 +08:00
.dockerignore docker ig 2026-11-03 12:41:32 +08:00
compose.yml init 2026-11-03 12:42:30 +08:00
Dockerfile init 2026-11-03 12:42:30 +08:00
nginx.conf init 2026-11-03 12:42:30 +08:00
README.md init 2026-11-03 12:42:30 +08:00
update.sh init 2026-11-03 12:42:30 +08:00

Radicale vCards:Podman 部署

Bun Alpine 在构建阶段生成 vCards,Python Alpine 运行 Radicale 3.9.0;外部 Nginx 负责 HTTPS。

目录与设计

文件或容器路径 用途
Dockerfile 多阶段构建;最终镜像不包含 Bun、Git 或 vCards 源仓库
compose.yml 使用已构建镜像启动服务,仅发布 127.0.0.1:5232
radicale/config → /etc/radicale/config 服务配置
radicale/rights → /etc/radicale/rights 只读权限规则
/var/lib/radicale/collections/collection-root/cn 上游 radicale/ios 数据
/var/lib/radicale/collections/collection-root/cnmacos 上游 radicale/macos 数据
/usr/share/vcards/revision 本次构建使用的 vCards Git 提交
nginx.conf 同机 Nginx 的 HTTP 跳转、HTTPS 和 CardDAV 反代示例
update.sh 拉取上游、重建镜像,成功后替换容器

提供公开、只读的黄页:auth = none 不校验密码,rights 仅允许 cn 和 cnmacos 读取各自的数据。用户名是数据选择方式,不构成密码保护。客户端不能创建、修改或删除联系人。

不挂载数据卷:通讯录随镜像发布,Radicale 的锁文件和缓存写入容器可写层。重建容器后缓存及同步历史重置,客户端可能重新全量同步。不要把空目录或旧卷挂到 /var/lib/radicale/collections,否则会遮住镜像中的通讯录;也不要直接加 read_only: true,否则缓存和锁无法写入。

安装、构建、启动

安装工具(已安装可跳过):

sudo apt update
sudo apt install -y podman podman-compose unzip curl

将压缩包放到 VPS,解压到当前用户的主目录:

cd /opt
git clone https://git.forge.st/dallaslu/vcards-radicale
cd "vcards-radicale"
podman build --pull=always -t localhost/vcards-radicale:latest .
podman-compose -f compose.yml up -d
podman-compose -f compose.yml logs -f

构建和运行始终使用同一个宿主机用户,不要混用 podman 和 sudo podman。默认可使用普通用户的 rootless Podman;容器内进程另外使用 UID/GID 10001,无需在宿主机创建同名账户。

基础镜像从 Docker Hub 拉取;构建还需要访问 GitHub、Alpine 软件源、Bun 包源及 PyPI。运行服务不需要拉取 vCards 源仓库。

如需固定数据版本,可给 VCARDS_REF 传入兼容当前 Bun/ios/macos 构建结构的提交 SHA:

podman build --pull=always --build-arg VCARDS_REF=实际提交SHA -t localhost/vcards-radicale:latest .

Nginx

Radicale 在容器内监听 0.0.0.0:5232,宿主机仅发布 127.0.0.1:5232;这两个地址分别属于不同网络空间。Nginx 如果也在独立容器内,必须改为共享容器网络并通过服务名访问。

独立域名的根路径直接转发,不设置 /radicale 前缀;示例清除了客户端传入的 X-Script-Name。Nginx 默认传递 Authorization 请求头,示例没有启用 Nginx Basic Auth。

验证与客户端

在 VPS 上检查:

podman port vcards-radicale
curl -i -u cn:cn -X PROPFIND -H 'Depth: 1' http://127.0.0.1:5232/cn/

端口应映射到 127.0.0.1:5232,CardDAV 请求应返回 207 Multi-Status,响应列出地址簿。经 Nginx 再验证:

curl -i -u cn:cn -X PROPFIND -H 'Depth: 1' https://vcards.example.com/cn/

客户端服务器填写 https://vcards.example.com;iOS 使用用户名 cn,macOS 使用 cnmacos,密码可填写 cn。其它 CardDAV 客户端可根据是否需要分组选择相应账户。浏览器访问同一地址可打开 Radicale Web 界面。

更新

cd /opt/radicale
sh ./update.sh

脚本先用 --no-cache 构建当前上游,成功后才 up -d --force-recreate。仅 restart 不会替换容器镜像,也不会更新数据。RUN git clone 的构建缓存不会因远端分支更新而自动失效。替换容器会有短暂服务中断。

如需每周一 05:00 更新,可在运行 Podman 的同一用户下执行 crontab -e,添加以下内容;时间按 VPS 的系统时区解释:

0 5 * * 1 /bin/sh "/opt/vcards-radicale/update.sh" >> "/opt/vcards-radicale/update.log" 2>&1

查看运行数据的提交版本:

podman exec vcards-radicale cat /usr/share/vcards/revision

开机恢复

Compose 的 restart: always 负责进程退出重启;Podman 在宿主机重启后恢复容器,还需启用相应的 systemd 服务。普通用户 rootless 运行时,在该用户的登录会话中执行:

sudo loginctl enable-linger "$(id -un)"
systemctl --user enable podman-restart.service

若构建、运行都使用 root,则对应执行 sudo systemctl enable podman-restart.service。

验证范围

已在本地安装 Radicale 3.9.0,使用所附 config/rights 和临时样例通讯录执行 13 个 HTTP 检查:Web 页面、两套主目录发现、联系人读取、CardDAV REPORT 均正常;其它用户名、跨账户访问及 PUT/DELETE/MKCOL 被拒绝;并确认生成锁文件和缓存。Compose YAML 和更新脚本语法已检查。验证环境没有 Podman/Nginx,未实际执行完整镜像构建、上游通讯录生成或 HTTPS 联调。

参考