Ubuntu 22.04 安装 Radicale 3.9 指南(pip + venv + systemd)

Ubuntu 22.04 裸机安装 Radicale 3.9.0 实测指南:pip+venv 在线与 wheelhouse 离线双方案、htpasswd 用户认证、systemd 常驻与 CalDAV/CardDAV 全链路 curl 验证。

RadicaleRadicaleCalDAV日历2026/10/30

Ubuntu 22.04 安装 Radicale 3.9 指南(pip + venv + systemd)

目标:在 Ubuntu 22.04 服务器上从零安装 Radicale 3.9.0,完成 venv 隔离安装、htpasswd 用户认证、systemd 常驻托管,并用 curl 实测 CalDAV/CardDAV 全链路(PROPFIND/MKCALENDAR/PUT/REPORT)。本文所有命令与回显均为 Ubuntu 22.04 + Radicale 3.9.0 实测结果。

一、环境信息

  • 操作系统:Ubuntu 22.04(x86_64)
  • Python:3.10.12(Ubuntu 22.04 自带)
  • Radicale:3.9.0(PyPI 当前最新稳定版,2026-10-30 实查;Python ≥3.9)
  • 安装方式:venv 虚拟环境 + pip 官方源(主方案)、pip wheelhouse 离线安装(备用方案)
  • 服务端口:5232(Radicale 默认端口,CalDAV/CardDAV 共用)
  • 配置目录:/etc/radicale/
  • 数据目录:/var/lib/radicale/collections/
  • 运行身份:专用系统用户 radicale(禁登录 shell)
  • 演示账号:admin / root,密码 q1w2e3r4

二、前置条件

安装开始前,确认系统具备以下条件:

  1. 一台可登录的 Ubuntu 22.04 服务器,具有 sudo 权限或 root 身份。
  2. Python 3.9 及以上(Ubuntu 22.04 自带 3.10.12,满足要求)。确认版本:
python3 --version    # 确认 Python 版本

实测回显:

Python 3.10.12
  1. 安装 venv 与 htpasswd 工具:venv 用于隔离 Radicale 的 Python 依赖(避免污染系统 Python 包),apache2-utils 提供生成认证口令文件的 htpasswd 命令:
apt-get update        # 刷新软件源索引
apt-get install -y python3-venv apache2-utils    # 安装 venv 与 htpasswd 工具

实测回显(节选):

Running hooks in /etc/ca-certificates/update.d...
done.
  1. 放行防火墙端口 5232(如启用 ufw):
ufw allow 5232/tcp    # 放行 Radicale 默认端口

⚠️ 注意:如果服务部署在内网且前面有 Nginx/Caddy 反向代理,则 5232 只需监听 127.0.0.1,无需对公网放行;对外只暴露反代的 443。反向代理配置思路可参考《Ubuntu 22.04 安装 Nginx 指南(apt/官方源/编译多方案)》与《Nginx HTTPS 与证书配置方案(certbot/自签名)》。

三、版本选型说明

截至 2026-10-30 实查(PyPI JSON API + GitHub Releases Atom 双渠道),Radicale 版本线如下:

版本发布主题说明
3.9.0Features+Fixes (Security)当前最新稳定版,2026-10-25 发布;本指南实测版本
3.8.3Fixes (Security)3.8 系列最后一个维护版
3.8.2Regressions and Feature功能与回归修复
3.8.1Fixes+Improvements+Extensions修复与扩展
3.8.0Features+Fixes+Improvements+Adjustments+Extensions3.8 系列起点

选型结论:新装一律选 3.9.0——它包含最新的安全修复(版本主题即标注 Security)与 3.9 引入的细粒度权限模型、共享(sharing)与通知(hook)能力。Radicale 遵循语义化版本,3.x 内升级兼容;不建议从 PyPI 安装预发布版本。

查询渠道(供复核):

pip index versions radicale    # 实查 PyPI 可用版本(需 pip 21.2+)

四、方案 A:pip + venv 在线安装(官方推荐)

本方案是官方文档主推的裸机形态:Python venv 隔离 + pip 官方源安装 + systemd 常驻。全程约 10 分钟。

4.1 创建专用系统用户与数据目录

Radicale 不应以 root 运行。创建无登录权限的系统用户,并准备数据目录:

useradd -r -s /usr/sbin/nologin radicale    # 创建系统用户(无 home、禁登录)
mkdir -p /var/lib/radicale                  # 创建数据目录
chown -R radicale:radicale /var/lib/radicale    # 数据目录归属 radicale 用户
mkdir -p /etc/radicale                      # 创建配置目录

实测回显:以上命令均无输出(Linux 惯例:成功即静默)。验证用户已创建:

id radicale    # 确认系统用户存在

实测回显:

uid=999(radicale) gid=999(radicale) groups=999(radicale)

4.2 创建 venv 并安装 Radicale

python3 -m venv /opt/radicale-venv              # 在 /opt 下创建独立虚拟环境
/opt/radicale-venv/bin/pip install --upgrade pip    # 升级 venv 内 pip
/opt/radicale-venv/bin/pip install radicale     # 从 PyPI 安装 Radicale 最新稳定版

实测回显(安装依赖解析与安装结果,节选):

Installing collected packages: pytz, urllib3, tzdata, six, pika, libpass, idna, defusedxml, charset_normalizer, certifi, requests, python-dateutil, vobject, radicale

Successfully installed certifi-2026.10.23 charset_normalizer-3.5.2 defusedxml-0.7.1 idna-3.20 libpass-1.9.4 pika-1.4.4 python-dateutil-3.0.0 pytz-2026.6 radicale-3.9.0 requests-2.34.2 six-1.17.0 tzdata-2026.6 urllib3-2.8.0 vobject-0.9.9

验证安装版本:

/opt/radicale-venv/bin/radicale --version

实测回显:

[2026-10-30 02:46:40 +0000] [36] [INFO] Logging level set to: 'INFO'
[2026-10-30 02:46:40 +0000] [36] [INFO] Logging of backtrace is disabled in this loglevel
3.9.0

💡 说明:--version 会先打印两行 INFO 日志再输出版本号,这是 3.9.x 的正常行为。核心依赖共 13 个:defusedxml(XML 安全解析)、libpass(口令哈希)、vobject(iCalendar/vCard 解析)、pika(RabbitMQ 客户端,hook 通知用)、requests 等——全部装进 venv,不碰系统 Python。

若官方 PyPI 源访问缓慢,可加 -i https://pypi.tuna.tsinghua.edu.cn/simple 走清华镜像(国内实测可用渠道),安装产物完全一致。

4.3 编写服务配置文件

创建 /etc/radicale/config(INI 格式)。本示例配置:监听全网卡 5232、htpasswd 文件认证、数据落 /var/lib/radicale/collections:

cat > /etc/radicale/config <<'EOF'
[server]
hosts = 0.0.0.0:5232

[auth]
type = htpasswd
htpasswd_filename = /etc/radicale/users
htpasswd_encryption = md5

[storage]
filesystem_folder = /var/lib/radicale/collections

[web]
type = internal

[logging]
level = info
EOF

实测回显:无输出。各参数的完整含义与取值范围见《Radicale 3.9 配置参数详解》。

4.4 创建认证用户(htpasswd)

用 htpasswd 生成口令文件。-m 采用 MD5-APR1 哈希(Radicale 内置支持,无需额外 Python 扩展),-b 从命令行读密码(首次建文件加 -c):

htpasswd -mb -c /etc/radicale/users admin q1w2e3r4    # 创建文件并添加第一个用户 admin
htpasswd -mb /etc/radicale/users root q1w2e3r4        # 追加第二个用户 root(注意:没有 -c)

实测回显:

Adding password for user admin
Adding password for user root

查看生成的口令文件(哈希值每次随机盐不同):

cat /etc/radicale/users

实测回显:

admin:$apr1$SmIWkUmx$qPpR9mk.JYaAUwQ6.81U01
root:$apr1$iyulEBrX$RKqcg.aZXS8wVKYJZoQYh0

💡 技术拓展:htpasswd 加密类型不止 MD5-APR1 一种,Radicale [auth] htpasswd_encryption 可选——

类型说明前置依赖
md5(MD5-APR1)Apache 口令传统格式,兼容性最好无(内置)
bcrypt更强哈希,抗暴力破解pip install radicale[bcrypt]
argon2新一代内存困难型哈希pip install radicale[argon2]
plain明文(仅调试用)无

