{"id":47594529,"url":"https://github.com/thekoma/rpodder","last_synced_at":"2026-04-01T17:57:09.516Z","repository":{"id":345377515,"uuid":"1183638792","full_name":"thekoma/rpodder","owner":"thekoma","description":"A modern, self-hostable gpodder.net replacement written in Rust. Sync podcast subscriptions and episode progress across all your devices.","archived":false,"fork":false,"pushed_at":"2026-03-26T11:18:12.000Z","size":1148,"stargazers_count":0,"open_issues_count":1,"forks_count":0,"subscribers_count":0,"default_branch":"main","last_synced_at":"2026-03-26T22:52:08.848Z","etag":null,"topics":["axum","docker","gpodder","kubernetes","oidc","podcast","podcast-sync","postgresql","prometheus","rest-api","rust","self-hosted","sqlite","sso","svelte"],"latest_commit_sha":null,"homepage":"https://thekoma.github.io/rpodder/","language":"Rust","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"agpl-3.0","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/thekoma.png","metadata":{"files":{"readme":"README.md","changelog":"CHANGELOG.md","contributing":null,"funding":null,"license":null,"code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null,"zenodo":null,"notice":null,"maintainers":null,"copyright":null,"agents":null,"dco":null,"cla":null}},"created_at":"2026-03-16T20:10:11.000Z","updated_at":"2026-03-26T11:07:21.000Z","dependencies_parsed_at":null,"dependency_job_id":null,"html_url":"https://github.com/thekoma/rpodder","commit_stats":null,"previous_names":["thekoma/rpodder"],"tags_count":42,"template":false,"template_full_name":null,"purl":"pkg:github/thekoma/rpodder","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/thekoma%2Frpodder","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/thekoma%2Frpodder/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/thekoma%2Frpodder/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/thekoma%2Frpodder/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/thekoma","download_url":"https://codeload.github.com/thekoma/rpodder/tar.gz/refs/heads/main","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/thekoma%2Frpodder/sbom","scorecard":null,"host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":286080680,"owners_count":31290710,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2026-04-01T13:12:26.723Z","status":"ssl_error","status_checked_at":"2026-04-01T13:12:25.102Z","response_time":53,"last_error":"SSL_read: unexpected eof while reading","robots_txt_status":"success","robots_txt_updated_at":"2025-07-24T06:49:26.215Z","robots_txt_url":"https://github.com/robots.txt","online":false,"can_crawl_api":true,"host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["axum","docker","gpodder","kubernetes","oidc","podcast","podcast-sync","postgresql","prometheus","rest-api","rust","self-hosted","sqlite","sso","svelte"],"created_at":"2026-04-01T17:57:08.603Z","updated_at":"2026-04-01T17:57:09.501Z","avatar_url":"https://github.com/thekoma.png","language":"Rust","funding_links":[],"categories":[],"sub_categories":[],"readme":"\u003cp align=\"center\"\u003e\n  \u003cimg src=\"web/static/logo.svg\" alt=\"rpodder logo\" width=\"200\" /\u003e\n\u003c/p\u003e\n\n\u003ch1 align=\"center\"\u003erpodder\u003c/h1\u003e\n\n\u003cp align=\"center\"\u003eA modern, fast, \u003ca href=\"https://gpodder.net\"\u003egpodder.net\u003c/a\u003e-compatible podcast sync server written in Rust.\u003c/p\u003e\n\n\u003cp align=\"center\"\u003e\n  \u003ca href=\"https://github.com/thekoma/rpodder/actions/workflows/ci.yml\"\u003e\u003cimg src=\"https://github.com/thekoma/rpodder/actions/workflows/ci.yml/badge.svg\" alt=\"CI\"\u003e\u003c/a\u003e\n  \u003ca href=\"https://github.com/thekoma/rpodder/pkgs/container/rpodder\"\u003e\u003cimg src=\"https://ghcr-badge.egpl.dev/thekoma/rpodder/latest_tag?trim=major\u0026label=latest\" alt=\"Latest Release\"\u003e\u003c/a\u003e\n  \u003ca href=\"https://ghcr.io/thekoma/rpodder\"\u003e\u003cimg src=\"https://ghcr-badge.egpl.dev/thekoma/rpodder/size\" alt=\"Image Size\"\u003e\u003c/a\u003e\n  \u003ca href=\"LICENSE\"\u003e\u003cimg src=\"https://img.shields.io/badge/license-AGPL--3.0-blue\" alt=\"License: AGPL-3.0\"\u003e\u003c/a\u003e\n  \u003cimg src=\"https://img.shields.io/badge/rust-edition%202024-orange?logo=rust\" alt=\"Rust Edition 2024\"\u003e\n  \u003cimg src=\"https://img.shields.io/badge/database-PostgreSQL%20%7C%20SQLite-336791?logo=postgresql\" alt=\"Database: PostgreSQL | SQLite\"\u003e\n  \u003cimg src=\"https://img.shields.io/badge/frontend-Svelte%205-FF3E00?logo=svelte\u0026logoColor=white\" alt=\"Frontend: Svelte 5\"\u003e\n  \u003cimg src=\"https://img.shields.io/badge/API-gpodder%20compatible-green\" alt=\"API: gpodder compatible\"\u003e\n  \u003cimg src=\"https://img.shields.io/badge/arch-amd64%20%7C%20arm64-blueviolet\" alt=\"Arch: amd64 | arm64\"\u003e\n  \u003cimg src=\"https://img.shields.io/badge/deploy-Docker%20%7C%20K8s-2496ED?logo=docker\u0026logoColor=white\" alt=\"Deploy: Docker | K8s\"\u003e\n  \u003ca href=\"https://thekoma.github.io/rpodder/\"\u003e\u003cimg src=\"https://img.shields.io/badge/docs-MkDocs-4051B5?logo=materialformkdocs\u0026logoColor=white\" alt=\"Docs\"\u003e\u003c/a\u003e\n  \u003cimg src=\"https://img.shields.io/badge/self--hosted-Raspberry%20Pi%20ready-C51A4A?logo=raspberrypi\u0026logoColor=white\" alt=\"Self-hosted: Raspberry Pi ready\"\u003e\n  \u003cimg src=\"https://img.shields.io/badge/SSO-OIDC%20%2F%20OAuth2-purple?logo=openid\" alt=\"SSO: OIDC / OAuth2\"\u003e\n\u003c/p\u003e\n\n\u003cp align=\"center\"\u003e\n  \u003cimg src=\"docs/src/assets/screenshots/discover-browse.png\" alt=\"rpodder screenshot\" width=\"700\" /\u003e\n\u003c/p\u003e\n\nSync your podcast subscriptions and episode progress across [AntennaPod](https://antennapod.org), [gPodder](https://gpodder.github.io), [Kasts](https://apps.kde.org/kasts/), and any client that supports the [gpodder.net API](https://gpoddernet.readthedocs.io/).\n\n## Features\n\n- **Full gpodder API** — subscriptions, episode actions, devices, sync groups, settings, favorites, chapters, podcast lists\n- **Web UI** — built-in Svelte 5 + Tailwind interface for browsing, searching, and managing podcasts\n- **User roles** — admin/user system with first-user auto-admin, full user management panel\n- **SSO/OAuth2** — OIDC single sign-on (Authentik, Keycloak, etc.) with group-based admin mapping\n- **Registration control** — open, closed, or invite-only (email activation)\n- **Password management** — self-service change/reset via email, admin set/reset, SSO-friendly\n- **HTTPS upgrade suggestions** — detects HTTP subscriptions with HTTPS alternatives, proposes upgrade\n- **Multi-format** — JSON, OPML, and TXT for subscription import/export\n- **Dual database** — PostgreSQL for scale, SQLite for self-hosting\n- **Feed indexing** — automatic feed fetching, parsing, fuzzy search, podcast directory\n- **Dynamic migrations** — reads all `*.up.sql` from migrations directory, no hardcoded filenames\n- **Privacy** — private/paid feeds (with tokens in URL) hidden from public directory\n- **Lightweight** — single binary with embedded UI, low memory footprint\n\n## Quickstart\n\n### SQLite (self-hosted)\n\n```bash\n# Build\ncargo build --release\n\n# Initialize database and create an admin user\n./target/release/rpodder migrate\n./target/release/rpodder user create myuser mypassword --admin\n\n# Start the server\n./target/release/rpodder serve\n```\n\nThe server starts on `http://localhost:3005`. Point your podcast app at this URL.\n\n### Docker (SQLite)\n\n```bash\ndocker compose --profile sqlite up -d\n# Create a user\ndocker compose exec rpodder-sqlite rpodder user create myuser mypassword --admin\n```\n\n### Docker (PostgreSQL)\n\n```bash\ndocker compose --profile release up -d\n```\n\n## Configuration\n\nAll settings can be set via environment variables or a TOML config file:\n\n| Env var | Default | Description |\n|---------|---------|-------------|\n| `RPODDER_DATABASE_URL` | `sqlite://rpodder.db` | Database connection URL |\n| `RPODDER_HOST` | `127.0.0.1` | Bind address |\n| `RPODDER_PORT` | `3005` | Bind port |\n| `RPODDER_RUN_MIGRATIONS` | `false` | Run migrations on startup |\n| `RPODDER_REGISTRATION` | `open` | `open` / `closed` / `invite` |\n| `RPODDER_SMTP_HOST` | | SMTP server for email features |\n| `RPODDER_OAUTH_ISSUER_URL` | | OIDC issuer URL for SSO |\n| `RPODDER_OAUTH_CLIENT_ID` | | OAuth2 client ID |\n| `RPODDER_OAUTH_CLIENT_SECRET` | | OAuth2 client secret |\n| `RPODDER_OAUTH_ADMIN_GROUP` | | OIDC group name for admin role |\n| `RPODDER_BASE_URL` | | Public URL for OAuth callbacks and emails |\n| `RPODDER_PODCASTINDEX_KEY` | | [Podcast Index](https://podcastindex.org/) API key (free) |\n| `RPODDER_PODCASTINDEX_SECRET` | | Podcast Index API secret |\n\nOr use a config file: `rpodder -c config.toml serve`\n\nSee [`config/rpodder.example.toml`](config/rpodder.example.toml) for an example.\n\n## CLI\n\n```\nrpodder serve                                    # Start the server\nrpodder migrate                                  # Run database migrations\nrpodder user create \u003cusername\u003e \u003cpassword\u003e         # Create a user\nrpodder user create \u003cusername\u003e \u003cpassword\u003e --admin # Create an admin user\nrpodder user delete \u003cusername\u003e                    # Deactivate a user\nrpodder repair                                   # Rebuild FTS5 index (SQLite)\n```\n\n## Client Setup\n\n### AntennaPod\nSettings → Synchronization → Choose provider → gpodder.net\nServer: `http://your-server:3005` • Username/Password as created above\n\n### Kasts (KDE)\nSettings → Synchronization → Custom server\n`http://your-server:3005`\n\n### gPodder Desktop\nPreferences → gpodder.net → Server URL: `http://your-server:3005`\n\n## API Endpoints\n\n| Endpoint | Description |\n|----------|-------------|\n| `POST /api/2/auth/{user}/login.json` | Login (HTTP Basic Auth) |\n| `GET/PUT /subscriptions/{user}/{device}.{json,opml,txt}` | Subscription sync |\n| `GET/POST /api/2/subscriptions/{user}/{device}.json` | Delta subscription sync |\n| `GET/POST /api/2/episodes/{user}.json` | Episode action sync |\n| `GET/POST /api/2/devices/{user}/{device}.json` | Device management |\n| `GET /api/2/me` | Current user info |\n| `POST /api/2/me/password` | Change password |\n| `GET /api/2/me/upgrades` | HTTPS upgrade suggestions |\n| `POST /api/2/register` | Public registration |\n| `POST /api/2/password-reset` | Request password reset |\n| `GET /search.json?q=...` | Podcast search |\n| `GET /toplist/{count}.json` | Top podcasts |\n| `GET /api/admin/users` | Admin: list users |\n| `GET /api/admin/stats` | Admin: server statistics |\n| `GET /health` | Health check |\n\nSee [`docs/API_REFERENCE.md`](docs/API_REFERENCE.md) for the complete API surface.\n\n## Systemd\n\n```bash\nsudo cp target/release/rpodder /usr/local/bin/\nsudo cp config/rpodder.service /etc/systemd/system/\nsudo useradd -r -s /bin/false rpodder\nsudo mkdir -p /var/lib/rpodder /usr/share/rpodder\nsudo cp -r migrations /usr/share/rpodder/\nsudo chown rpodder:rpodder /var/lib/rpodder\nsudo systemctl enable --now rpodder\n```\n\n## License\n\nAGPL-3.0-or-later\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fthekoma%2Frpodder","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fthekoma%2Frpodder","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fthekoma%2Frpodder/lists"}