Keyboard shortcuts

Press or to navigate between chapters

Press S or / to search in the book

Press ? to show this help

Press Esc to hide this help

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 variableDescriptionDefault
S3_URLThe URL of the S3 service.http://localhost:9000
S3_REGIONThe region of the S3 service.eu-west-1
S3_ACCESS_KEYThe access key of the S3 service./
S3_SECRET_KEYThe secret key of the S3 service./
S3_PROFILEThe profile of the S3 service./
S3_SECURITY_TOKENThe security token of the S3 service./
S3_SESSION_TOKENThe 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:
VariableDescriptionDefault
POLLING_INTERVALInterval in minutes to check for new episodes300
PORTThe port PodFetch listens on8000
SUB_DIRECTORYSub-path when hosting behind a reverse proxy (e.g. /podfetch)(none)
DATABASE_URLURL of the databasesqlite://./db/podcast.db
PODFETCH_FOLDERDirectory (inside the container) where podcast files are storedpodcasts

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

  1. Go to the Obtainium website and download the latest obtainium.apk file.
  2. 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.
  3. 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.
  4. 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

[Screenshot of startpage]

[Screenshot of player]

[Screenshot of player detail]

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.

  1. Open your Podfetch web UI in the mobile browser (https://your-server/ui/).
  2. Android (Chrome/Edge): open browser menu and choose Install app / Add to Home screen.
  3. iOS (Safari): open Share menu and choose Add to Home Screen.
  4. 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.

VariableDefaultWhat it does
AUDIOBOOKSHELF_DATA_DIR<podfetch_data>/audiobookshelfWorking dir for the HLS segment cache. Point this at fast local storage if you transcode a lot.
AUDIOBOOKSHELF_HLS_CACHE_MAX_MB2048Hard cap on the HLS segment cache (LRU eviction). Raise on big servers, lower on Raspberry Pis.
AUDIOBOOKSHELF_TRANSCODER_MAX_CONCURRENT2Maximum ffmpeg processes that may run in parallel. One per concurrent transcoding listener.
AUDIOBOOKSHELF_ROTATE_API_KEY_ON_LOGOUTfalseWhen 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:

4. Connect to PodFetch

  1. Open the app and pick Connect to server.
  2. 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.
  3. Enter your PodFetch username and password. The same credentials the web UI uses; the app does not need a separate account.
  4. 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:

  1. Listing: every podcast you subscribed to in PodFetch shows up.
  2. 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.
  3. 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.
  4. 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 Authorization header. 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.

VariableDescription
BASIC_AUTHSet to true if you want to use basic auth
USERNAMEUsername for basic auth
PASSWORDPassword 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 sam again and you should find sam is now an admin.

Keycloak

VariableDescriptionExample
OIDC_AUTHFlag if OIDC should be enabledtrue
OIDC_AUTHORITYThe url of the OIDC authority.<keycloak-url>/realms/master
OIDC_CLIENT_IDThe client id of the OIDC client.podfetch
OIDC_REDIRECT_URIThe URI the OIDC authority redirects to after authentication.<your-server-url>/ui/login
OIDC_SCOPEThe scope of the oidc tokenopenid profile email
OIDC_JWKSThe 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

VariableDescriptionExample
OIDC_AUTHFlag if OIDC should be enabledtrue
OIDC_AUTHORITYThe url of the OIDC authority.https://auth.DOMAIN.COM
OIDC_CLIENT_IDThe client id of the OIDC client.podfetch
OIDC_REDIRECT_URIThe URI the OIDC authority redirects to after authentication.https://podfetch.DOMAIN.COM/ui/login
OIDC_SCOPEThe scope of the oidc tokenopenid profile email
OIDC_JWKSThe JWKS token urihttps://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:

VariableDescriptionExample
REVERSE_PROXYFlag if reverse proxy should be enabledtrue
REVERSE_PROXY_HEADERThe url of the reverse proxy.X-FORWARDED-FOR
REVERSE_PROXY_AUTO_SIGN_UPFlag if PodFetch should automatically sign up userstrue

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. Audio Player

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.

Continue listening to episodes

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. Audio Player

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

Home Screen

Home Screen

Timeline View

Timeline

Info View

Info page

Settings

Settings

Administration

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 (https or http)
    • 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:

VariableDescriptionexample
TELEGRAM_BOT_TOKENThe Bot token that you can acquire from Botfather with /newbotasdj23:hsifuhi234klerlf…sadasd
TELEGRAM_BOT_CHAT_IDThe chat id of the chat where the bot should send the messages123456789
TELEGRAM_API_ENABLEDIf the telegram api should be enabled.true

You can acquire the Telegram Bot chat id with the following steps:

  1. Write a message to the bot
  2. Open the following url in your browser: Telegram API page
  3. 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.

VariableDescriptionDefault
PODINDEX_API_KEYthe api key sent to you via mail%
PODINDEX_API_SECRETthe 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:

  1. Direct — the PodFetch server runs on the same LAN as the Chromecast and speaks the CAST protocol itself.
  2. Agent relay — the PodFetch server is hosted somewhere remote (a public instance, a VPS, a friend’s server) and a small podfetch --agent process 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:

FlagRequiredDefaultDescription
--remoteyesBase URL of the PodFetch instance to connect to.
--api-keyyesExisting user API key on the remote instance.
--agent-idnorandom UUIDStable id; pass the same one across restarts so the server keeps a single agent identity.
--proxy-portno8011Reserved 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/ws with a Bearer token,
  • pushes the discovered device list to the server, and
  • forwards Play requests 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-port flag 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

  1. Set up the environment variables in your docker-compose under the PodFetch service:
BASIC_AUTH: true
USERNAME: test
PASSWORD: test
  1. Run docker-compose up -d to 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.

Login Screen

  1. [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.

Basic Auth

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:

VariableDescriptionDefault
GPODDER_INTEGRATION_ENABLEDActivates the GPodder integrationfalse

⚙️ 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

Adding server url to gpodder

  1. 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.

  1. Enter the username and password of the user that you created before through the CLI

Login to gpodder

  1. 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.

Adding a device

  1. Success screen

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

GPodder Sync

  1. 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

  2. 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

Setup in Kodi

  1. Install the addon. In Kodi, go to Add-ons → Install from repository → All repositories → Music → RSS Podcasts.
  2. Configure the provider. Open the addon and select Configure → Provider: gpodder.net.
  3. 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.
  4. Enter credentials. Username and password of the PodFetch user you created in the prerequisites.
  5. 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.

FeatureKodi callsPodFetch routeStatus
Login (Basic Auth → session cookie)POST /api/2/auth/{user}/login.jsonlogin in gpodder_api/auth/authentication.rsendpoint exists ✓
Import subscriptions (OPML)GET /subscriptions/{user}.opmlget_simple_subscriptions in gpodder_api/subscription/subscriptions.rsendpoint exists ✓
Push subscription changes from Kodinot supported by the addon
Episode play-state / position syncnot supported by the addon
Device list / managementnot 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_request directive overrides the upstream auth, and that any proxy_set_header block does not unset Authorization. nginx forwards it by default unless you remove it.
  • Traefik — by default the Authorization header is forwarded. Check that no custom headers middleware lists it under customRequestHeaders with an empty value (which would strip it).

“Provider gpodder.net” rejects credentials

Check, in order:

  1. GPODDER_INTEGRATION_ENABLED=true is set on the PodFetch container and the container has been restarted since.
  2. 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.
  3. 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, and X-Forwarded-Prefix headers correctly

I cannot login to the UI

  • Make sure you have set up the BASIC_AUTH environment variable
  • Make sure you have set up the USERNAME environment variable
  • Make sure you have set up the PASSWORD environment 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.