Publish-Docker-Github-Action: turning a branch name into a pushed Docker tag
A Github Action used to build and publish Docker images
At a glance
- What is it?
- elgohr/Publish-Docker-Github-Action is a composite GitHub Action that builds a Docker image from your repository, tags it with the branch that triggered the run and pushes it to a registry. It suits small teams that want a publish step without writing pipeline glue, and its snapshot and platform options are useful, but the Docker layer caching it offers will silently freeze package versions.
- Who is it for?
- Adopt Publish-Docker-Github-Action when you want a short publish step that derives the image tag from the branch and you are content to drive it with the name, username and password inputs. Do not adopt it if you need reproducible layer caching, signed images or per-environment promotion, and check first that the branch you trigger on is the one your deployment expects as latest, because the action publishes the default branch under the latest tag.
- Can I use it commercially?
- Yes. MIT is a permissive licence: you can use, modify and sell software built on it, as long as you keep its copyright and licence notices.
- Is it still maintained?
- Yes. The repository last received commits 3 days ago.
- What is it written in?
- Mainly Shell, according to GitHub's language statistics.
Answers come from the project's GitHub data, last synced on October 22, 2026, and from our analysis. They are not legal advice.
Editorial analysis
The branch name is the image tag, and the default branch becomes latest
The core idea of elgohr/Publish-Docker-Github-Action is a single mapping: the Git branch that triggered the workflow is used as the Docker tag for the image that gets built and pushed. The documentation states that the action for Docker "uses the Git branch as the Docker tag for building and pushing the container", and adds that the master branch is published under the `latest` tag.
That is the whole contract in one sentence, and it decides more about your workflow than it first appears. Every push to a feature branch produces a tag named after that branch, which is convenient because a developer can pull `my-feature-branch` and get exactly what that branch built. It also means tag names carry whatever characters your branch names carry, including slashes, so a branch called `feature/login-fix` needs care on the registry side.
The action does not care what is inside your repository beyond a buildable context and a Dockerfile. There is no manifest of expected outputs, no release train and no version numbering scheme imposed on you. If your project already has a convention where the branch name matches something meaningful downstream, the action simply follows it.
Three outputs come back to later steps in the same job: `tag`, described as the tag that was pushed, `snapshot-tag`, the tag produced by the snapshot option, and `digest`, the digest of the pushed image. Since these are step outputs rather than logs, a later job can consume them to record exactly which artifact was published.
The action runs its own Docker daemon inside the runner container
The repository layout explains why this action is heavier than a plain shell step. There is a Dockerfile at the root, an `entrypoint.sh`, a `makefile`, and a `test.bats` suite with `mock.sh` used as a stand-in for the `docker` and `date` commands.
The Dockerfile starts from an Ubuntu base pinned by digest and installs the Docker engine itself, not just the client:
FROM ubuntu:26.04@sha256:f144425ff09be612d6d9ad965196e9cdc23dae1f42110a8a11a3e9a8198759f7 as runtime
ENV DEBIAN_FRONTEND=noninteractive
ADD entrypoint.sh /entrypoint.sh
ENTRYPOINT ["/entrypoint.sh"]So the GitHub Actions job does not run the publish logic on the host runner. The action image starts, and the entrypoint drives `docker build` and `docker push` from inside that container. The Docker-in-Docker arrangement is the price of the action being packaged as a container rather than a JavaScript action, and it is why a job using it needs a runner that permits nested container execution.
The test stage shows how the project checks itself. A second build stage adds `bats` and `coreutils` to the runtime image, copies in `test.bats` and installs the mock script under `/usr/local/mock` as both `docker` and `date`, then runs the suite during the image build. Mocking `date` is a neat touch given that the snapshot tag embeds a timestamp, and mocking `docker` means the tests can assert on command sequences without a daemon or a registry anywhere near them.
The repository also carries a `SECURITY.md` and a `LICENSE`, and the licence is MIT.
A publish step needs only name, username and password
The documented usage is a single step in an otherwise ordinary workflow. Three inputs are mandatory: `name`, the image name you want to push, `username`, the registry login name, and `password`, which the documentation describes as the authentication token, preferred, or the login password for the registry.
name: Publish Docker
on: [push]
jobs:
build:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v3
- name: Publish to Registry
uses: elgohr/Publish-Docker-Github-Action@v5
with:
name: myDocker/repository
username: ${{ secrets.DOCKER_USERNAME }}
password: ${{ secrets.DOCKER_PASSWORD }}Read that as a complete first use. The trigger is any push, the checkout action pulls your source, and the publish step tags and pushes it. If the workflow file lives in the same repository as your Dockerfile, no path arguments are needed and the default build context applies.
Credentials come from repository secrets rather than literals, and the GitHub-hosted registries have a shortcut. When you push to GitHub Packages or GitHub Container Registry, the documentation suggests using the actor name as the username, because the registry path format differs from Docker Hub:
with:
name: owner/repository/image
username: ${{ github.actor }}
password: ${{ secrets.GITHUB_TOKEN }}
registry: ghcr.ioFor Docker Hub and other registries, the `registry` input selects the target host. For Amazon ECR or Google Cloud Container Registry, the action points to separate login actions, `elgohr/ecr-login-action` and `elgohr/gcloud-login-action`, which means authentication for those providers is a separate step rather than something this action handles.
Snapshot tags add a timestamp so rebuilt images stop colliding
Branch-name tags have a weakness: rebuild the same commit and you overwrite the same tag. The `snapshot` option pushes an additional image to solve that, tagged with a string built from the date and the commit.
The documented format is `{YEAR}{MONTH}{DAY}{HOUR}{MINUTE}{SECOND}{first 6 digits of the git sha}`. The stated reason for putting the date in front is precise: it prevents new builds with external dependencies from overwriting older builds that share the same sha. Without it, a rebuild pulled in a newer base layer or a newer package version, and the earlier image built from that commit becomes unreachable.
with:
name: myDocker/repository
username: ${{ secrets.DOCKER_USERNAME }}
password: ${{ secrets.DOCKER_PASSWORD }}
snapshot: trueThe result is a tag that is unique per build attempt rather than per commit, which is exactly what you want when the content of two builds from one commit is not identical. The cost is storage and a tag namespace that grows without bound, so the option is only worth turning on where you genuinely rebuild from the same commit and need to distinguish the artifacts.
The documentation suggests the snapshot option when thinking about image versioning, and it also points to it for keeping older builds reachable. It does not document any cleanup or retention policy for these tags, so if you enable it in a busy repository, pruning is something you have to arrange on the registry side yourself.
Multi-architecture builds require setup-buildx-action to run first
Building for more than one CPU architecture is supported through the `platforms` input, which takes a comma separated list of target architectures. The documentation is explicit about the ordering requirement: `docker/setup-buildx-action` must be executed before a step that contains `platforms`.
- name: Set up Docker Buildx
uses: docker/setup-buildx-action@v2
- name: Publish to Registry
uses: elgohr/Publish-Docker-Github-Action@v5
with:
name: myDocker/repository
username: ${{ secrets.DOCKER_USERNAME }}
password: ${{ secrets.DOCKER_PASSWORD }}
platforms: linux/amd64,linux/arm64That dependency is the kind of detail that bites when you paste a step into an existing workflow. If you add `platforms` to a job that has never run Buildx, the failure is not a missing input but a missing builder, and the fix is to add the setup step rather than to change any of this action's arguments.
The remaining optional inputs all adjust the underlying `docker build` invocation rather than the tagging scheme. `dockerfile` names an explicit Dockerfile, which the documentation suggests for projects with several images in one repository. `workdir` changes the directory the build runs in, and `context` changes the Docker build context, which are two different knobs that are easy to confuse: workdir affects where the command is issued, context affects what the build can see.
For build-time configuration there are two more. `buildargs` passes environment variables into the build as build args, with identifiers separated by commas, and the documentation states that all of them are masked so they do not appear in the logs.
- name: Publish to Registry
uses: elgohr/Publish-Docker-Github-Action@v5
env:
MY_FIRST: variableContent
MY_SECOND: variableContent
with:
name: myDocker/repository
username: ${{ secrets.DOCKER_USERNAME }}
password: ${{ secrets.DOCKER_PASSWORD }}
buildargs: MY_FIRST,MY_SECOND`buildoptions` passes option flags straight through to the build command, for example `"--compress --force-rm"`. One caveat applies to the masking promise: it holds for the values of the named build args, but a secret that reaches the image through another route, such as a file copied in by the build context, is not covered by it.
The cache option reuses old layers and will freeze your packages
The `cache` input exists for large images where you only want to rebuild the layers that changed. The documentation carries a clear warning: Docker builds cache non-reproducible commands such as installing packages, so if you use this option your packages will never update. The suggested remedy is to run the action on a schedule with caching disabled, so the cache is rebuilt periodically.
The conditional in the example is the neat part. By comparing the event name against `schedule`, a workflow can use the cache on ordinary pushes and skip it on the scheduled rebuild:
- name: Publish to Registry
uses: elgohr/Publish-Docker-Github-Action@v5
with:
name: myDocker/repository
username: ${{ secrets.DOCKER_USERNAME }}
password: ${{ secrets.DOCKER_PASSWORD }}
cache: ${{ github.event_name != 'schedule' }}The companion trigger for that pattern pushes on master and also schedules a weekly run:
on:
push:
branches:
- master
schedule:
- cron: '0 2 * * 0' # Weekly on Sundays at 02:00This is the sharpest trade-off in the action, and it deserves to be read as a design constraint rather than a feature to enable by default. The same caching that saves you minutes on a large build also means that an `apt-get install` or an `apk add` layer from months ago is reused verbatim, including its pinned package versions, until a scheduled run without the cache refreshes it. If your image tracks an upstream distribution closely, that is a stale-image problem rather than a speed problem, and no setting in this action distinguishes between the two layers you care about.
There is a second failure mode worth planning for. Because the action pushes on every trigger, a workflow that runs on all pushes will publish a branch-named image for every branch, and the `latest` tag follows the branch named as the default one, `master` by default. The `default_branch` input exists to change that: set it to `trunk` and the action treats `trunk` as the default branch that receives the `latest` tag.
The newest tag is from 2022 while the repository itself was pushed to recently
The release history and the commit history tell different stories, and the difference matters for how you pin the action. Tagged releases are v5 from 2022-11-30, v4 from 2022-04-14 and 3.04 from 2021-04-27. The last push to the repository was on 2026-10-07.
So the action you reference as `elgohr/Publish-Docker-Github-Action@v5` points at a major tag whose published release date is from late 2022, while work has continued on the repository in the intervening years. The examples throughout the documentation still use `actions/checkout@v3` and `docker/setup-buildx-action@v2`, which are themselves dated major versions rather than the current ones, so the usage guidance has not been refreshed against newer runner defaults.
The upgrade path is short. The action is referenced by a version tag in a workflow file, there is no configuration file to migrate and no state stored on disk between runs, so upgrading means changing one reference in your workflow YAML and watching a build. The real risk is behavioural rather than procedural: a change to how the tag or the build arguments are handled would surface as a different image tag appearing in your registry, so read the diff on the entrypoint before rolling a version change across several repositories at once.
Licence terms are MIT, so using the action inside your own commercial workflows carries no copyleft requirement on your pipeline files. The practical obligations sit in the credentials you pass it: `username` and `password` are handed to the action, and the `password` input is documented as preferring an authentication token over a login password, which is worth honouring since a scoped registry token limits the damage if a workflow log or runner is exposed.
Editorial conclusion
Adopt Publish-Docker-Github-Action when you want a short publish step that derives the image tag from the branch and you are content to drive it with the name, username and password inputs. Do not adopt it if you need reproducible layer caching, signed images or per-environment promotion, and check first that the branch you trigger on is the one your deployment expects as latest, because the action publishes the default branch under the latest tag.
Frequently asked questions
How does Publish-Docker-Github-Action decide the Docker tag?
It uses the Git branch that triggered the workflow as the image tag, and the master branch is published under the latest tag. The `default_branch` input lets you treat a different branch, such as trunk, as the one that receives latest.
What inputs does Publish-Docker-Github-Action require?
Three are mandatory: `name` for the image to push, `username` for the registry login, and `password` for the authentication token or login password. Optional inputs cover the registry host, snapshot tags, the Dockerfile, workdir, context, buildargs, buildoptions, platforms, cache and no_push.
Can I publish a multi-architecture image with this action?
Yes, through the `platforms` input with a comma separated list such as `linux/amd64,linux/arm64`. The documentation requires `docker/setup-buildx-action` to run before a step that contains `platforms`.
What is the snapshot option for?
It pushes an extra image tagged with the date and time plus the first six digits of the git sha. The date prefix exists so a rebuild of the same commit with newer external dependencies does not overwrite the earlier image.
Is there a downside to using the cache option?
Yes. The documentation warns that Docker caches non-reproducible commands such as package installs, so cached packages will never update on their own. The suggested fix is a scheduled run with caching disabled to rebuild the cache periodically.
How do I publish to Amazon ECR or Google Cloud with this action?
Authentication for those registries is handled by separate login actions, `elgohr/ecr-login-action` and `elgohr/gcloud-login-action`. The publish action itself expects the registry credentials to already be in place.
Official sources
Add this badge to your README
If you maintain this project, the badge below links readers to this analysis and shows its maintenance status from the daily GitHub snapshot. Paste the markdown into your README; add ?metric=license or ?metric=stars to the image URL for a different field.
[](https://hysenlabs.com/projects/elgohr-publish-docker-github-action)Was this useful?