Either works for repeatability, as long as the value depends only on your source and not on when the build runs. Zero is simple but makes every file look like 1970. The commit time is more informative, and the specification recommends the last modification time of the source.
What the specification requires
SOURCE_DATE_EPOCH is a Unix timestamp, the number of seconds since 1 January 1970 UTC, written as an integer with no fractional part, like the output of date plus percent s. The spec's key points:
- The value must be reproducible across executions of the build, depending only on the source code.
- It should be set to the last modification time of the source, including packaging changes.
- Build processes must use it in place of the current date and time for embedded timestamps.
- Where a build embeds timestamps that are not current but still specific to one run, it must use a timestamp no later than this value. This is called clamping.
- A build process must not unset it for child processes if it is already present.
- If the value is malformed, the build should exit with a non zero code.
The two common choices
| Choice | How to get it | Upside | Downside |
|---|---|---|---|
| Zero | Set it to 0 | Trivial, no dependency on git | Timestamps say 1970, which confuses tools and humans |
| Commit time | Run git log -1 with the commit timestamp format | Reflects the age of the code, changes with each commit | Needs git history; shallow or modified checkouts need care |
Docker's guide for reproducible builds in GitHub Actions shows both. One example sets the variable to 0, Unix epoch. The other reads the Git commit timestamp:
echo "TIMESTAMP=$(git log -1 --pretty=%ct)" >> $GITHUB_ENV
and then passes it as SOURCE_DATE_EPOCH to the build step.
A trap with commit time
The spec adds a warning. If developers use the date of the latest commit, it recommends also updating all source file timestamps, which git does not do by itself. Otherwise, old timestamps specific to the developer's working tree may be embedded in the output.
In practice: files in a fresh checkout get the checkout time, which differs per run. If a tool copies those mtimes into an archive, your output varies even though SOURCE_DATE_EPOCH is fixed. Clamping fixes this. The spec says all source timestamps are reasonably before the value and builds happen after it, so rewriting any timestamp newer than the value back to the value preserves source based times and drops build specific ones. GNU tar's clamp mtime option does this.
How to choose
- If you want the artifact to look like it was built when its source was last changed, use the commit time and make sure clamping or timestamp resets are in place.
- If you just want repeatability and do not care about dates, use zero, and check that nothing in your toolchain treats 1970 as invalid.
- Whichever you choose, set it in one place, and make sure the same value reaches every tool in the build, including container builds. See how SOURCE_DATE_EPOCH works in container builds.
- Do not use the build date, ever. That is the thing the variable exists to replace.
Quick check
Can I change the value for each release?
Yes, it will change with the commit or release. It just must be the same for every rebuild of that release.
Does setting the variable guarantee a reproducible build?
No. It only helps tools that honour it. Paths, ordering and tool versions can still differ.
Safeguard's SBOM and dependency scanning is generally available, and the free scan at gold.safeguard.sh/scan covers repository dependencies.
Sources
- Reproducible Builds: SOURCE_DATE_EPOCH specification
- Docker Docs: Reproducible builds with GitHub Actions
- Reproducible Builds: Archive metadata
Last reviewed: 19 October 2026.