# PigeonPod: Self-Hosted YouTube and Bilibili Podcast Feeds

> PigeonPod turns YouTube and Bilibili channels into RSS feeds for any podcast app. It runs as a Spring Boot service with Docker or a JAR, and it is GPL-3.0 licensed.

**aizhimou/pigeon-pod** — Listen to YouTube & Bilibili. Anywhere.

- Repository: https://github.com/aizhimou/pigeon-pod
- Website: https://pigeonpod.cloud
- Stars: 1,185 · Forks: 105
- Language: Java
- License: GPL-3.0
- Published: 2026-09-10 · Updated: 2026-09-10 · Language: en
- Canonical page: https://hysenlabs.com/projects/aizhimou-pigeon-pod

## What PigeonPod Solves, and for Whom

Podcast apps speak RSS. YouTube and Bilibili do not publish RSS feeds for channels. PigeonPod sits between the two: it subscribes to a channel or playlist, downloads the media, and exposes a standard RSS feed that any podcast client can read. The README describes the project as a way to "Listen to YouTube & Bilibili, Anywhere, Anytime."

The target user is someone who already has a podcast app and wants to listen to video-platform audio inside it, without a separate player and without a phone screen staying on. Self-hosters who want their subscriptions and downloaded files on their own hardware are the primary audience. The README also points to a hosted option, PigeonPod Cloud, for people who say self-hosting is "too much hassle."

The feature list is wide: single-video subscriptions that become an auto-generated playlist feed, audio or video output with quality and format control, history backfill, cookies for restricted content, proxy routing, and per-feed filters by keyword and duration. Multi-user roles matter if more than one person shares the instance, because the README says system and feed configuration is restricted to admin users.

## How the Sync Pipeline Fits Together

The backend is Java 17 with Spring Boot 3.5, MyBatis-Plus for persistence, Sa-Token for authentication, SQLite as the database, and Flyway for migrations. RSS generation uses the Rome library. YouTube channel data comes from the YouTube Data API v3, and the actual media download is handled by yt-dlp.

That split matters. The API tells PigeonPod what exists on a channel; yt-dlp fetches the media. The README includes a YouTube API usage panel so you can watch quota before a sync job hits the limit. If you subscribe to many channels, the API quota, not your disk or network, is often the first ceiling you meet.

The frontend is React 19 built with Vite 7 and Mantine 8, served as static resources from the Spring Boot JAR. The Dockerfile confirms this: it builds the frontend with Node 22, copies the output into the backend's static resources, and packages everything with Maven. The runtime image is Wolfi base with ffmpeg, OpenJDK 17, Python 3, SQLite, Deno, and yt-dlp installed via pip.

Storage is either LOCAL or S3, and the README is explicit that only one mode can be active and that switching modes does not migrate historical media. Plan the mode before you accumulate files.

## Installing PigeonPod with Docker Compose

The README recommends Docker Compose. The repository ships a docker-compose.yml that builds from the Dockerfile and maps host port 8834 to container port 8080. It also sets a log file path inside the data volume.

```yaml
services:
  pigeon-pod:
    build: .
    image: pigeon-pod:latest
    restart: unless-stopped
    container_name: pigeon-pod
    ports:
      - '8834:8080'
    environment:
      - SPRING_DATASOURCE_URL=jdbc:sqlite:/data/pigeon-pod.db
      - PIGEON_LOG_FILE=/data/logs/pigeon-pod.log
    volumes:
      - pigeon-pod-data:/data

volumes:
  pigeon-pod-data:
```

The README's own compose example uses the published image ghcr.io/aizhimou/pigeon-pod:latest instead of a local build. Either way, start it with docker-compose up -d, then open http://localhost:8834. The default credentials in the README are username root and password Root@123, so change them immediately.

```bash
docker-compose up -d
```

If you prefer running the JAR, the README requires Java 17+ and yt-dlp on the host. Create a data directory next to the JAR and pass the SQLite path as a system property. The README's example uses a placeholder filename, so substitute the JAR you downloaded from Releases.

```bash
mkdir -p data
java -jar -Dspring.datasource.url=jdbc:sqlite:/path/to/your/pigeon-pod.db pigeon-pod-x.x.x.jar
```

Once inside, the first real task is adding a channel. Subscribe to a YouTube or Bilibili channel or playlist, let the sync run, then copy the feed URL into your podcast client. The README notes feeds can be protected, and that a public episode sharing page exists for playback without login.

## The Download Stack Is the Fragile Part

PigeonPod does not download video itself. It shells out to yt-dlp, and yt-dlp breaks when a platform changes its player or its anti-bot checks. The project acknowledges this by shipping in-app yt-dlp management: you can manage runtimes, switch active versions, and update yt-dlp without leaving the app. That is a useful mitigation, but it is still a mitigation. A self-hosted instance that is not updated can stop downloading while the rest of the application looks healthy.

The Docker image pins nothing about yt-dlp. The Dockerfile runs pip3 install --no-cache-dir "yt-dlp[default,curl-cffi]", so a rebuild picks up whatever is current at build time. Deno is installed in the image, which is relevant because yt-dlp increasingly relies on a JavaScript runtime for some extraction paths. If you run the JAR instead of Docker, none of that is provided: you supply Java 17+, yt-dlp, and by implication ffmpeg, since the Docker image sets PIGEON_FFMPEG_LOCATION=/usr/bin/ffmpeg.

