A modern, fast, gpodder.net-compatible podcast sync server written in Rust.
Sync your podcast subscriptions and episode progress across AntennaPod, gPodder, Kasts, and any client that supports the gpodder.net API.
- Full gpodder API — subscriptions, episode actions, devices, sync groups, settings, favorites, chapters, podcast lists. Works with AntennaPod, gPodder, Kasts and other gpodder clients
- Reliable episode sync — positions uploaded late still reach your other devices (
sincefollows when the server received an action, not the phone's clock), updated positions are re-delivered,aggregated=truereturns the latest action per episode, no 1000-action cap - Sync groups — keep the subscriptions of several devices identical; the browser can join one too
- Built in — embedded in the binary, fonts and icons included: no outside requests except podcast artwork and audio
- Library — Continue listening across all your devices, subscriptions filed by letter, which device holds each podcast, last played, filter
- Web player — listen in the browser with a player bar that survives navigation and a full-screen view on phones; optional position sync as a gpodder device, so you resume where your phone stopped (and the phone where the browser stopped)
- Your data — add any feed by URL (private feeds too), download episodes, export OPML/TXT/JSON for the account or a single device
- Fresh episodes — feeds are checked at their own publishing rhythm (hourly for a news show, daily for a weekly one), and opening a podcast re-reads a feed that hasn't been checked for an hour
- Light and dark themes, phone layout with a bottom tab bar, History by day, Devices and sync-group editor, admin panel
- User roles — admin/user, first user becomes admin, full user management panel
- SSO / OAuth2 — OIDC single sign-on (Authentik, Keycloak, …), group-based admin mapping, login bound to the browser that started it (OAuth
state) - Registration control — open, closed, or invite-only (email activation)
- Passwords — self-service change and email reset, admin set/reset; any credential change or deactivation signs out the user's other sessions
- Privacy — private/paid feeds (tokens in the URL) are hidden from the public directory
- Directory and search — full-text search over every podcast on the server, top podcasts, categories
- Podcast Index — optional search across millions of podcasts and trending charts by language
- HTTPS upgrade suggestions — finds HTTP subscriptions that have an HTTPS address
- Dual database — PostgreSQL for scale, SQLite for self-hosting (Raspberry Pi friendly)
- Single binary, multi-arch Docker images (amd64/arm64), Helm chart, Prometheus metrics
- Self-applying migrations — every
*.up.sqlin the migrations directory, idempotent on both databases
See the documentation and the changelog for details.
# Build
cargo build --release
# Initialize database and create an admin user
./target/release/rpodder migrate
./target/release/rpodder user create myuser mypassword --admin
# Start the server
./target/release/rpodder serveThe server starts on http://localhost:3005. Point your podcast app at this URL.
docker compose --profile sqlite up -d
# Create a user
docker compose exec rpodder-sqlite rpodder user create myuser mypassword --admindocker compose --profile release up -dThe compose profiles above build from this checkout. To run the published image without building, use the deployment methods below.
Production self-hosting uses the published image
ghcr.io/thekoma/rpodder — no build required.
Ready-to-use compose files live in examples/. They import cleanly
into Dockge, Portainer and CasaOS.
# SQLite — simplest, single container
docker compose -f examples/docker-compose.yml up -d
# PostgreSQL — for multi-user instances
cp examples/.env.example examples/.env # then set POSTGRES_PASSWORD
docker compose -f examples/docker-compose.postgres.yml up -dCreate the first admin user:
docker compose -f examples/docker-compose.yml exec rpodder rpodder user create <name> <password> --adminA self-managed chart built on the bjw-s common library
lives in deploy/helm/rpodder/:
helm dependency build deploy/helm/rpodder
helm install rpodder deploy/helm/rpodder \
--set ingress.main.enabled=true \
--set 'ingress.main.hosts[0].host=podcasts.example.com'Defaults to SQLite on a PersistentVolumeClaim; point RPODDER_DATABASE_URL at
PostgreSQL for multi-user. See the chart README.
We're looking into TrueCharts support. Once we've carefully gone through their contribution guidelines, we may add rpodder to the catalog. Until then, use the Helm chart above.
See Systemd below.
All settings can be set via environment variables or a TOML config file:
| Env var | Default | Description |
|---|---|---|
RPODDER_DATABASE_URL |
sqlite://rpodder.db |
Database connection URL |
RPODDER_HOST |
0.0.0.0 |
Bind address |
RPODDER_PORT |
3005 |
Bind port |
RPODDER_RUN_MIGRATIONS |
true |
Run migrations on startup |
RPODDER_REGISTRATION |
open |
open / closed / invite |
RPODDER_SMTP_HOST |
SMTP server for email features | |
RPODDER_OAUTH_ISSUER_URL |
OIDC issuer URL for SSO | |
RPODDER_OAUTH_CLIENT_ID |
OAuth2 client ID | |
RPODDER_OAUTH_CLIENT_SECRET |
OAuth2 client secret | |
RPODDER_OAUTH_ADMIN_GROUP |
OIDC group name for admin role | |
RPODDER_BASE_URL |
Public URL for OAuth callbacks and emails | |
RPODDER_PODCASTINDEX_KEY |
Podcast Index API key (free) | |
RPODDER_PODCASTINDEX_SECRET |
Podcast Index API secret |
Or use a config file: rpodder -c config.toml serve
See config/rpodder.example.toml for an example.
rpodder serve # Start the server
rpodder migrate # Run database migrations
rpodder user create <username> <password> # Create a user
rpodder user create <username> <password> --admin # Create an admin user
rpodder user delete <username> # Deactivate a user
rpodder repair # Rebuild FTS5 index (SQLite)
Settings → Synchronization → Choose provider → gpodder.net
Server: http://your-server:3005 • Username/Password as created above
Settings → Synchronization → Custom server
http://your-server:3005
Preferences → gpodder.net → Server URL: http://your-server:3005
| Endpoint | Description |
|---|---|
POST /api/2/auth/{user}/login.json |
Login (HTTP Basic Auth) |
GET/PUT /subscriptions/{user}/{device}.{json,opml,txt} |
Subscription sync |
GET/POST /api/2/subscriptions/{user}/{device}.json |
Delta subscription sync |
GET/POST /api/2/episodes/{user}.json |
Episode action sync |
GET/POST /api/2/devices/{user}/{device}.json |
Device management |
GET /api/2/me |
Current user info |
POST /api/2/me/password |
Change password |
GET /api/2/me/upgrades |
HTTPS upgrade suggestions |
POST /api/2/register |
Public registration |
POST /api/2/password-reset |
Request password reset |
GET /search.json?q=... |
Podcast search |
GET /toplist/{count}.json |
Top podcasts |
GET /api/admin/users |
Admin: list users |
GET /api/admin/stats |
Admin: server statistics |
GET /health |
Health check |
See docs/API_REFERENCE.md for the complete API surface.
sudo cp target/release/rpodder /usr/local/bin/
sudo cp config/rpodder.service /etc/systemd/system/
sudo useradd -r -s /bin/false rpodder
sudo mkdir -p /var/lib/rpodder /usr/share/rpodder
sudo cp -r migrations /usr/share/rpodder/
sudo chown rpodder:rpodder /var/lib/rpodder
sudo systemctl enable --now rpodderAGPL-3.0-or-later
