Introduction
PodFetch is a simple, lightweight, fast, and efficient podcast downloader for hosting your own podcasts. It supports all the features you would expect from a podcast player, including:
- Downloading podcasts
- Listening to podcasts
- Searching for podcasts
- Managing your subscriptions
- Managing your podcast episodes
- Managing your podcast feed
- Host your own Gpodder compatible podcast feed
- Start listening on your phone and continue on your computer
All you need to do is download the latest release from the release page or use the listed docker-compose file to get started.
S3 configuration
So you want to use an S3 compatible storage backend to e.g. host files central or save costs for storage provisioning in the cloud? PodFetch now also supports S3 configuration. This is also valuable if you want to use a self-hosted MinIO instance and don’t want to map and mount volumes around. It is currently necessary that you have your files in S3 configured readonly so people can stream them from there.
| Environment variable | Description | Default |
|---|---|---|
S3_URL | The URL of the S3 service. | http://localhost:9000 |
S3_REGION | The region of the S3 service. | eu-west-1 |
S3_ACCESS_KEY | The access key of the S3 service. | / |
S3_SECRET_KEY | The secret key of the S3 service. | / |
S3_PROFILE | The profile of the S3 service. | / |
S3_SECURITY_TOKEN | The security token of the S3 service. | / |
S3_SESSION_TOKEN | The session token of the S3 service. | / |
Installation
Installation with Docker
Installation with Docker (SQLite)
version: '3'
services:
podfetch:
image: samuel19982/podfetch:latest
user: ${UID:-1000}:${GID:-1000}
ports:
- "80:8000"
volumes:
- podfetch-podcasts:/app/podcasts
- podfetch-db:/app/db
environment:
- POLLING_INTERVAL=60
- DATABASE_URL=sqlite:///app/db/podcast.db
volumes:
podfetch-podcasts:
podfetch-db:
| Variable | Description | Default |
|---|---|---|
| POLLING_INTERVAL | Interval in minutes to check for new episodes | 300 |
| PORT | The port PodFetch listens on | 8000 |
| SUB_DIRECTORY | Sub-path when hosting behind a reverse proxy (e.g. /podfetch) | (none) |
| DATABASE_URL | URL of the database | sqlite://./db/podcast.db |
| PODFETCH_FOLDER | Directory (inside the container) where podcast files are stored | podcasts |
It is important to change UID and GID to your user id and group id so that the files are owned by you and not by root.
Docker will create the volumes by default as root and podfetch will not be able to write to them.
Storing podcasts on a different drive
In most cases you don’t need PODFETCH_FOLDER. To put podcast files on a different drive (e.g. a separate HDD while the rest of the container stays on an SSD), just bind-mount that drive to the default /app/podcasts:
volumes:
- /mnt/hdd/podcasts:/app/podcasts
- podfetch-db:/app/db
If you do want to change the in-container path, set PODFETCH_FOLDER and mount the drive at that path. The directory (and any missing parents) will be created on startup.
Installation with Docker (Postgres)
To use postgres you need to set the following environment variables:
- DATABASE_URL=postgres://postgres:postgres@postgres:5432/podfetch
Installation without Docker
Requirements
- Download the latest release from the release page
- Create a shell script that sets the above environment variables and starts the podfetch binary
- Make the shell script executable
- Run the shell script
Terraform
For terraform have a look at the setup directory. There you will find everything needed to start with your infrastructure as code.
Mobile app
Podfetch also has a mobile app for Android and iOS. For Android you can download it directly from Obtainium. There is no iOS version because Apple requires a paid developer account to publish apps on the App Store, and Podfetch is a free and open source project. However if someone is willing to pay the 100€ fee I can publish it also for iOS.
Downloading and installing for Android
- Go to the Obtainium website and download the latest obtainium.apk file.
- Open Obtainium and put the url https://github.com/SamTV12345/PodFetch into the search bar. After that click on Search and it will say podfetch.apk is available in version x.x.x. Click on install and wait for the installation to finish.
- Open the app and enter the url of the PodFetch instance server you want to connect to. Depending on your auth settings you might also need to enter your username and password. After that click on Connect and you should be able to see your podcasts and episodes, and they sync via the standard PodFetch API.
- You can also download episodes for offline listening. When you are back online the app will automatically sync your downloaded episodes with the server, so you can listen to them on other devices as well.
Some impressions
[
]
[
]
[
]
Progressive Web App (PWA) on Android and iOS
You can also use the web UI as an installable PWA. This enables a standalone app-like experience and lock-screen media controls.
- Open your Podfetch web UI in the mobile browser (
https://your-server/ui/). - Android (Chrome/Edge): open browser menu and choose
Install app/Add to Home screen. - iOS (Safari): open Share menu and choose
Add to Home Screen. - Start playback once from the installed app. The media controls are then available from lock screen and notification controls.
Audiobookshelf API
PodFetch can answer to the audiobookshelf protocol. That means the official audiobookshelf mobile apps (Android and iOS) and the audiobookshelf web client connect to your PodFetch server, list your podcasts and play episodes with progress sync.
This page walks you through enabling the integration and pairing the apps.
1. Enable the integration
Set the environment variable and restart the server:
AUDIOBOOKSHELF_INTEGRATION_ENABLED=true
In docker-compose.yml:
services:
podfetch:
image: samuel19982/podfetch:latest
environment:
AUDIOBOOKSHELF_INTEGRATION_ENABLED: "true"
# … your other variables …
On startup PodFetch mounts the audiobookshelf-shaped routes at the root
paths the mobile apps hardcode (/login, /api/..., /public/...,
/hls/..., /socket.io/) and creates a default Podcasts library
containing every podcast you have already subscribed to in PodFetch.
2. Optional configuration
All of these have sensible defaults; only set them if you need to tweak the behaviour.
| Variable | Default | What it does |
|---|---|---|
AUDIOBOOKSHELF_DATA_DIR | <podfetch_data>/audiobookshelf | Working dir for the HLS segment cache. Point this at fast local storage if you transcode a lot. |
AUDIOBOOKSHELF_HLS_CACHE_MAX_MB | 2048 | Hard cap on the HLS segment cache (LRU eviction). Raise on big servers, lower on Raspberry Pis. |
AUDIOBOOKSHELF_TRANSCODER_MAX_CONCURRENT | 2 | Maximum ffmpeg processes that may run in parallel. One per concurrent transcoding listener. |
AUDIOBOOKSHELF_ROTATE_API_KEY_ON_LOGOUT | false | When true, signing out from the mobile app rotates users.api_key, immediately invalidating any other device still logged in with the old token. |
ffmpeg must be on PATH for HLS transcoding to work. The Docker image
ships with it; on a manual install verify with ffmpeg -version.
3. Install the mobile app
Pick the store that matches your phone:
- Android: Play Store, F-Droid or the APK from the project’s releases page.
- iOS: App Store.
4. Connect to PodFetch
- Open the app and pick Connect to server.
- Enter your PodFetch URL — the same URL you use for the web UI,
including the scheme:
https://podcasts.example.com. Do not append/ui/or any path. - Enter your PodFetch username and password. The same credentials the web UI uses; the app does not need a separate account.
- Tap Connect.
After a successful login the app shows a single library called Podcasts. Open it to see every podcast you have subscribed to in PodFetch. Tap an episode to start playback.
The token the app stores after login is your PodFetch users.api_key.
You can rotate it from the PodFetch web UI in Profile → API key — the
app will be signed out the next time it tries to sync.
5. Verify it works
Quick smoke test, in order:
- Listing: every podcast you subscribed to in PodFetch shows up.
- Playback: tap an episode. The player should open at the saved position (or 0 for the first play) and start within a few seconds. FLAC episodes take longer the first time because they are transcoded per segment.
- Progress sync: play 30 seconds, pause, force-close the app, re-open. The episode should resume at roughly where you stopped. Cross-check in the PodFetch web UI under Home → Continue listening.
- Stats: browse to Stats in the mobile app. After at least one played episode you should see a non-zero total and today’s listening time.
6. Adding new podcasts from the app
In the app, tap + and either
- type a search term (the app calls iTunes through PodFetch and shows matching podcasts), or
- paste an RSS feed URL directly.
Pick a podcast and confirm. PodFetch subscribes it the same way the web UI’s Add Podcast dialog does, so the new feed appears in PodFetch as well as in the app.
PodFetch has no concept of library folders, so the Library and Folder dropdowns in the audiobookshelf “New Podcast” form are ignored. The podcast always lands in the default Podcasts library.
7. Reverse proxy notes
If you front PodFetch with a reverse proxy, two things matter:
- Pass through
/socket.io/with WebSocket upgrade. The app uses socket.io for real-time progress and notification events; without it multi-device sync still works (over HTTP polling) but with a 5–10 second delay. - Forward the
Authorizationheader. PodFetch reads the bearer token from it; some default proxy configs strip unknown headers.
Sample Caddy snippet:
podcasts.example.com {
reverse_proxy localhost:8000
}
Sample nginx snippet (the important parts):
location / {
proxy_pass http://127.0.0.1:8000;
proxy_http_version 1.1;
proxy_set_header Host $host;
proxy_set_header Authorization $http_authorization;
proxy_set_header Upgrade $http_upgrade;
proxy_set_header Connection $connection_upgrade;
}
$connection_upgrade requires the standard nginx upgrade map:
map $http_upgrade $connection_upgrade {
default upgrade;
'' close;
}
Troubleshooting
The app accepts login but the library list is empty. The default Podcasts library is created during server startup. If you enabled the integration on a running server, restart PodFetch once. After restart, the library appears even if you don’t have any subscribed podcasts yet — but it will be empty until you add some.
Login fails with “Unexpected error”.
Most often the URL is wrong: the app’s Server URL field expects the
root (https://podcasts.example.com), not a path. Strip /ui/,
/login or any other suffix and include the scheme.
Playback button shows a spinner forever.
Check the server log for a line like POST /api/items/.../play …. If
it never appears, the bearer token did not reach PodFetch — your
reverse proxy is dropping the Authorization header. See section 7.
Progress does not sync between devices.
The socket.io connection is probably blocked. Check
https://your-server/socket.io/?EIO=4&transport=polling in the browser
— it should return a JSON handshake. If it returns a 502 or 404 your
proxy is not forwarding /socket.io/; add it to the proxy config.
FLAC / OGG episode stutters or won’t play.
PodFetch transcodes those to HLS/AAC via ffmpeg. Make sure ffmpeg is
on PATH. If transcoding is slow, raise
AUDIOBOOKSHELF_TRANSCODER_MAX_CONCURRENT (more parallel ffmpegs) or
point AUDIOBOOKSHELF_DATA_DIR at faster storage.
Cover images don’t appear. The app caches covers per server. After a server restart or an HTTPS certificate change, force-refresh once (pull-to-refresh on the library screen).
I want to sign all devices out at once.
Set AUDIOBOOKSHELF_ROTATE_API_KEY_ON_LOGOUT=true and log out from
one device. PodFetch generates a new users.api_key and every other
device drops to the login screen on its next sync. Alternatively,
rotate the key manually in Profile → API key in the web UI.
Disabling the integration
Set AUDIOBOOKSHELF_INTEGRATION_ENABLED=false (or remove the variable)
and restart PodFetch. The audiobookshelf routes disappear from the
server; the rest of PodFetch keeps working unchanged. Existing user
data (subscribed podcasts, listening history, playlists) stays — the
audiobookshelf endpoints just expose them in a different format.
Authorization
Basic Auth
Basic Auth is not required. If you use a reverse proxy like nginx you can use a better form that is also able to save passwords in your phone. If you decide to use basic auth you need to set all three variables below. Otherwise, the container will crash with an error message as a safety measure.
| Variable | Description |
|---|---|
| BASIC_AUTH | Set to true if you want to use basic auth |
| USERNAME | Username for basic auth |
| PASSWORD | Password for basic auth |
OIDC
PodFetch also supports OIDC authentication. If you want to use it you need to set the following variables.
If you enable it you need to disable BASIC_AUTH as it is not possible to use both at the same time.
Once you have created the user you intend to use as admin, you are then required to promote this user to admin via the command line.
Assuming your podfetch container is called $PODFETCH this can be done as follows, illustrating how the user sam is elevated to admin. (or uploader)
- Login with OIDC as user
sam - Run
docker exec -it $PODFETCH /app/podfetch users update - Enter the name
sam - Enter
role - Enter
admin - Login as
samagain and you should findsamis now an admin.
Keycloak
| Variable | Description | Example |
|---|---|---|
| OIDC_AUTH | Flag if OIDC should be enabled | true |
| OIDC_AUTHORITY | The url of the OIDC authority. | <keycloak-url>/realms/master |
| OIDC_CLIENT_ID | The client id of the OIDC client. | podfetch |
| OIDC_REDIRECT_URI | The URI the OIDC authority redirects to after authentication. | <your-server-url>/ui/login |
| OIDC_SCOPE | The scope of the oidc token | openid profile email |
| OIDC_JWKS | The JWKS token uri | <keycloak-url>/realms/master/protocol/openid-connect/certs |
Note: For OIDC authorities that allow for selecting between Confidential/Private and Public for the Client Type (for example Authentik), use Public, as PodFetch does not need a client secret.
Authelia
This assumes you already have OIDC set up in Authelia and your Authelia instance is being served on a subdomain https://auth.DOMAIN.COM with podfetch being served on it’s own subdomain at https://podfetch.DOMAIN.COM
Podfetch Configuration
| Variable | Description | Example |
|---|---|---|
| OIDC_AUTH | Flag if OIDC should be enabled | true |
| OIDC_AUTHORITY | The url of the OIDC authority. | https://auth.DOMAIN.COM |
| OIDC_CLIENT_ID | The client id of the OIDC client. | podfetch |
| OIDC_REDIRECT_URI | The URI the OIDC authority redirects to after authentication. | https://podfetch.DOMAIN.COM/ui/login |
| OIDC_SCOPE | The scope of the oidc token | openid profile email |
| OIDC_JWKS | The JWKS token uri | https://auth.DOMAIN.COM/jwks.json |
Authelia Configuration
Configure the OIDC client in Authelia as below, you can change your authorization_policy and consent_mode according to your needs.
- id: podfetch
description: Podfetch
public: true
authorization_policy: one_factor
scopes:
- openid
- profile
- email
consent_mode: explicit
redirect_uris:
- https://podfetch.DOMAIN.COM/ui/login
userinfo_signing_algorithm: none
Reverse Proxy
You can also use a reverse proxy like nginx to do the authentication. PodFetch supports this mode by setting the following variables:
| Variable | Description | Example |
|---|---|---|
| REVERSE_PROXY | Flag if reverse proxy should be enabled | true |
| REVERSE_PROXY_HEADER | The url of the reverse proxy. | X-FORWARDED-FOR |
| REVERSE_PROXY_AUTO_SIGN_UP | Flag if PodFetch should automatically sign up users | true |
User Creation
You can create an admin, user, or uploader either through CLI or via invites.
To generate an invite, log into Podfetch → Top Right Icon → User Administration → Invites
UI
Audio Player
The podcast listening tool contains an advanced audio player that can be used to listen to your podcasts,skip episodes, turn the volumes as high as 300% or skip around in the current episode.

Continue right where you stopped
The tool will automatically save your progress in the current episode and will resume from where you left off even if you close the browser. You can continue listening on all devices by just hitting play on any episode on your home screen.

Search for podcasts
You can search for podcast episodes by hitting CTRL+F and typing any word that might appear in the description or title of the podcast episode you want to listen to.

Below are some fullscreen images so you can get a better grasp of the UI.
Home Screen

Timeline View

Info View

Settings

Administration

Hosting
Proxy
PodFetch automatically derives its public URL from the incoming request headers. There is no need to configure a server URL manually.
Requirements
- Ensure your reverse proxy forwards the following headers to PodFetch:
X-Forwarded-Host— the public hostname (e.g.podfetch.example.com)X-Forwarded-Proto— the protocol (httpsorhttp)X-Forwarded-Prefix— the sub-path, if PodFetch is hosted under a sub-directory (e.g./podfetch)
- Turn on websocket support in your proxy
If you host PodFetch under a sub-path without a reverse proxy that sets X-Forwarded-Prefix, you can set the SUB_DIRECTORY environment variable instead (e.g. SUB_DIRECTORY=/podfetch).
The websocket protocol is determined from the forwarded protocol:
- https => Secured Websocket (wss)
- http => Unsecured Websocket (ws)
Telegram
PodFetch can also send messages via Telegram if a new episode was downloaded.
To enable it you need to set the following variables:
| Variable | Description | example |
|---|---|---|
| TELEGRAM_BOT_TOKEN | The Bot token that you can acquire from Botfather with /newbot | asdj23:hsifuhi234klerlf…sadasd |
| TELEGRAM_BOT_CHAT_ID | The chat id of the chat where the bot should send the messages | 123456789 |
| TELEGRAM_API_ENABLED | If the telegram api should be enabled. | true |
You can acquire the Telegram Bot chat id with the following steps:
- Write a message to the bot
- Open the following url in your browser: Telegram API page
- Search for the chat id in the response
Proxying requests to the podcast servers
In some cases you also need to proxy the traffic from the PodFetch server via a proxy. For that exists the PODFETCH_PROXY variable. You set it to the address of your proxy.
Translations
Internationalization
Podfetch is available in multiple languages.
If you want to add a new language you can do so by adding a new file to thei18n folder and adding the translations
to the file. The file should be named after the ISO 639-1
language code. For example en.json for English or de.json for German.
If you want to add a new language, please take a look at this file to see which translations are required. You only need to add the translations for the values.
RSS Feed
You can use the RSS feed to get the latest updates from the site. The feed is available at the following URL:
http://<your_ip>/rss
You can also control the output of the feed by using the following parameter:
- top: The number of items to return
So if you want to get the latest 5 items from the feed from each podcast, you can use the following URL:
http://<your_ip>/rss?top=5
Podcast Index
It is also possible to retrieve/add podcasts from Podcast Index. To configure it you need to create an account on that website. After creating an account an email is sent to you with the required credentials.
| Variable | Description | Default |
|---|---|---|
| PODINDEX_API_KEY | the api key sent to you via mail | % |
| PODINDEX_API_SECRET | the api secret also found in the mail | % |
- % means an empty string is configured as default
After successful setup you should see on the settings page a green checkmark next to the Podindex config section.
Chromecast
PodFetch can play podcast episodes on Chromecast (Google Cast) devices on the local network. Two deployment modes are supported:
- Direct — the PodFetch server runs on the same LAN as the Chromecast and speaks the CAST protocol itself.
- Agent relay — the PodFetch server is hosted somewhere remote
(a public instance, a VPS, a friend’s server) and a small
podfetch --agentprocess running on the user’s home LAN forwards Chromecast control commands to local devices.
The relay mode lets you use a hosted PodFetch UI without exposing your home network or running PodFetch in two places.
Permission model
Each Chromecast device has one of two kind values:
chromecast_personal— visible only to the user that owns it. Devices reported by an agent default to this kind, owned by the user whose API key the agent authenticated with.chromecast_shared— household device, visible to every user on the instance. An admin promotes a device from personal to shared.
When you cast an episode, you can pick from your personal devices plus every shared device. Active sessions are owned by the user who started them — only that user can pause/stop/seek; a different user starting a session on the same device implicitly stops the previous one.
Running an agent
A user on the remote instance must already have an API key. (Run
podfetch users to manage them — see CLI usage.)
On a machine on the home LAN that can see the Chromecasts:
podfetch --agent \
--remote https://podfetch.example.com \
--api-key YOUR_USER_API_KEY \
--agent-id home-lan
Flags:
| Flag | Required | Default | Description |
|---|---|---|---|
--remote | yes | — | Base URL of the PodFetch instance to connect to. |
--api-key | yes | — | Existing user API key on the remote instance. |
--agent-id | no | random UUID | Stable id; pass the same one across restarts so the server keeps a single agent identity. |
--proxy-port | no | 8011 | Reserved for the local episode-byte proxy (planned). |
The agent:
- browses the local network for
_googlecast._tcp.local.services (Chromecast mDNS records), - connects to
wss?://<remote>/agent/wswith aBearertoken, - pushes the discovered device list to the server, and
- forwards
Playrequests to the local Chromecast(s).
It reconnects with exponential backoff (1s → 2s → 4s … capped at 60s) if the websocket drops.
What works today
- Discovery: Chromecasts on the agent’s LAN appear in the UI as personal devices owned by the agent’s user.
- Play: starting playback on a Chromecast works end-to-end. The Default Media Receiver app is launched and the episode URL is loaded.
- Pause / Resume / Stop / Seek / SetVolume: forwarded to the receiver via the per-session worker that holds the CAST connection.
- Live status streamback: the agent polls the receiver every ~1.5s and pushes Status updates over the agent websocket. The UI’s progress bar reflects what the Chromecast actually plays, including external pauses or end-of-stream.
- Watchtime sync: every status update from the Chromecast is also persisted via the existing watchtime store, so the listened position during a cast session is visible everywhere PodFetch shows progress (episode resume, last-watched list, gPodder sync).
- Permission resolution: per-user vs household visibility is enforced on every API call.
Known limitations
- Codec transcoding: PodFetch does not transcode for Chromecast. Common podcast codecs (MP3, AAC) work; uncommon ones may fail at the receiver.
- Public-instance episode reachability: when running in agent
relay mode, the Chromecast still fetches the audio URL itself. If
the public PodFetch URL is not reachable from the home LAN, casting
will fail. A local agent proxy that re-serves bytes is on the roadmap
but not yet implemented (the
--proxy-portflag is reserved for it).
Troubleshooting
Agent connects but no devices appear. The mDNS browser only sees devices on the same broadcast domain. Check the agent and Chromecast are on the same VLAN, that mDNS isn’t filtered by the router, and that no firewall is blocking UDP port 5353 on the agent host.
Playback fails with “device unreachable” or a TLS error. Chromecasts present a self-signed certificate; PodFetch already disables hostname verification for that case. The remaining failure mode is the audio URL itself — make sure the URL the server sends is reachable from the Chromecast.
Agent disconnects every few minutes. Likely an HTTP proxy or load balancer in front of the remote PodFetch that idles long-lived websockets. Configure a longer idle timeout, or put PodFetch directly behind a TCP-level proxy.
CLI usage
The CLI can be used to manage users and to refresh & list subscribed podcasts.
You can get help anytime by typing --help or help.
Usage
Get general help
podfetch --help
Get help for a specific command
podfetch <command> --help
e.g.
podfetch users --help
podfetch podcasts --help
Running as a Chromecast agent
PodFetch can also run in agent mode, where it does not start an HTTP server but instead connects to a remote PodFetch instance over websocket and forwards Chromecast control commands to devices on the local LAN. See the Chromecast chapter for the full flow.
podfetch --agent \
--remote https://podfetch.example.com \
--api-key YOUR_USER_API_KEY \
--agent-id home-lan
Usage in docker
docker ps #This will help you obtain the container's id and name
docker exec -it <container id or name> /app/podfetch <your-command> # Will execute your desired command in the container
Contributing
Preamble
First of all, thank you for considering contributing to Podfetch. It is people like you that make Podfetch great. I appreciate every contribution, no matter how small it is. If you have any questions, don’t hesitate to ask them in the discussions section.
Building the project
Prerequisites
- Rust
- Cargo
- Node
- npm/yarn/pnpm
Building the app
# File just needs to be there
touch static/index.html
cargo.exe run --color=always --package podfetch --bin podfetch
cd ui
<npm/yarn/pnpm> install
<npm/yarn/pnpm> run dev
Setting up Basic auth and user management
- Set up the environment variables in your docker-compose under the PodFetch service:
BASIC_AUTH: true
USERNAME: test
PASSWORD: test
- Run
docker-compose up -dto start the service.
Make sure to change the username and password to something more secure than test and test.
UI Login
Enter your credentials from step 1. If you don’t want to enter always username and password you can click on memorize account. This is not recommended if this computer is not yours/shared with others.

- [Optional] Invite other users to your instance
Because listening to podcasts is kind of boring you can invite your family/friends to also use your podcast server to stream podcasts. Therefore go to User administration which is found in the top right corner when clicking on the user avatar icon.

Here you can view all the existing users:
You can edit their role by clicking on the pencil in the role column or delete them by clicking on the trash icon. You can create a new invitation by selecting the “Invites” tab on the right hand, Click on +Add new. Here you can select the role of the user to invite and if he is allowed to listen to explicit podcasts. After that click on createInvite. You can now see the invite in the table. It has an expiracy date of 1 week. So after 1 week the invite cannot be used again. You can click on the link icon to copy the invite link and paste it into e.g. Discord/WhatsApp or your messenger of choice.
On the other end the invitee can simply copy that link, paste it into his/her browser and take the registration steps. He/She can review the settings taken by the administrator and enter a username/password for the login page. The password needs to be strong. So it must have a length of 8, a number, a small letter and a capital letter.
E.g.:
- ✅ Test123$Test
- ✅ myComplicated$Password
- ❌ test
- ❌ weakPassword
Upon succesful registration you should see a success message with account created. Now the user should be redirected to /ui and can also login as the administrator has done it in the second step.
Setting up OIDC
GPodder API
See also: Kodi (RSS Podcasts addon) for using PodFetch with the Kodi RSS Podcasts addon.
📖 Activating the GPodder API
The following environment variable must be set to true to enable it:
| Variable | Description | Default |
|---|---|---|
| GPODDER_INTEGRATION_ENABLED | Activates the GPodder integration | false |
⚙️ Using the GPodder API
For the GPodder API, you need to choose a way to authenticate yourself. You can use:
- Basic Auth
- OAuth2
- Proxy Auth
👤 Create a user via the CLI
For AntennaPod to succesfully authenticate it requires a username and password. In order to enable that we jump to the commandline. If you have PodFetch running in Docker just execute:
docker ps #This will help you obtain the container's id and name
docker exec -it <your-container-id> /app/podfetch users add
After that a dialog opens up. I paste here a sample you can change username and password etc. to the values you like.
Starting from command line
User management
Enter your username:
myUsername
User does not exist
Enter your password:
Select your role user, admin, uploader
user
Should a user with the following settings be applied User { id: 0, username: "myUsername", role: "user", password: Some("myPassword"), explicit_consent: false, created_at: 2023-07-22T10:39:59.297771400 }
Y[es]/N[o]
Yes
User succesfully created
After that you have created a user with the username myUsername and the password: myPassword. It is enough to have him assigned to the role user. You could e.g. switch to another admin user that adds new podcasts for increased security.
🖂 Create a user via the UI
You can also generate a user using the invite function within the GUI
To generate an invite, log into Podfetch → Top Right Icon → User Administration → Invites . This works on both basic auth and OIDC.
📱 Setting up AntennaPod
First download the app:
- Google: https://play.google.com/store/apps/details?id=de.danoeh.antennapod&hl=de&gl=US
- FDroid: https://f-droid.org/de/packages/de.danoeh.antennapod/ ⚠️ Be aware that the FDroid version doesn’t have the Google signing for usage in an Android car. So if you have an Android Car and want to navigate via the built-in touchscreen you definitely need the one from the Google App Store
After opening the app, press the burger menu and tap settings. Go to synchronisation => Select provider => GPodder.net

- Enter here your PodFetch server URL (e.g. https://podfetch.example.com)
⚠️ AntennaPod always chooses https and thus requires a valid SSL certificate from e.g. Let’s Encrypt. If you want to host PodFetch in your local network it is okay to not use SSL. Therefore you can use http:// to establish an insecure connection which AntennaPod will complain in the next steps about.
- Enter the username and password of the user that you created before through the CLI

- Choose an existing device or create a new device
A user can have different devices or restore from existing device configuration. This is what the next view is about. You can choose between using an existing device. That is the ovale device below. This can be done by simply tapping on it.
If you don’t see a device because you started the server from scratch you can name your device to a memorable one so you can later select the correct device better.

- Success screen
You should now see this screen. Tap on Sync now and the modal should close leaving you at the synchronisation page.

-
Test the settings You can test the settings by tapping on Sync now. After a short period the title on the navbar should change to Success #currentTimestamp
-
Import the respective podcast into podcast Because not everybody can download/create a podcast it is necessary to also download a podcast to the server. Therefore go to the homepage and add the podcast. If the rss feeds are the same they are linked in the backend and you can listen to the podcasts from your phone or via the web and the two locations synchronize with each other.
Kodi (RSS Podcasts addon)
This page covers using the third-party Kodi addon RSS Podcasts by Heckie75 to import your PodFetch subscriptions into Kodi. The addon talks to PodFetch’s gpodder-compatible Simple API.
Prerequisites
- PodFetch reachable from the Kodi machine over HTTP or HTTPS.
GPODDER_INTEGRATION_ENABLED=trueon the PodFetch container — see Adding GPodder support § Activating the GPodder API.- A PodFetch user with username and password — see Adding GPodder support § Create a user via the CLI.
Setup in Kodi
- Install the addon. In Kodi, go to Add-ons → Install from repository → All repositories → Music → RSS Podcasts.
- Configure the provider. Open the addon and select Configure
→ Provider:
gpodder.net. - Enter the hostname. Use the bare host only — for example
podfetch.example.com. Do not include a scheme (https://), port suffix, or trailing slash; the addon adds those itself. - Enter credentials. Username and password of the PodFetch user you created in the prerequisites.
- Import subscriptions. Open the addon and choose Import subscriptions to group, then pick a Kodi group name. Your PodFetch subscriptions appear inside that group.
Known limitations
The “RSS Podcasts” addon talks to PodFetch on a deliberately narrow surface: it logs in once for a session cookie, then downloads your subscriptions as OPML. Everything else — episode play-state sync, device management, pushing changes back from Kodi — is not implemented by the addon itself, regardless of what PodFetch supports. The addon is effectively a one-way subscription importer.
| Feature | Kodi calls | PodFetch route | Status |
|---|---|---|---|
| Login (Basic Auth → session cookie) | POST /api/2/auth/{user}/login.json | login in gpodder_api/auth/authentication.rs | endpoint exists ✓ |
| Import subscriptions (OPML) | GET /subscriptions/{user}.opml | get_simple_subscriptions in gpodder_api/subscription/subscriptions.rs | endpoint exists ✓ |
| Push subscription changes from Kodi | — | — | not supported by the addon |
| Episode play-state / position sync | — | — | not supported by the addon |
| Device list / management | — | — | not supported by the addon |
The ✓ rows are empirically confirmed working end-to-end against recent PodFetch releases. If you hit a problem on either of those rows, please report it on issue #372. If you want bidirectional sync (episode actions, push), use AntennaPod via the GPodder tutorial — that flow exercises the full gpodder API.
Troubleshooting
Empty group / 401 after “Import subscriptions to group”
The most common cause is a reverse proxy stripping or failing to
forward the Authorization header. PodFetch’s Simple API uses HTTP
Basic Auth.
First, confirm the endpoint works directly:
curl -i -u <user>:<pass> https://<host>/subscriptions/<user>.opml
A working response is HTTP/1.1 200 OK with
Content-Type: text/x-opml+xml and an OPML body. If curl works
but Kodi fails, the proxy is stripping Authorization.
- nginx — make sure no
auth_requestdirective overrides the upstream auth, and that anyproxy_set_headerblock does not unsetAuthorization. nginx forwards it by default unless you remove it. - Traefik — by default the
Authorizationheader is forwarded. Check that no customheadersmiddleware lists it undercustomRequestHeaderswith an empty value (which would strip it).
“Provider gpodder.net” rejects credentials
Check, in order:
GPODDER_INTEGRATION_ENABLED=trueis set on the PodFetch container and the container has been restarted since.- The user actually exists. From the host, run
docker exec -it <container> /app/podfetch users add— if it reports the user already exists, that confirms the account. - The hostname field in Kodi has no scheme prefix (
http:///https://) and no trailing slash.
Import succeeds but the group is empty
PodFetch’s OPML endpoint only emits <outline> entries for podcasts
the user is currently subscribed to. Add at least one podcast in
PodFetch first (via the web UI or AntennaPod), then re-run Import
subscriptions to group in Kodi.
Reporting issues
If you hit a problem not covered above, please comment on issue #372 and include:
- PodFetch version (e.g.
4.2.2). - Kodi version and platform (e.g.
LibreELEC 11.0.3 / RPi3). - “RSS Podcasts” addon version.
- Reverse-proxy software and any auth-related config (redacted).
- The relevant excerpt from the PodFetch server log.
Pitfalls
My server is not reachable from the internet
- Check your firewall
- Make sure you can ping the system
My PodFetch server does not show any images
- Make sure your server is reachable and that your reverse proxy forwards the
X-Forwarded-Host,X-Forwarded-Proto, andX-Forwarded-Prefixheaders correctly
I cannot login to the UI
- Make sure you have set up the
BASIC_AUTHenvironment variable - Make sure you have set up the
USERNAMEenvironment variable - Make sure you have set up the
PASSWORDenvironment variable - Otherwise, reset your password via the CLI
I can’t stream any podcasts with authentication enabled
- Make sure your user has an api key
- Otherwise, generate one via the UI in the profile tab.