<?xml version="1.0" encoding="utf-8" standalone="yes"?><rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom"><channel><title>Reproducibility | Erich Heil</title><link>https://reboting.com/tag/reproducibility/</link><atom:link href="https://reboting.com/tag/reproducibility/index.xml" rel="self" type="application/rss+xml"/><description>Reproducibility</description><generator>Wowchemy (https://wowchemy.com)</generator><language>en-us</language><copyright>© {2021} hedata</copyright><lastBuildDate>Wed, 21 Oct 2026 07:00:00 +0000</lastBuildDate><image><url>https://reboting.com/media/icon_hua2ec155b4296a9c9791d015323e16eb5_11927_512x512_fill_lanczos_center_2.png</url><title>Reproducibility</title><link>https://reboting.com/tag/reproducibility/</link></image><item><title>A redeploy should not change what runs</title><link>https://reboting.com/post/2026-10-21-pin-what-runs/</link><pubDate>Wed, 21 Oct 2026 07:00:00 +0000</pubDate><guid>https://reboting.com/post/2026-10-21-pin-what-runs/</guid><description>&lt;p>My servers were last deployed on 30 September. Had I run the very same deploy the next morning, with not one line changed in the repository, the VPN server would have come up as a different release, because its maintainers published one in between.&lt;/p>
&lt;h2 id="a-floating-tag-lets-the-registry-decide-when-you-upgrade">A floating tag lets the registry decide when you upgrade&lt;/h2>
&lt;p>An image reference without a version, or with &lt;code>latest&lt;/code>, is not a version. It is a question the registry answers at the moment you ask, and the answer changes whenever a maintainer publishes. The Compose reference puts it plainly: the default pull policy fetches an image only when it is missing locally, &lt;a href="https://docs.docker.com/reference/compose-file/services/" target="_blank" rel="noopener">except that &lt;code>latest&lt;/code> is always pulled&lt;/a>.
My deploy runs a pull every time, and five third-party images floated: three parts of the VPN server on &lt;code>latest&lt;/code>, a Docker socket proxy with no tag at all, and a WordPress image on &lt;code>latest&lt;/code>. A re-run with an unchanged repository could replace any of them, and no diff would show it, because the change was never in my repository. I think a deploy that can do this is a lottery with a good-looking pipeline.&lt;/p>
&lt;pre>&lt;code class="language-mermaid">%%{init: {&amp;quot;theme&amp;quot;: &amp;quot;base&amp;quot;, &amp;quot;themeVariables&amp;quot;: {&amp;quot;fontFamily&amp;quot;: &amp;quot;Roboto, Helvetica, Arial, sans-serif&amp;quot;, &amp;quot;primaryColor&amp;quot;: &amp;quot;#e8f1fb&amp;quot;, &amp;quot;primaryTextColor&amp;quot;: &amp;quot;#313131&amp;quot;, &amp;quot;primaryBorderColor&amp;quot;: &amp;quot;#1565c0&amp;quot;, &amp;quot;secondaryColor&amp;quot;: &amp;quot;#f7f7f7&amp;quot;, &amp;quot;tertiaryColor&amp;quot;: &amp;quot;#ffffff&amp;quot;, &amp;quot;lineColor&amp;quot;: &amp;quot;#34495e&amp;quot;, &amp;quot;textColor&amp;quot;: &amp;quot;#313131&amp;quot;, &amp;quot;clusterBkg&amp;quot;: &amp;quot;#f7f7f7&amp;quot;, &amp;quot;clusterBorder&amp;quot;: &amp;quot;#e6e6e6&amp;quot;, &amp;quot;edgeLabelBackground&amp;quot;: &amp;quot;#ffffff&amp;quot;}}}%%
flowchart LR
A[&amp;quot;18 Sep: release 0.79.0 pushed&amp;lt;br/&amp;gt;latest = 0.79.0&amp;quot;] --&amp;gt; B[&amp;quot;30 Sep: pre-release 0.80.0-rc.2 pushed&amp;lt;br/&amp;gt;latest unchanged&amp;quot;]
B --&amp;gt; C[&amp;quot;30 Sep: deploy pulls latest&amp;lt;br/&amp;gt;runs 0.79.0&amp;quot;]
C --&amp;gt; D[&amp;quot;1 Oct: release 0.80.0 pushed&amp;lt;br/&amp;gt;latest = 0.80.0&amp;quot;]
D --&amp;gt; E[&amp;quot;same deploy, same repository&amp;lt;br/&amp;gt;would run 0.80.0&amp;quot;]
&lt;/code>&lt;/pre>
&lt;h2 id="when-you-cannot-look-infer-it-and-say-how-sure-you-are">When you cannot look, infer it and say how sure you are&lt;/h2>
&lt;p>Pinning means writing down the version that runs today; pin to anything else and the pin is itself an upgrade. I had no shell on the server from where I worked, so I could not ask the containers so I used two other methods, with different confidence.&lt;/p>
&lt;p>For the VPN&amp;rsquo;s web dashboard I fingerprinted the version. I downloaded the static files of three adjacent releases from the registry, picked files that exist in only one of them, and requested those from the live site. The files unique to one release answered 200, the ones unique to the other two answered 404.
For the server component there was nothing to request, so I dated it. The deploy log gave the time of the last pull, the registry gave the push dates, and &lt;code>latest&lt;/code> on that day pointed at 0.79.0. A pre-release pushed the same day did not move &lt;code>latest&lt;/code>, as the project&amp;rsquo;s release workflow confirms. The dashboard fingerprint came from the same pull and agreed. What remains is the assumption that nobody pulled by hand since, and I wrote it into the change as an assumption: if it is wrong, the next deploy rolls those containers back one release.&lt;/p>
&lt;p>Beside every pin I recorded how I know: fingerprinted, inferred from dates, or unchanged since long before the deploy. That a re-run now changes nothing stays unverified until someone does a real pull on the server, and I listed it that way.&lt;/p>
&lt;h2 id="a-pin-tells-you-what-was-installed-not-what-runs">A pin tells you what was installed, not what runs&lt;/h2>
&lt;p>The WordPress image showed me the limit. I pinned it to the version its frozen &lt;code>latest&lt;/code> had always been, then saw that the public site announces a newer WordPress core than the image contains. Core had updated itself inside the container&amp;rsquo;s volume. The image says 6.8.2, the site runs 6.9.4, and re-creating the container might roll core back. It is tracked, not fixed.&lt;/p>
&lt;p>So before your next deploy, list every image reference in your configuration and mark those with no tag or with &lt;code>latest&lt;/code>. Pin each to the version that runs now, found by asking the container if you can and by dates and file fingerprints if you cannot, and write beside the pin how you know. Then upgrade on purpose, as one change you can read, instead of whenever the deploy happens to run.&lt;/p></description></item></channel></rss>