# podgrab advertises version 2022.07.07, builds its image with Go 1.15.2, and requires Go 1.25.0

> akhilrex/podgrab is a self-hosted podcast downloader and player: a Go and SQLite backend behind a JavaScript frontend, shipped as a Docker image, with Basic Authentication off unless you set an environment variable. Three version claims sit in one repository four years apart, the container widens permissions on both volumes to 777, and an MIT badge sits above a GPL-3.0 licence.

**akhilrex/podgrab** — A self-hosted podcast manager/downloader/archiver tool to download podcast episodes as soon as they become live with an integrated player.

- Repository: https://github.com/akhilrex/podgrab
- Stars: 1,988 · Forks: 132
- Language: JavaScript
- License: GPL-3.0
- Published: 2026-09-10 · Updated: 2026-09-10 · Language: en
- Canonical page: https://hysenlabs.com/projects/akhilrex-podgrab

## Three version claims in one repository

The header of the readme advertises a current version of 2022.07.07. The module file declares go 1.25.0. The container's builder stage is pinned to a much older toolchain, with the Go version passed in as a build argument defaulting to 1.15.2 and then used as the base of an Alpine builder image.

That last one is the one with consequences. A builder from the 1.15 era cannot build a module that declares a language version from the 1.25 era, so the image and the source it claims to build from are not describing the same program. Anyone building the container from this Dockerfile should expect to have to override the build argument before anything compiles.

Meanwhile the platform shows no GitHub releases at all, and the default branch is master. So there is no tag to pin, no release note to read, and a version string in the readme that has not moved in four years while the last commit on the branch is far more recent than that. The readme also carries a developer's note saying the project is under active development and that updates are released very frequently, and points readers at watchtower for automatic container updates. Read that note next to the 2022 date and the absence of releases, and the picture is a project whose release channel is the image tag rather than the platform.

## Both volumes are created with permissions of 777

The runtime half of the Dockerfile does the setup for you, and it does it permissively. It sets the config and data locations as environment variables, sets a UID and a PID, puts Gin into release mode, declares the two volumes, creates the directories, and then runs chmod 777 on both the config directory and the assets directory. It installs one package in the runtime image, the certificate authority bundle, and nothing else.

The 777 on the two mounted paths is the detail to weigh. Those are the host directories you bind in, so the image asks for world-writable permissions on whatever you mount, which is broader than the application needs to write its own files. On a host where the config directory holds anything else, that is a real exposure rather than a theoretical one, and it happens before the application starts.

Two other things are worth noting from the same file. The build step compiles a single file, main.go, into the output binary, so every other package in the module has to be reachable as an import from that one file. And the runtime image copies two directories across from the builder, the client directory and the web assets directory, which is where the JavaScript that the platform's language statistics attribute to this project comes from.

## Authentication is off by default and the username cannot be changed

The environment variable table has three entries. CHECK_FREQUENCY sets how often the application looks for new episodes and missing files, in minutes, and documents a default of 30. PASSWORD, when set to a non-empty value, enables Basic Authentication with the username fixed as podgrab, and its default is empty, which means authentication is off. PORT changes the internal port, and the readme warns you will probably have to change the Docker configuration to match.

The port coupling is the kind of note that exists because it has bitten somebody. Changing PORT without changing the published port leaves you with a container that is running and not reachable.

The compose example compounds the first entry. The shipped file starts like this:

```yaml
version: "2.1"
services:
  podgrab:
    image: akhilrex/podgrab
    container_name: podgrab
    environment:
      - CHECK_FREQUENCY=240
     # - PASSWORD=password     ## Uncomment to enable basic authentication, username = podgrab
    volumes:
      - /path/to/config:/config
      - /path/to/data:/assets
    ports:
      - 8080:8080
    restart: unless-stopped
```

It sets CHECK_FREQUENCY to 240, which is four hours, against a documented default of 30 minutes. So a reader who copies the shipped compose file is polling eight times less often than the documentation says, and the file does not say so. There is a second twist: the compose snippet printed in the readme carries a commented-out password line explaining that uncommenting it enables authentication with the username podgrab, and that comment is not present in the compose file in the repository. Two copies of one configuration file, already out of step.

## A committed .env file beside a dependency list from 2020

There is a .env file in the repository root, alongside a .gitignore. For a project whose only documented environment settings are three variables, having a dotenv file at the root means the defaults the application will actually run with are part of the repository rather than something each operator writes.

The module file is a study in two eras. The language requirement is current, and so are a few dependencies: the network package, the crypto package, the system and text packages. Around them sit a web framework at release 1.7.2, an object mapper at 1.20.2, a logger at 1.16.0, a SQLite driver at 1.1.3, and two dependencies pinned by 2020 pseudo-versions rather than by a release number. A mixture like that is normal in a project that grows one need at a time, and it is the kind of thing that ages unevenly.

The interesting part is which dependency does what. An XML query library parses the RSS feeds. An HTML tag stripper cleans the feed descriptions. A cron scheduler implements CHECK_FREQUENCY. A dotenv loader is what reads that root file. A WebSocket library is there for one named feature, add to playlist, which is also the only reason the setup notes tell you to enable WebSocket support behind a reverse proxy. And a podcast index client covers the search side of the discovery feature.

## JavaScript is the primary language of a Go backend

The project is described as a lightweight application built using Go, and the built-with list names Go, the Gin web framework, an object mapper and SQLite. The repository holds main.go, a go.mod, a controllers directory, a db directory, a model directory, a service directory, an internal directory, and two frontend directories named client and webassets.

Yet the language statistics for the repository report JavaScript. That is not a contradiction so much as a measurement: the frontend directories are copied into the runtime image and served by the Go binary, and if their combined byte count is larger than the Go sources, the statistics follow the assets rather than the language that does the work.

The file naming around them is a little unconventional for the same reason. The readme file is named Readme.md rather than README.md, and the logo placeholder and the demo link in the header are both HTML comments rather than absent, so the header carries the residue of a template: a project logo comment, a commented-out demo link, and an Explore the docs link that points at the repository itself rather than at a documentation site. The one real documentation link is a guide to building from source on Ubuntu.

## An MIT licence shield above a GPL-3.0 statement

The badge row at the top of the file carries six shields: contributors, forks, stargazers, issues, licence, and LinkedIn. The licence shield is labelled MIT License. The licence section further down states that the project is distributed under the GPL-3.0 licence, and the repository tree holds a LICENSE file.

So the file advertises one licence in its imagery and names another in its prose. For a self-hosted application that people run on their own servers and sometimes repackage, that difference is the kind of detail somebody will notice at the wrong moment, and it is trivially fixed by relabelling one shield.

The same header area carries other template residue worth knowing about, because it tells you what is and is not real. The logo slot is an HTML comment with nothing in it. The demo link is commented out. The version line is a plain paragraph inside centred markup. None of that is a defect in the running application, but each is a place where the file describes something that is not there, and a reader scanning quickly will take those at face value.

## Three roadmap items open, including the one the project was built for

The roadmap is a checklist with ten items ticked and three left open. Ticked: Basic Authentication, appending the date to filenames, iTunes search, existing episode detection, a downloaded indicator, a played and unplayed flag, OPML import, OPML export, and the built-in player. Open: setting ID3 tags when they are not already set, filtering and sorting options, and a native installer for Windows, Linux and macOS.

The first open item is the interesting one. The stated motivation is that most podcasting applications do not expose the audio files directly, which is what stopped the author from using them, so the project downloads the files to a watch. Writing the ID3 tags is exactly the finishing work that makes a downloaded file usable in another player, and it is still unticked. The other two open items explain the shape of the install story: there is no native installer, so Docker is the recommended path and building from source is the alternative. The bare container, offered for testing and evaluation, is one command:

```sh
docker run -d -p 8080:8080 --name=podgrab akhilrex/podgrab
```

Keeping downloads and configuration means binding host directories for the assets and the config:

```sh
docker run -d -p 8080:8080 --name=podgrab -v "/host/path/to/assets:/assets" -v "/host/path/to/config:/config"  akhilrex/podgrab
```

Neither command sets a password, so both start with authentication off.

The setup notes add two small obligations. Enable WebSocket support if you sit behind a reverse proxy, because add to playlist needs it, and go through the settings page once and change what is relevant before adding any podcasts, which is a hint that the defaults are not what a new user wants.

## Conclusion

Podgrab is worth trying if you already know which feeds you want and you want the audio files on your own disk rather than inside somebody else's app, because it is a downloader with a player attached, not a subscription service. Three things to weigh first. Decide how you will pin it, since there are no releases on the platform, the header still advertises 2022.07.07, and the container's builder is pinned to a Go version far behind what the module declares, so a reproducible install is your problem to solve. Turn on the PASSWORD variable before you expose the port, because Basic Authentication is off by default and the username is fixed. And read the permission story before you bind a host directory: the image creates both volumes with 777, which is the right thing to inspect if the host path holds anything else.

## FAQ

### How do I run podgrab with Docker?

The simplest command is docker run -d -p 8080:8080 --name=podgrab akhilrex/podgrab, with no volumes for a quick evaluation. To keep downloads and configuration, bind a host directory to /assets and another to /config. The compose file maps 8080, sets restart to unless-stopped and is started with docker-compose up -d.

### How do I turn on authentication in podgrab?

Set the PASSWORD environment variable to a non-empty value. The username is always podgrab and cannot be configured. The default is empty, which leaves Basic Authentication disabled. The separate PORT variable changes the internal port and would also need a matching change to the published port.

### Which environment variables does podgrab use?

Three. CHECK_FREQUENCY sets how often to look for new episodes and missing files, in minutes, with a documented default of 30. PASSWORD enables Basic Authentication with the username podgrab and defaults to empty. PORT changes the internal port and also defaults to empty.

### What is podgrab built with?

Go with the Gin web framework and GORM on SQLite, plus a frontend in the client and webassets directories that the Go binary serves. The module declares Go 1.25.0, while the Dockerfile's builder stage is pinned to Go 1.15.2 by default.

### Can I import my existing podcast subscriptions into podgrab?

Yes, by direct RSS feed URL, by OPML import, or through the built-in search, which is powered by the iTunes API. Exporting back to OPML is supported as well, and downloaded files can be played in the built-in player or streamed from the original source.

### Will podgrab download episodes it already has?

Not according to its own documentation. The feature list includes existing episode file detection to prevent re-downloading files that are already present, and the roadmap describes it as not redownloading when files exist even after a fresh install.

## Sources

- [akhilrex/podgrab on GitHub](https://github.com/akhilrex/podgrab)
- [Issues](https://github.com/akhilrex/podgrab/issues)
- [License: GPL-3.0](https://github.com/akhilrex/podgrab/blob/master/LICENSE)
- [README](https://github.com/akhilrex/podgrab/blob/master/README.md)

---

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