本文实测采用 md5——零额外依赖且 htpasswd 命令原生支持;安全要求更高的生产环境建议 bcrypt/argon2。

4.5 配置 systemd 常驻

写入 /etc/systemd/system/radicale.service,让服务开机自启、崩溃自动拉起、以 radicale 用户身份运行:

cat > /etc/systemd/system/radicale.service <<'EOF'
[Unit]
Description=A simple CalDAV (calendar) and CardDAV (contact) server
After=network.target
Wants=network.target

[Service]
User=radicale
Group=radicale
ExecStart=/opt/radicale-venv/bin/radicale --config /etc/radicale/config
Restart=on-failure
RestartSec=3

[Install]
WantedBy=multi-user.target
EOF
systemctl daemon-reload               # 重新加载 unit 定义
systemctl enable --now radicale       # 设置开机自启并立即启动

实测回显:

Created symlink /etc/systemd/system/multi-user.target.wants/radicale.service -> /etc/systemd/system/radicale.service.

4.6 验证服务状态

systemctl is-active radicale      # 查看服务是否 active
systemctl status radicale --no-pager -l | head -12    # 查看状态详情

实测回显:

active
* radicale.service - A simple CalDAV (calendar) and CardDAV (contact) server
     Loaded: loaded (/etc/systemd/system/radicale.service; enabled; vendor preset: enabled)
     Active: active (running) since Fri 2026-10-30 02:47:12 UTC; 2s ago
   Main PID: 2703 (radicale)
      Tasks: 1 (limit: 2266)
     Memory: 19.5M
        CPU: 96ms
     CGroup: /system.slice/radicale.service
             `-2703 /opt/radicale-venv/bin/python3 /opt/radicale-venv/bin/radicale --config /etc/radicale/config

Oct 30 02:47:12 radicale radicale[2703]: [2703] [INFO] permit delete of collection: True

服务 active (running),常驻内存约 19.5M——印证 Radicale「轻量」的定位。

4.7 CalDAV/CardDAV 协议实测验证

服务已常驻,用 curl 走一遍真实客户端会执行的协议动作:认证拦截 → 用户目录发现 → 建日历 → 写事件 → 查询 → Web 界面 → 建地址簿 → 写联系人。

① 未认证请求应被 401 拒绝(3.9 默认安全基线):

curl -s -o /dev/null -w "HTTP %{http_code}\n" -X PROPFIND http://127.0.0.1:5232/admin/

实测回显:

HTTP 401

② 认证后发现用户 principal(CalDAV 客户端连接的第一步):

curl -s -X PROPFIND -u admin:q1w2e3r4 -H "Depth: 0" http://127.0.0.1:5232/admin/

实测回显(节选,XML 已去掉换行):

<?xml version='1.0' encoding='utf-8'?>
<multistatus xmlns="DAV:" xmlns:C="urn:ietf:params:xml:ns:caldav" xmlns:CR="urn:ietf:params:xml:ns:carddav" xmlns:RADICALE="http://radicale.org/ns/"><response><href>/admin/</href><propstat><prop><principal-collection-set><href>/</href></principal-collection-set><RADICALE:version>3.9.0</RADICALE:version><current-user-principal><href>/admin/</href></current-user-principal>…<C:calendar-home-set><href>/admin/</href></C:calendar-home-set></prop><status>HTTP/1.1 200 OK</status></propstat></response></multistatus>

响应中的 RADICALE:version 直接确认服务版本 3.9.0。

③ 建日历并写入一个事件:

curl -s -o /dev/null -w "MKCALENDAR HTTP %{http_code}\n" -X MKCALENDAR -u admin:q1w2e3r4 http://127.0.0.1:5232/admin/calendar/
curl -s -o /dev/null -w "PUT HTTP %{http_code}\n" -X PUT -u admin:q1w2e3r4 -H "Content-Type: text/calendar; charset=utf-8" --data-binary $'BEGIN:VCALENDAR\r\nVERSION:2.0\r\nPRODID:-//quzuo//radicale-test//CN\r\nBEGIN:VEVENT\r\nUID:event-2026-1030-001\r\nDTSTAMP:20261030T030000Z\r\nDTSTART:20261102T100000Z\r\nDTEND:20261102T110000Z\r\nSUMMARY:Radicale 上线验收会\r\nEND:VEVENT\r\nEND:VCALENDAR\r\n' http://127.0.0.1:5232/admin/calendar/event-2026-1030-001.ics

实测回显:

MKCALENDAR HTTP 201
PUT HTTP 201

④ REPORT 查询日历内容(模拟客户端增量同步):

curl -s -X REPORT -u admin:q1w2e3r4 -H "Depth: 1" -H "Content-Type: application/xml; charset=utf-8" --data-binary '<?xml version="1.0"?><c:calendar-query xmlns:d="DAV:" xmlns:c="urn:ietf:params:xml:ns:caldav"><d:prop><d:getetag/><c:calendar-data/></d:prop><c:filter><c:comp-filter name="VCALENDAR"/></c:filter></c:calendar-query>' http://127.0.0.1:5232/admin/calendar/

实测回显(节选):

<?xml version='1.0' encoding='utf-8'?>
<multistatus xmlns="DAV:" xmlns:C="urn:ietf:params:xml:ns:caldav"><response><href>/admin/calendar/event-2026-1030-001.ics</href><propstat><prop><getetag>"ceb8c8d5353e25c0e86f92392ac9fe854d30cbdb21669dfb739407d082dbb895"</getetag><C:calendar-data>BEGIN:VCALENDAR
VERSION:2.0
PRODID:-//quzuo//radicale-test//CN
BEGIN:VEVENT
UID:event-2026-1030-001
DTSTART:20261102T100000Z
DTEND:20261102T110000Z

⑤ 内置 Web 界面:

curl -s -o /dev/null -w "HTTP %{http_code}\n" -u admin:q1w2e3r4 http://127.0.0.1:5232/.web/

实测回显:

HTTP 200

浏览器访问 http://<服务器IP>:5232/.web/,用 admin / q1w2e3r4 登录即可看到刚写入的事件。

⑥ CardDAV 建地址簿并写入联系人:

curl -s -o /dev/null -w "MKCOL HTTP %{http_code}\n" -X MKCOL -u admin:q1w2e3r4 -H "Content-Type: application/xml; charset=utf-8" --data-binary '<?xml version="1.0"?><d:mkcol xmlns:d="DAV:" xmlns:cr="urn:ietf:params:xml:ns:carddav"><d:set><d:prop><d:resourcetype><d:collection/><cr:addressbook/></d:resourcetype><d:displayname>contacts</d:displayname></d:prop></d:set></d:mkcol>' http://127.0.0.1:5232/admin/contacts/
curl -s -o /dev/null -w "PUT HTTP %{http_code}\n" -X PUT -u admin:q1w2e3r4 -H "Content-Type: text/vcard; charset=utf-8" --data-binary $'BEGIN:VCARD\r\nVERSION:3.0\r\nUID:contact-001\r\nFN:张三\r\nTEL;TYPE=CELL:13800138000\r\nEMAIL:zhangsan@example.com\r\nEND:VCARD\r\n' http://127.0.0.1:5232/admin/contacts/contact-001.vcf
curl -s -u admin:q1w2e3r4 http://127.0.0.1:5232/admin/contacts/contact-001.vcf    # 回读联系人

实测回显:

MKCOL HTTP 201
PUT HTTP 201
BEGIN:VCARD
VERSION:3.0
UID:contact-001
EMAIL:zhangsan@example.com
FN:张三
TEL;TYPE=CELL:13800138000
END:VCARD

回读时 Radicale 自动重排了 vCard 属性顺序(FN/EMAIL/TEL 排序),这是服务端的规范化行为,数据无损。

4.8 查看数据文件落盘结构

所有数据以普通文件存储,直接观察磁盘即可确认:

find /var/lib/radicale/collections -type f | sort

实测回显:

/var/lib/radicale/collections/collection-root/admin/calendar/event-2026-1030-001.ics
/var/lib/radicale/collections/collection-root/admin/calendar/.Radicale.cache/history/event-2026-1030-001.ics
/var/lib/radicale/collections/collection-root/admin/calendar/.Radicale.cache/item/event-2026-1030-001.ics
/var/lib/radicale/collections/collection-root/admin/calendar/.Radicale.props
/var/lib/radicale/collections/collection-root/admin/contacts/contact-001.vcf
/var/lib/radicale/collections/collection-root/admin/contacts/.Radicale.cache/history/contact-001.vcf
/var/lib/radicale/collections/collection-root/admin/contacts/.Radicale.cache/item/contact-001.vcf
/var/lib/radicale/collections/collection-root/admin/contacts/.Radicale.props
/var/lib/radicale/collections/collection-root/.Radicale.lock

结构解读:collection-root/<用户>/ 下每个子目录是一个集合(日历或地址簿),.ics/.vcf 文件就是单条事件/联系人本体(可直接 cat 查看),.Radicale.props 存集合属性,.Radicale.cache/ 存同步缓存,.Radicale.lock 为运行期文件锁。备份 = 拷贝整个 collections 目录(先 stop 服务或避开写入瞬间)。

五、方案 B:wheelhouse 离线安装(断网/内网服务器)

内网隔离环境无法直连 PyPI 时,先在任一台同架构(x86_64)、同 Python 版本的联网机器上把 Radicale 及全部依赖下载为 wheel 包,再拷贝到内网服务器离线安装。

5.1 联网侧:下载离线 wheel 包

mkdir -p /tmp/wheelhouse
python3 -m pip download radicale -d /tmp/wheelhouse    # 下载 radicale 与全部依赖 wheel

实测回显(下载完成后检查产物):

ls /tmp/wheelhouse
certifi-2026.10.23-py3-none-any.whl
charset_normalizer-3.5.2-cp310-cp310-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl
defusedxml-0.7.1-py2.py3-none-any.whl
idna-3.20-py3-none-any.whl
libpass-1.9.4-py3-none-any.whl
pika-1.4.4-py3-none-any.whl
python_dateutil-3.0.0-py2.py3-none-any.whl
pytz-2026.6-py2.py3-none-any.whl
radicale-3.9.0-py3-none-any.whl
requests-2.34.2-py3-none-any.whl
six-1.17.0-py2.py3-none-any.whl
tzdata-2026.6-py2.py3-none-any.whl
urllib3-2.8.0-py3-none-any.whl
vobject-0.9.9-py2.py3-none-any.whl

共 14 个 wheel,合计约 2.5M,scp 或 U 盘即可带走。注意 charset_normalizer 是带平台标签的 wheel(manylinux2014_x86_64),联网机与内网机的 CPU 架构与 Python 大版本必须一致,否则离线安装会缺包。

5.2 内网侧:离线安装

把 wheelhouse 拷贝到内网服务器(示例路径 /opt/wheelhouse),在 venv 中以 --no-index --find-links 安装(完全不走网络):

python3 -m venv /opt/radicale-venv
/opt/radicale-venv/bin/pip install --no-index --find-links /opt/wheelhouse radicale

实测回显(在干净 venv 中离线装后验证):

/opt/radicale-venv/bin/radicale --version
3.9.0

后续配置、htpasswd、systemd 托管与方案 A 的 4.3~4.8 完全一致,此处不重复。

⚠️ 注意:python3-venv 与 apache2-utils 两个系统包在内网机也需要可用——无内网源时可从 Ubuntu 安装光盘镜像或 apt-get download 离线下载对应 deb 包。

六、方案对比与选择建议

维度方案 A:pip + venv 在线方案 B:wheelhouse 离线
是否需联网安装时需连 PyPI仅联网机下载,内网机零外联
版本灵活性任意版本即时可得固定下载时的版本
产物体积按需拉取14 wheel / 2.5M 随身携带
升级方式pip install -U radicale重新 download → 重新离线装
适用场景普通云服务器/VPS银行/政企内网、隔离生产区

选择建议:能联网选方案 A(官方主推,升级方便);内网隔离选方案 B,且 wheelhouse 目录随部署物一起归档,保证可重复安装。

七、配置说明(两方案共用)

安装完成后,无论哪个方案,都涉及以下三处固定路径:

  1. /etc/radicale/config:主配置文件(INI 格式)。本文 4.3 给出的最小可用配置已可运行;server/auth/storage/web/logging/headers 等全部 section 的逐参数详解(含默认值与安全建议)见《Radicale 3.9 配置参数详解》。
  2. /etc/radicale/users:htpasswd 口令文件。增删用户直接用 htpasswd 命令维护(追加用户不加 -c,删除用户用 -D),日常命令见《Radicale 3.9 常用 CLI 命令与运维详解》。
  3. /var/lib/radicale/collections/:数据目录。属主必须是运行服务的用户(本文为 radicale),否则启动后写入报 500:
chown -R radicale:radicale /var/lib/radicale    # 每次手工搬数据后都要重新授权
systemctl restart radicale                      # 改配置/权限后重启生效

⚠️ 注意:/etc/radicale/config 归属 root 即可(服务只读它);users 口令文件建议 chmod 640 /etc/radicale/users 并 chown root:radicale,避免全局可读。

八、常见问题(FAQ)

Q1:启动后所有请求都返回 401,连 Web 界面都打不开? 这是 3.9.0 的安全默认行为:[auth] type 默认 denyall(拒绝一切)。必须显式配置认证(如本文的 htpasswd)后重启服务。用 systemctl status radicale 与 journalctl -u radicale -n 20 可看到对应的 access denied 日志。

Q2:curl 建地址簿(MKCOL)返回 403,日志提示 rejected because of missing rights 'W'? 实测踩坑:默认 owner_only 权限下,用户对自己的两层路径(如 /admin/contacts/)只授予内容级权限字母 rw;MKCOL 请求的 XML body 若命名空间前缀不完整(如 <set> 写成无前缀形式而非 <d:set>),Radicale 解析不出 tag(地址簿 VADDRESSBOOK/日历 VCALENDAR),按「无类型普通集合」处理,要求大写 W 权限——于是被拒。解决办法:MKCOL 的 XML 必须给所有元素带正确的 d:/cr: 命名空间前缀(见 4.7 ⑥ 的写法),让服务端识别出这是地址簿集合,走小写 w 权限分支即通过。定位过程中 journalctl -u radicale 的关键日志行:

[WARNING] MKCOL request '/admin/contacts/' (type:UNKNOWN): rejected because of missing rights 'W'
[NOTICE] Access to '/admin/contacts/' denied for 'admin'
[NOTICE] MKCOL response status for '/admin/contacts/' in 1.373 seconds plain 43 bytes: 403 Forbidden

Q3:htpasswd 登录总失败,日志出现 Successful login 之前的失败记录? 逐项排查:① 文件路径与 htpasswd_filename 一致;② 加密类型匹配——htpasswd_encryption = autodetect 会按行自动识别,但若手工指定为 bcrypt 而 radicale[bcrypt] 扩展未装会报错;③ 密码含特殊字符时避免 shell 转义问题,改用交互式 htpasswd /etc/radicale/users 用户名。认证成功日志样例:[INFO] Successful login: 'admin' (htpasswd)。

Q4:服务写入报错 500,日志提示权限拒绝? 数据目录属主不是运行用户。执行 chown -R radicale:radicale /var/lib/radicale 后重启(见七、配置说明第 3 条)。

Q5:升级 Radicale 到新版本? venv 内一条命令,数据与配置均不受影响(存储格式向前兼容):

/opt/radicale-venv/bin/pip install -U radicale    # 升级到 PyPI 最新稳定版
systemctl restart radicale                        # 重启加载新版本
/opt/radicale-venv/bin/radicale --version         # 复核版本

升级前建议先备份数据目录(拷贝 /var/lib/radicale/collections),并跑一次 radicale --verify-storage 校验存储完整性(用法见《Radicale 3.9 常用 CLI 命令与运维详解》)。

九、卸载与清理

彻底移除 Radicale(含数据)的完整步骤:

systemctl disable --now radicale          # 停止并取消开机自启
rm -f /etc/systemd/system/radicale.service
systemctl daemon-reload                   # 清理 unit 后重载
rm -rf /opt/radicale-venv                 # 删除虚拟环境(程序本体)
rm -rf /etc/radicale                      # 删除配置与口令文件
rm -rf /var/lib/radicale                  # 删除数据目录(含全部日历/联系人!)
userdel radicale                          # 删除专用系统用户

实测回显:以上命令均无输出即成功;systemctl status radicale 应提示 Unit radicale.service could not be found.。

⚠️ 注意:rm -rf /var/lib/radicale 会永久删除所有用户的日历与联系人数据,卸载前如需保留,先把该目录整体备份。

十、参考链接

站内相关阅读: