最近实在是受不了 Umami 用起来各种黏滞感,还是回到了 Plausible CE 的怀抱。
几年前我曾写过 简单、隐私友好的谷歌分析替代品,Plausible 自托管部署指南 ,随着新版本发布,有些内容已经过时了。最新的 自托管版本 部署起来更简单。此外,默认仍然是仅支持国家级的地理区域识别。当添加 MaxMind 集成后,则可以精确到城市级,这会增加 1GB 左右的 RAM 占用。
按照 官方 Wiki 配置后,我检查了容器日志,发现一直出现以下报错:
plausible-clickhouse | 2025.11.28 03:06:51.659944 [ 1 ] {} <Warning> Context: Delay accounting is not enabled, OSIOWaitMicroseconds will not be gathered. You can ena
ble it using `echo 1 > /proc/sys/kernel/task_delayacct` or by using sysctl.
plausible | Plausible.IngestRepo database already exists
plausible | Creation of Db successful!
plausible | Loading plausible..
plausible | Starting dependencies..
plausible | Starting repos..
plausible | 03:07:02.357 [error] [locus] [geolocation] database failed to load (:remote): {:http, 451, ~c"Unavailable For Legal Reasons"}
plausible | 03:07:03.664 [error] [locus] [geolocation] database failed to load (:remote): {:http, 451, ~c"Unavailable For Legal Reasons"}
plausible | 03:07:05.515 [error] [locus] [geolocation] database failed to load (:remote): {:http, 451, ~c"Unavailable For Legal Reasons"}
plausible | 03:07:08.199 [error] [locus] [geolocation] database failed to load (:remote): {:http, 451, ~c"Unavailable For Legal Reasons"}
plausible | 03:07:12.181 [error] [locus] [geolocation] database failed to load (:remote): {:http, 451, ~c"Unavailable For Legal Reasons"}
plausible | 03:07:18.135 [error] [locus] [geolocation] database failed to load (:remote): {:http, 451, ~c"Unavailable For Legal Reasons"}
我试着添加一个 geoipupdate 服务,好嘛,容器运行后同样也是 HTTP 451。这时我还以为是因为 MaxMind 把数据库下载地址迁移到 Cloudflare R2 后,之前的存储桶预签名 URL 出了问题。于是我尝试手动下载,结果出现了:

去 Reddit 搜了下,一位自称 MaxMind 员工声称
他们不再提供免费 GeoLite 城市数据库,此限制仅适用于以下有限的国家或地区:中国/香港/澳门、古巴、伊朗、朝鲜、俄罗斯、委内瑞拉,访问 GeoLite 国家数据库的情况保持不变。
总之,当出现这种情况的时候,账号就已经被标记为 CN 了,除非换号。我尝试重新注册一个账号,然后因为 IP 不干净注册又失败了😅。说实话,这时候已经有点冒火了。(没错,就是无能狂怒~)

之前我在 GitHub 上提了 Issue #262 ,得到项目维护者回复,在新版本中,我仍可以通过手动下载可用的 GeoIP 数据库,映射到容器内部使用。
正好又发现一个 GitHub Action 定期发布 GeoLite 的存储库,由 P3TERX 大佬维护,我们从这里下载。开始前,做点准备工作。
# Plausible CE compose.yml template dir
pwd
# like this
/path/to/container/plausible
# change directory to above dir
cd /path/to/container/plausible
# for bash shell logs
mkdir -p geoip/logs
让 Gemini 协助,写了一个 Bash 脚本 update_geoip.sh
#!/bin/bash
DOWNLOAD_URL="https://github.com/P3TERX/GeoLite.mmdb/releases/latest/download/GeoLite2-City.mmdb"
# Plausible CE compose.yml template dir
PLAUSIBLE_DIR="/path/to/container/plausible"
# file user uid/gid
HOST_UID=1000
HOST_GID=1000
# target dir
TARGET_DIR="${PLAUSIBLE_DIR}/geoip"
TARGET_FILE="${TARGET_DIR}/city.mmdb"
TEMP_FILE="${TARGET_FILE}.tmp"
LOG_DIR="${TARGET_DIR}/logs"
COMPOSE_DIR="${PLAUSIBLE_DIR}"
# log format
LOG_FILE="${LOG_DIR}/$(date +%Y%m%d).log"
# check target dir
if [ ! -d "$TARGET_DIR" ]; then
echo "$(date '+%Y-%m-%d %H:%M:%S') - Error: Target directory '$TARGET_DIR' does not exist. Please create it first." | tee -a "$LOG_FILE"
exit 1
fi
# mk log dir
mkdir -p "$LOG_DIR"
echo "$(date '+%Y-%m-%d %H:%M:%S') - Script started. Attempting to download GeoIP DB..." | tee -a "$LOG_FILE"
# --- download logic---
echo "$(date '+%Y-%m-%d %H:%M:%S') - Downloading to temporary file $TEMP_FILE..." | tee -a "$LOG_FILE"
if curl -L --fail "$DOWNLOAD_URL" -o "$TEMP_FILE"; then
echo "$(date '+%Y-%m-%d %H:%M:%S') - Download successful. Setting ownership and permissions..." | tee -a "$LOG_FILE"
# set file permission to UID/GID 1000
if chown ${HOST_UID}:${HOST_GID} "$TEMP_FILE"; then
echo "$(date '+%Y-%m-%d %H:%M:%S') - Ownership set to ${HOST_UID}:${HOST_GID} for $TEMP_FILE." | tee -a "$LOG_FILE"
else
echo "$(date '+%Y-%m-%d %H:%M:%S') - Error: Failed to change ownership for $TEMP_FILE. Exiting." | tee -a "$LOG_FILE"
# rm temp file
rm -f "$TEMP_FILE"
exit 1
fi
if chmod 644 "$TEMP_FILE"; then
mv "$TEMP_FILE" "$TARGET_FILE"
echo "$(date '+%Y-%m-%d %H:%M:%S') - File updated and permissions set for $TARGET_FILE." | tee -a "$LOG_FILE"
else
echo "$(date '+%Y-%m-%d %H:%M:%S') - Error: Failed to set permissions for $TEMP_FILE. Exiting." | tee -a "$LOG_FILE"
rm -f "$TEMP_FILE"
exit 1
fi
# --- restart container ---
if ! cd "$COMPOSE_DIR"; then
echo "$(date '+%Y-%m-%d %H:%M:%S') - Error: Failed to change directory to $COMPOSE_DIR." | tee -a "$LOG_FILE"
exit 1
fi
echo "$(date '+%Y-%m-%d %H:%M:%S') - Stopping and removing containers (docker compose down)..." | tee -a "$LOG_FILE"
if docker compose down; then
echo "$(date '+%Y-%m-%d %H:%M:%S') - Containers stopped successfully. Starting them up again..." | tee -a "$LOG_FILE"
if docker compose up -d; then
echo "$(date '+%Y-%m-%d %H:%M:%S') - Containers started successfully." | tee -a "$LOG_FILE"
else
echo "$(date '+%Y-%m-%d %H:%M:%S') - Error: Failed to start containers (docker compose up -d). Check permissions." | tee -a "$LOG_FILE"
exit 1
fi
else
echo "$(date '+%Y-%m-%d %H:%M:%S') - Error: Failed to stop containers (docker compose down). Check permissions." | tee -a "$LOG_FILE"
exit 1
fi
else
echo "$(date '+%Y-%m-%d %H:%M:%S') - Error: Failed to download GeoLite2-City.mmdb. Curl exit code: $?." | tee -a "$LOG_FILE"
rm -f "$TEMP_FILE"
exit 1
fi
echo "$(date '+%Y-%m-%d %H:%M:%S') - Script finished successfully." | tee -a "$LOG_FILE"
exit 0
修改实际的路径以及合理的用户/组 ID(通常首个普通用户是 1000/1000,可使用 id 命令确认),保存后赋予脚本可执行权限
sudo chmod +x update-geoip.sh
尝试首次运行
sudo ./update-geoip.sh
2025-11-29 05:54:43 - Downloading GeoLite2-City.mmdb from https://github.com/P3TERX/GeoLite.mmdb/releases/latest/download/GeoLite2-City.mmdb...
% Total % Received % Xferd Average Speed Time Time Time Current
Dload Upload Total Spent Left Speed
0 0 0 0 0 0 0 0 --:--:-- --:--:-- --:--:-- 0
0 0 0 0 0 0 0 0 --:--:-- --:--:-- --:--:-- 0
100 60.2M 100 60.2M 0 0 53.1M 0 0:00:01 0:00:01 --:--:-- 53.1M
2025-11-29 05:54:45 - Download successful. File saved to /path/to/container/plausible/geoip/city.mmdb
2025-11-29 05:54:45 - Setting file permissions to 644 for /path/to/container/plausible/geoip/city.mmdb...
2025-11-29 05:54:45 - File permissions set for /path/to/container/plausible/geoip/city.mmdb.
修改 docker-compose.yml,找到这一部分
plausible:
image: ghcr.io/plausible/community-edition:v3.1.0
container_name: plausible
restart: unless-stopped
command: sh -c "/entrypoint.sh db createdb && /entrypoint.sh db migrate && /entrypoint.sh run"
depends_on:
plausible_db:
condition: service_healthy
plausible_events_db:
condition: service_healthy
volumes:
- ./plausible-data:/var/lib/plausible
添加一个 Docker 数据卷映射
volumes:
- ./plausible-data:/var/lib/plausible
+ - ./geoip/city.mmdb:/var/lib/plausible-mmdb/city.mmdb:ro
需要在 .env 里添加一个环境变量
IP_GEOLOCATION_DB=/var/lib/plausible-mmdb/city.mmdb
重启容器
sudo docker compose down && sudo docker compose up -d && sudo docker compose logs -f
打开 Plausible CE 控制台,切换到实时访客视图

可以看到区域和城市了

定期手动执行该脚本更新 GeoIP 数据库即可,或者,我们设置一个 Crontab 定时任务。考虑到对更新需求没那么大,我选择在 GitHub Actions 发布的两个周期左右,延迟几小时执行
sudo crontab -e
在最后添加一行(UTC 时间)
0 4 1,7,13,19,25 * * /bin/bash /path/to/container/plausible/update_geoip.sh
嗯,现在应该完全工作了。