Pinepods Docker Compose 部署指南

基于 Pinepods 0.9.0 实测:三服务 Compose 编排、数据持久化与重启验证、初始化向导建管理员、添加真实播客 RSS 端到端验证与排障实录。

PinepodsPinepodsDockerDocker Compose播客2026/10/30

Pinepods Docker Compose 部署指南

目标:在一台 Linux 服务器上用 Docker Compose 完成自托管播客服务器 Pinepods 0.9.0 的整套部署,覆盖镜像选型、三服务编排(应用 + 数据库 + 缓存)、数据持久化、初始化向导建管理员、添加一档真实播客 RSS 的端到端验证,并沉淀部署过程实测排障记录。本文所有命令与回显均在真实部署环境执行验证。

一、环境信息

项目值说明
宿主系统Ubuntu(x86_64)Docker 环境由宿主提供
Docker 版本29.9.2docker --version 实测
Compose 版本v5.6.1docker compose version 实测
应用服务madeofpendletonwool/pinepods:0.9.0官方镜像,tag 固定版本
数据库服务postgres:18本文实测后端;官方默认编排用 mariadb:12
缓存服务valkey/valkey:8-alpine官方编排指定
应用端口宿主 8140 → 服务 8040(Web/API),服务内 8042(gPodder 同步)docker port 实测
数据目录/opt/pinepods/{pgdata,downloads,backups,local-media}bind mount 持久化
网络自定义 bridge,独立子网 10.89.64.0/24compose 自动创建

二、镜像选型说明

2.1 应用镜像

选型结论:使用官方镜像 madeofpendletonwool/pinepods,tag 固定为 0.9.0,不使用 latest——Pinepods 仍在 0.x 快速迭代期,固定 tag 可避免升级带来的行为变化。

实测镜像元数据(docker image inspect 输出节选):

{
  "Id": "sha256:ebc258bb0c62cdb69e5fb726b247c885dac19b50d06fed68fa8d37197b88ace4",
  "Created": "2026-06-26T14:35:06.86597788Z",
  "Architecture": "amd64",
  "Config": {
    "Env": ["PATH=/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin", "TZ=UTC", "APP_ROOT=/pinepods"],
    "ExposedPorts": { "8040/tcp": {}, "8042/tcp": {} },
    "Healthcheck": {
      "Test": ["CMD-SHELL", "curl -fsS http://localhost:8040/api/health || exit 1"],
      "Interval": 30000000000, "Timeout": 5000000000,
      "StartPeriod": 30000000000, "Retries": 3
    }
  }
}

三点解读:镜像构建日期与 0.9.0 发布日期(2026-06-26)同日,是版本正式构建;APP_ROOT=/pinepods 是应用在服务内的工作根目录;内置健康检查每 30 秒探测一次 /api/health,启动宽限期 30 秒,失败重试 3 次。

2.2 数据库与缓存镜像

  • 数据库:官方同时支持 MariaDB/MySQL 与 PostgreSQL(DB_TYPE 切换)。官方文档默认编排用 mariadb:12;本文实测环境选择 postgres:18,两种后端功能面一致。若选 MariaDB,把数据库服务替换为 mariadb 并改用 MYSQL_* 环境变量(差异对照见第六节)。
  • 缓存:valkey/valkey:8-alpine 为官方编排指定,应用通过 Redis 协议访问它,无需任何密码配置(仅内网通信)。

三、前置条件

  1. 一台 Linux 服务器(x86_64,建议 2 核 2GB 内存起步),已安装 Docker 与 Docker Compose 插件。
  2. 规划好数据根目录,本文统一用 /opt/pinepods/,先建好四个子目录并放行应用运行身份(服务内以 911:911 非 root 身份运行):
mkdir -p /opt/pinepods/{pgdata,downloads,backups,local-media}
ls /opt/pinepods

实测回显(目录创建后核对):

backups  downloads  local-media  pgdata
  1. 防火墙放行对外端口(本文映射到宿主 8140,按需调整)。

四、部署步骤

4.1 编写 compose 文件