There is also a legal boundary the README does not discuss. Downloading media from YouTube or Bilibili may conflict with those platforms' terms or with local copyright law depending on what you subscribe to. PigeonPod is a tool; the compliance question is yours.

## Auth, Proxies and the Trusted-Network Shortcut

PIGEON_AUTH_ENABLED defaults to true. The README carries a warning that you should set it to false only when another trusted layer already protects the web UI, such as an auth proxy, reverse proxy access control, VPN, or private network, and that an auth-disabled instance must not be exposed to the public Internet. This is a real footgun: the setting exists to support the "trusted-environment auto login" feature, and it is easy to copy a compose file with the flag commented out and later uncomment it without thinking.

The README also lists proxy-ready network access, routing YouTube API and yt-dlp traffic through custom proxies, and expanded cookie support for YouTube and Bilibili. Those three features address the same practical problem: from some networks, direct requests to these platforms fail or return restricted content. Cookies help with age-restricted or members-only material; proxies help with reachability. Neither is documented in the README beyond the feature bullet, so the wiki is where you would look for the actual configuration keys.

Built-in SSL/TLS is listed as a feature for encrypting web and RSS traffic. That is convenient for a single-container deployment, though many operators will terminate TLS at a reverse proxy instead and leave PigeonPod on plain HTTP behind it.

## Podsync and the Hosted Alternative

Podsync is the closest comparison, and it appears in the project's own topic list alongside listenbox and podsync. Podsync is a Go application that also turns YouTube channels and playlists into podcast feeds. The architectural difference is the runtime and the surrounding surface: Podsync is a single Go binary, while PigeonPod is a Spring Boot application with a React frontend, SQLite, Flyway migrations, and a multi-user permission model.

That difference cuts both ways. A Go binary is simpler to deploy and has fewer moving parts at runtime. PigeonPod's stack buys it a web UI with a download dashboard, bulk retry and cancel actions, per-feed filters, chapter generation, OPML export, and role-based access. If you want a headless feed generator, Podsync's approach is leaner. If you want a browsable interface where you can see failed downloads and retry them, PigeonPod is doing more work for you.

The other alternative is not self-hosting at all. The README promotes PigeonPod Cloud for people who find self-hosting a hassle. That is the honest trade: you give up control of the data and the instance, and in exchange you skip yt-dlp updates, storage planning, and TLS. The README does not state pricing for the cloud service.

## Licence, Maintenance and Upgrade Cost

PigeonPod is GPL-3.0. If you run it as a service for yourself, the licence mostly means that if you distribute modified versions, you must do so under the same licence and provide the source. If you fork it and offer it to others as a network service, consult the licence text and, if it matters to you, a lawyer. This is not legal advice.

The repository is not archived, and the last push was on 2026-08-19, roughly a month before this writing. Recent releases include 1.30.0 on 2026-06-05, 1.29.1 on 2026-05-27, and 1.28.0 on 2026-05-03. The release cadence in that window is steady.

Upgrade cost is mostly about two things. First, yt-dlp: the in-app manager lets you update the runtime without rebuilding the container, which lowers the cost of keeping downloads working. Second, the database: Flyway handles schema migrations, so upgrading the JAR or image should apply them on startup, but you should back up the SQLite file before a version jump. The README does not document rollback, so treat an upgrade as one-way unless you have a copy of the database. Storage mode is the other upgrade trap: the README states switching between LOCAL and S3 does not migrate historical media automatically.

## Conclusion

Adopt PigeonPod if you want your podcast client to play YouTube or Bilibili audio and you can run a Docker container with Java, yt-dlp and ffmpeg inside it. Do not adopt it if you cannot legally download the content you subscribe to, or if you need a hosted service with no server to run. Before you commit, verify that the container's yt-dlp version still downloads the sources you care about, and confirm whether your storage mode is LOCAL or S3, because switching later does not migrate existing media.

## FAQ

### How do I install PigeonPod with Docker?

The README recommends Docker Compose. Use the compose file with the image ghcr.io/aizhimou/pigeon-pod:latest, run docker-compose up -d, then open http://localhost:8834 and sign in with the default username root and password Root@123.

### What port does PigeonPod listen on?

The compose files map host port 8834 to container port 8080, so the web UI is at http://localhost:8834. When running the JAR directly, the README says to visit http://localhost:8080.

### Can I run PigeonPod without Docker?

Yes. The README documents running the release JAR with Java 17+ and yt-dlp installed on the machine, creating a data directory, and passing the SQLite path via -Dspring.datasource.url.

### Does PigeonPod support S3 storage?

The README says PigeonPod supports LOCAL and S3 storage modes, that only one can be enabled at a time, and that S3 mode works with MinIO, Cloudflare R2, AWS S3 and other S3-compatible services. Switching modes does not migrate historical media automatically.

## Sources

- [aizhimou/pigeon-pod on GitHub](https://github.com/aizhimou/pigeon-pod)
- [License: GPL-3.0](https://github.com/aizhimou/pigeon-pod/blob/main/LICENSE)
- [Project website](https://pigeonpod.cloud)
- [README](https://github.com/aizhimou/pigeon-pod/blob/main/README.md)
- [Releases](https://github.com/aizhimou/pigeon-pod/releases)

---

Hysen Labs editorial analysis, written from the project's own repository and release notes. Cite the canonical page: https://hysenlabs.com/projects/aizhimou-pigeon-pod
