An image built on an Apple Silicon laptop is arm64 and the servers are amd64, and the resulting failure is an exec format error with no other explanation.
$ docker build -t app:latest .
$ docker push registry/app:latest
# on the server
$ docker run registry/app:latest
exec /usr/local/bin/docker-entrypoint.sh: exec format error
# the fix, per build
$ docker buildx build --platform linux/amd64 -t app:latest --push .
# and the check
$ docker image inspect app:latest --format '{{.Architecture}}'
This became a weekly occurrence in 2021 as the laptops changed and the servers did not. Building both architectures with --platform linux/amd64,linux/arm64 produces a manifest list and is the right answer for an image other people pull; forcing amd64 is right for one that only ever runs on your servers. Emulated builds are slow enough — often five times — that a native runner for each architecture is worth the pipeline complexity once the build takes minutes.