在 /opt/pinepods/ 下创建 docker-compose.yml(官方默认编排为 MariaDB,下面是本文实测的 PostgreSQL 版本,数据库差异仅在这一段):

name: pinepods-stack

services:
  db:
    container_name: pinepods-db
    image: postgres:18
    environment:
      POSTGRES_DB: pinepods_database
      POSTGRES_USER: postgres
      POSTGRES_PASSWORD: myS3curepass
      PGDATA: /var/lib/pgdata/pgdata
    volumes:
      - /opt/pinepods/pgdata:/var/lib/pgdata
    restart: unless-stopped

  valkey:
    container_name: pinepods-valkey
    image: valkey/valkey:8-alpine
    restart: unless-stopped

  pinepods:
    container_name: pinepods-web
    image: madeofpendletonwool/pinepods:0.9.0
    ports:
      - "8140:8040"
    environment:
      SEARCH_API_URL: "https://search.pinepods.online/api/search"
      PEOPLE_API_URL: "https://people.pinepods.online"
      HOSTNAME: "http://<服务器IP或域名>:8140"
      DB_TYPE: postgresql
      DB_HOST: db
      DB_PORT: 5432
      DB_USER: postgres
      DB_PASSWORD: myS3curepass
      DB_NAME: pinepods_database
      VALKEY_HOST: valkey
      VALKEY_PORT: 6379
      DEBUG_MODE: false
      PUID: 911
      PGID: 911
      TZ: "Asia/Shanghai"
      DEFAULT_LANGUAGE: "en"
    volumes:
      - /opt/pinepods/downloads:/opt/pinepods/downloads
      - /opt/pinepods/backups:/opt/pinepods/backups
      - /opt/pinepods/local-media:/opt/pinepods/local-media
    depends_on:
      - db
      - valkey

逐段说明:db 服务把数据目录 bind mount 到宿主实现持久化;valkey 无需端口映射(仅编排网络内通信);pinepods 服务只对外映射 8040 一个端口,HOSTNAME 必须写成外部实际访问地址(分享链接、同步地址都会以它为准);PUID/PGID=911 是服务内运行身份,保持官方默认即可。

4.2 启动服务

cd /opt/pinepods && docker compose up -d
docker compose ps

实测回显(三服务全部就绪后的状态):

NAME                    IMAGE                                STATUS                    PORTS
pinepods-0-9-0-db       postgres:18                          Up 7 hours                5432/tcp
pinepods-0-9-0-valkey   valkey/valkey:8-alpine               Up 7 hours                6379/tcp
pinepods-0-9-0-web      madeofpendletonwool/pinepods:0.9.0   Up 43 seconds (healthy)   8042/tcp, 0.0.0.0:8140->8040/tcp, [::]:8140->8040/tcp

应用状态带 (healthy) 即通过内置健康检查;数据库与缓存不对外暴露端口是预期行为。

4.3 首次启动日志解读

docker compose logs pinepods | head -40 关键行实测(首次启动时序):

  PinePods 0.9.0
  https://pinepods.online

Setting timezone to Asia/Shanghai
Using mounted timezone file from host
Creating required directories...
Using postgresql database
2026-10-30 01:40:29,214 - INFO - Starting PinePods database setup...
2026-10-30 01:40:29,307 - WARNING - Connection attempt 1/30 failed: connection failed: connection to server at "10.89.64.2", port 5432 failed: Connection refused
2026-10-30 01:40:30,318 - INFO - PostgreSQL is ready after 2 attempts
2026-10-30 01:40:30,333 - INFO - Database pinepods_database already exists.
2026-10-30 01:40:30,373 - INFO - Registered migration 001: create_core_tables
2026-10-30 01:40:30,396 - INFO - Found 57 pending migrations
2026-10-30 01:40:30,625 - INFO - Database setup completed successfully!
Starting Horust in production mode (logs to files)...
Configuring non-root execution as 911:911...
Starting services as 911:911 with Horust...
PinePods startup complete, running Horust in foreground...

时序解读:应用先等数据库就绪(最多重试 30 次,首次启动数据库尚未监听时会出现 1 次 Connection refused 警告,属正常重试)→ 自动执行 57 个数据库迁移建表 → 用 Horust 进程管理器以 911:911 非 root 身份拉起各组件。出现最后一行即启动完成。

4.4 初始化向导:创建管理员

浏览器打开 http://<服务器IP>:8140,服务检测到无管理员账号时自动进入初始化向导。本文实测填入:用户名 admin、全名 Pinepods Admin、邮箱 admin@pinepods.local、密码 q1w2e3r4,提交后用该账号登录进入主界面。

向导是否已完成的接口验证回显(first_admin_created: true 即完成):

$ curl -s http://127.0.0.1:8140/api/data/admin_self_service_status -H "Api-Key: <你的API密钥>"
{"first_admin_created":true,"status":false}

登录后进入用户设置页创建一个 API 密钥(认证头 Api-Key 用),后续验证与运维接口都依赖它。

4.5 添加一档播客验证端到端链路

用 Web 界面搜索或直接用 API 添加一档真实公开播客。先查重(exists: false 表示可添加):

$ curl -s "http://127.0.0.1:8140/api/data/check_podcast?user_id=2&podcast_name=The%20Daily&podcast_url=https%3A%2F%2Ffeeds.simplecast.com%2F54nAGcIl" -H "Api-Key: <你的API密钥>"
{"exists":false}

再通过 RSS 添加(服务端会自动拉取封面、作者、剧集列表):

$ curl -s -X POST "http://127.0.0.1:8140/api/data/add_custom_podcast" -H "Api-Key: <你的API密钥>" -H "Content-Type: application/json" -d '{"feed_url":"https://feeds.simplecast.com/54nAGcIl","user_id":2}'
{"data":{"artworkurl":"https://image.simplecastcdn.com/images/7f2f4c05-9c2f-4deb-82b7-b538062bc22d/73549bf1-94b3-40ff-8aeb-b4054848ec1b/3000x3000/the-daily-album-art-original.jpg?aid=rss_feed","author":"The New York Times","autodownload":false,"categories":{},"description":"This is what the news should sound like. ...","episodecount":3021,"explicit":false,"feedcutoffdays":30,"feedurl":"https://feeds.simplecast.com/54nAGcIl","isyoutubechannel":false,"notificationsenabled":false,"password":null,"playbackspeed":1.0,"playbackspeedcustomized":false,"podcastid":3,"podcastindexid":0,"podcastname":"The Daily","startskip":0,"userid":2,"username":null,"websiteurl":"https://feeds.simplecast.com/54nAGcIl"}}

回显要点:podcastid=3 是服务端分配的订阅 ID,episodecount=3021 说明服务端已完整解析该 RSS 的剧集列表。此时浏览器刷新订阅页即可看到《The Daily》并可播放。API 的完整用法见《Pinepods 0.9.0 API 使用详解》。

五、数据持久化说明

挂载点(服务内)宿主目录内容
/var/lib/pgdata/opt/pinepods/pgdata数据库全部数据(用户/订阅/进度/队列)
/opt/pinepods/downloads/opt/pinepods/downloads下载的剧集音频/视频文件
/opt/pinepods/backups/opt/pinepods/backups备份产物
/opt/pinepods/local-media/opt/pinepods/local-media本地媒体库(可选功能)

重启持久化实测:重启应用服务后健康检查通过、订阅数据完好:

$ docker compose restart pinepods
 Container pinepods-0-9-0-web Restarting
 Container pinepods-0-9-0-web Started
$ curl -s http://127.0.0.1:8140/api/health
{"status":"healthy","database":true,"redis":true,"timestamp":"2026-10-30T00:21:10.885665443Z"}

同时用数据库侧复核订阅数(3 档:默认新闻播客 + 2 档测试添加的播客):

$ docker exec pinepods-0-9-0-db psql -U postgres -d pinepods_database -t -c 'select count(*) from "Podcasts";'
     3

两点注意:pgdata 是唯一不可丢失的目录,备份策略围绕它制定(服务自带备份能力见《Pinepods 0.9.0 配置参数详解》);downloads/backups/local-media 属于「可再生成/可选」数据,按磁盘预算决定是否保留。

六、环境变量表

6.1 应用服务(pinepods)

变量实测值说明
DB_TYPEpostgresql数据库类型,postgresql 或 mariadb,决定连接驱动与迁移方言
DB_HOSTdb数据库地址(编排内服务名)
DB_PORT5432数据库端口,PostgreSQL 5432 / MariaDB 3306
DB_USERpostgres数据库用户
DB_PASSWORDmyS3curepass数据库密码
DB_NAMEpinepods_database数据库名
VALKEY_HOSTvalkey缓存地址
VALKEY_PORT6379缓存端口
HOSTNAMEhttp://<服务器IP或域名>:8140对外访问地址,分享/同步链接以它为准
SEARCH_API_URLhttps://search.pinepods.online/api/search官方托管搜索 API;自建可换自托管搜索服务
PEOPLE_API_URLhttps://people.pinepods.online主持人信息库(PodPeopleDB)地址
DEBUG_MODEfalse调试日志开关
PUID / PGID911 / 911服务内运行身份(非 root)
TZAsia/Shanghai时区;启动日志会回显「Setting timezone to Asia/Shanghai」
DEFAULT_LANGUAGEen界面默认语言
PINEPODS_AI_URL未设置(可选)AI 转写侧车地址,需另起 pinepods-ai 服务才启用

6.2 数据库服务(PostgreSQL 与 MariaDB 对照)

用途PostgreSQL(本文实测)MariaDB(官方默认)
镜像postgres:18mariadb:12
库名变量POSTGRES_DB: pinepods_databaseMYSQL_DATABASE: pinepods_database
管理员密码变量POSTGRES_PASSWORD: myS3curepassMYSQL_ROOT_PASSWORD: myS3curepass
端口5432(DB_PORT 同步改)3306(官方另设 MYSQL_TCP_PORT: 3306)
数据目录PGDATA: /var/lib/pgdata/pgdata/var/lib/mysql
附加参数无command: --wait_timeout=1800、MYSQL_INIT_CONNECT 放大 max_allowed_packet 至 64MB(官方编排为导入大剧集元数据设置)

切换要点:两种后端除数据库段环境变量外,只需把应用的 DB_TYPE/DB_PORT 与之对齐,功能面一致;官方 MariaDB 编排的字符集用 utf8mb4/utf8mb4_unicode_ci,中文播客名实测(本文环境「声东击西」)在两种后端下均正常存取。

七、配置文件挂载

Pinepods 应用本体零配置文件:全部配置通过环境变量注入,运行期设置存数据库并从 Web 界面管理,因此不需要挂载任何配置文件。需要挂载的只有三类路径:

  1. 数据卷(见第五节表格):downloads / backups / local-media 三个功能目录 + 数据库数据目录。
  2. 时区文件(可选):官方默认编排挂载 /etc/localtime 与 /etc/timezone(只读)让服务时区与宿主一致;未挂载时用 TZ 环境变量亦可(实测两种方式取其一即可,启动日志「Using mounted timezone file from host」表示挂载方式生效)。
  3. 不要挂载应用目录:APP_ROOT=/pinepods 内是程序本体,挂载覆盖会破坏启动。

八、等价 docker run 命令

不用 Compose 时,以下三条命令等价于上面的编排(逐条执行,注意先后顺序):

docker network create pinepods-net
docker run -d --name pinepods-db --network pinepods-net \
  -e POSTGRES_DB=pinepods_database \
  -e POSTGRES_USER=postgres \
  -e POSTGRES_PASSWORD=myS3curepass \
  -e PGDATA=/var/lib/pgdata/pgdata \
  -v /opt/pinepods/pgdata:/var/lib/pgdata \
  --restart unless-stopped \
  postgres:18
docker run -d --name pinepods-valkey --network pinepods-net \
  --restart unless-stopped \
  valkey/valkey:8-alpine
docker run -d --name pinepods-web --network pinepods-net \
  -p 8140:8040 \
  -e SEARCH_API_URL="https://search.pinepods.online/api/search" \
  -e PEOPLE_API_URL="https://people.pinepods.online" \
  -e HOSTNAME="http://<服务器IP或域名>:8140" \
  -e DB_TYPE=postgresql \
  -e DB_HOST=pinepods-db \
  -e DB_PORT=5432 \
  -e DB_USER=postgres \
  -e DB_PASSWORD=myS3curepass \
  -e DB_NAME=pinepods_database \
  -e VALKEY_HOST=pinepods-valkey \
  -e VALKEY_PORT=6379 \
  -e DEBUG_MODE=false \
  -e PUID=911 -e PGID=911 \
  -e TZ="Asia/Shanghai" \
  -e DEFAULT_LANGUAGE="en" \
  -v /opt/pinepods/downloads:/opt/pinepods/downloads \
  -v /opt/pinepods/backups:/opt/pinepods/backups \
  -v /opt/pinepods/local-media:/opt/pinepods/local-media \
  --restart unless-stopped \
  madeofpendletonwool/pinepods:0.9.0

与 Compose 版的差异只有两处:服务名换成容器名(DB_HOST=pinepods-db、VALKEY_HOST=pinepods-valkey),网络需先手工创建。生产环境建议仍用 Compose,便于一键升级与回滚。

九、常用运维命令

docker compose ps                      # 三服务状态(应用应带 healthy)
docker compose logs -f pinepods        # 跟踪应用日志
docker compose restart pinepods        # 重启应用(不动数据库)
docker compose stop                    # 停止全部服务(保留数据)
docker compose up -d                   # 启动/应用配置变更

升级到新版本的标准流程:改 compose 中应用镜像 tag 为新版本号 → docker compose pull pinepods → docker compose up -d。启动时迁移机制会自动把数据库结构迁到新版本(首次部署实测执行 57 个迁移即此机制);回滚则把 tag 改回旧版本重新 up -d,但需注意新版本迁移不可自动逆转,回滚前先备份 /opt/pinepods/pgdata。

十、常见问题(排障实录)

Q1:应用启动日志出现 Connection attempt 1/30 failed: ... Connection refused。 实测结论:这是首次启动时数据库还未完成初始化监听,应用会自动重试(上限 30 次),实测第 2 次尝试即成功(「PostgreSQL is ready after 2 attempts」)。若持续 30 次失败,检查 DB_HOST 是否与数据库服务名一致、数据库服务是否处于 Up 状态(docker compose ps)。

Q2:应用容器状态一直 starting,不出现 healthy。 排查顺序:先 docker compose logs pinepods 看是否卡在数据库连接(对应 Q1);再看 /api/health 手工返回什么——实测健康检查同时报告数据库与缓存两层({"status":"healthy","database":true,"redis":true,...}),哪一层为 false 就查哪一层;本文实测一次重启后 30 秒内即恢复 healthy(镜像健康检查宽限期 30 秒)。

Q3:宿主端口 8140 被占用。 把 compose 中 ports 改为其他宿主端口(如 "9140:8040"),同时同步修改 HOSTNAME 环境变量(它必须与外部访问地址一致,否则分享链接与同步地址不可用)。

Q4:添加播客时返回 HTTP 400 Feed request failed: HTTP 404 Not Found。 实测复现:feed 地址失效时服务端原样返回该错误(实测用已失效的 RSS 地址添加得到 {"error":"Bad request","message":"Bad request: Feed request failed: HTTP 404 Not Found","status_code":400})。先在浏览器打开 RSS 地址确认可达,再重新添加。

Q5:中文播客名乱码或存库失败。 MariaDB 后端确认字符集变量为 utf8mb4(官方默认编排已带);PostgreSQL 后端默认 UTF8 无需处理。本文实测中文播客「声东击西」订阅、播放、删除全程正常。

十一、参考链接