mix phx.gen.release --docker frequently fails with 504 from hub.docker.com

Running forgejo CI/CD build pipeline. The action mix phx.gen.release –docker frequently fails with 504 from hub.docker.com. This is trying to fetch a json file with a URL like the following to find the correct image to use in the build:-

https://hub.docker.com/v2/namespaces/hexpm/repositories/elixir/tags?name=1.17.3-erlang-27.3.4.3-debian-bullseye-

What is the best workaround? Create a proxy to the internet to cache successful answers from hub.docker.com ? Allow a command line option for phx.gen.release to specify the image to use so that remote calls are not made?

I’ve run into this issue before as well. It would be nice if the task printed a warning and had a hardcoded fallback to a known version.

I’ve run into this a few times myself. I had built out a prototype of filtering through these Docker Hub images as a web UI but what I found is that the Docker Hub API is incredibly non-performant combined with the sheer number of tags inside hexpm/elxir.

I’d almost prefer a sentinel value stored somewhere that could be just updated manually as necessary. A TXT DNS record, a plain text file on the Phoenix website, etc.

Given that Steffen recently had to mock it out in tests I think they’re aware of the issue :slight_smile:

It really is remarkably slow when it works at all. It’s not an intermittent thing; it’s just busted all the time. Idk if there’s any way to fix it without operating a service to cache the value, though. Maybe rig something up with Actions/Pages? I assume the versions don’t change that often.

The problem is real.

Have you considered running that mix task on your development environment once and committing the resulting files to git?

I believe that’s the intent of that task, as it generates some files you can customize, the Dockerfile included. This task is similar to generating a migration file or scaffolding a new controller, etc, such that typical use is not part of the CI/CD workflow (the usage in the Phoenix repository tests is not typical).

Once you commit your Dockerfile, then deploying the app builds it off a known base image that only changes when you explicitly update it.

That definitely is the intent, but it’s still unfortunate that the task is so slow/flaky. It means that new users are quite likely to see the command fail when they try to run it.

Thinking about it more, a repo under the Phoenix org with a simple Action that pings the API every hour and caches the results by uploading them to Pages would be an easy no-maintenance solution. Just point the task at the pages URL by default.

I’m running into this issue as well. I wouldn’t mind helping figure out some kind of solution or process to hack a solution.

I’ve found something that kind of works, but it’s unreliable—which leads me to believe it’s possible this hack doesn’t work as I think it may be.

I’ll run the mix phx.gen.release --docker command and then get a fail. As said before, often I’ll get a 504 back, but it’s from Cloudflare. This makes me wonder if it’s some kind of cache-miss issue.

I’ll run some curl commands and walk up the stack starting with curl --head “http://hub.docker.com/v2/namespaces/hexpm/repositories/elixir/tags?name=:

  • <elixir-version>-erlang-<erlang-version>-
  • <elixir-version>-erlang-<erlang-version>-debian-
  • <elixir-version>-erlang-<erlang-version>-debian-trixie-

The first two will usually work. The last will usually fail. But after a brief amount of time, it’ll succeed, and I’ll be able to re-run the mix task.

I don’t know how important it is for the mix task to fully finish. I know it’ll create the rel/ dir and lib/os/release.ex file. It’s possible it only needs to finish to create the Dockerfile and .dockerignore files. If that’s the case, you can fetch those here-ish and fill in the missing values.

If you don’t know what values you’re missing, try running a command like this: curl -s "https://hub.docker.com/v2/namespaces/hexpm/repositories/elixir/tags?name=<elixir-version>-erlang-<erlang-version>-" | jq '.results[] | .name' (requires jq to be installed).

You can take those values and plug them into the Dockerfile template (specifically in the 3 args up top).

Here’s an example:

> url="https://hub.docker.com/v2/namespaces/hexpm/repositories/elixir/tags?name=1.19.5-erlang-28.4.1-debian-"
> curl -s "$url" | jq -r '.results[] | .name'
1.19.5-erlang-28.4.1-ubuntu-noble-20260217
1.19.5-erlang-28.4.1-ubuntu-jammy-20260217
1.19.5-erlang-28.4.1-debian-trixie-20260316-slim
1.19.5-erlang-28.4.1-debian-trixie-20260316
1.19.5-erlang-28.4.1-debian-bullseye-20260316-slim
1.19.5-erlang-28.4.1-debian-bullseye-20260316
1.19.5-erlang-28.4.1-debian-bookworm-20260316-slim
1.19.5-erlang-28.4.1-debian-bookworm-20260316
1.19.5-erlang-28.4.1-ubuntu-focal-20250404
1.19.5-erlang-28.4.1-ubuntu-jammy-20260109

I’ll plug in the following args (picking the latest trixie build available) into the Dockerfile from the link above:

ARG ELIXIR_VERSION=1.19.5
ARG OTP_VERSION=28.4.1
ARG DEBIAN_VERSION=trixie-20260316-slim

Again, I don’t know if anything is being done between the timeout and the creation of the two Docker-related files. Hopefully this gets someone out of a bind.

my workaround…

while ! mix phx.gen.release --docker; do sleep 1; done

Oh man, golden :joy:

Cloudflare never blocks you?

Not something I do every day, but yeah it will commonly try 10-50 times before making a connection. :face_with_bags_under_eyes: