Artifacts with OCI (& Docker)

December 18, 2025 · Tyler Yeager · 3 min reading time

For over ten years, I’ve had a background desire that I’ve been trying to find a solution too. In short, it was about sharing programs that I wrote, simply.

Writing Python back in 2014, the worst part was sharing the code with someone else. Technical, fine. I can have them install the correct dependencies. But non-technical? Good luck getting them to install the Python runtime, installing dependencies or anything like that.

The answer eventually came around to binaries. Discovering Golang felt like the exact kind of discovery I needed. Built in binary compilation? Cross OS capability? Sign me up!

But, then there was a new issue. How do I host a binary to be available for download? I tried a basic file server, s3 object storage, licensing software, but nothing has felt quite right. Except, I genuinely like pre-signed urls available with s3 storage.

So, my requirements were:

  • Ability to share a single binary. It should be able to include extra items like fonts / config variables.
  • Cross OS compilation. Easy to distribute to Windows / Mac
  • Easy to host, easy to update and provide updates for.

OCI Storage

Well, I stumbled upon Oras, and it feels like the right kind of storage. OCI storage is best known for Docker image storage. Think docker image pull or push

I already have a docker registry, so this would just piggyback on top of that.

Uploading is fine. Oras provides a cli called oras to provide push and pull. If you’re already logged into using Docker, then this doesn’t require any additional authentication. I’m a big Docker fan, so this worked out.

Terminal window
oras push docker.mydomain.tld/binaries/myApp:v1 ./myApp

To download, it’s the opposite: oras pull [...]. But you probably see the problem here! If I was so preoccupied with easy distribution to non-technical folks, I have no chance distributing this to people and leading with “Well, first download oras…”.

Downloading from OCI, Directly

I needed a download link. Which led me to the question, how do you download something directly from a docker OCI image. repository? Turns out that’s not an especially easy answer

In fact, debugging with docker is cryptic as well. What network calls does docker pull use? Who knows? Good luck! I ended up using a mocking tool (mockoon) to proxy the request and reverse engineer the requests made by docker.

Eventually, as a first iteration I came up with this:

Terminal window
curl https://docker.mydomain.tld/v2/myApp/manifests/sha256:44136fa355b3678a1146ad16f7e8649e94fb4fc21fe77e8310c060f61caaff8a \
-sL -H 'Accept: application/vnd.oci.image.manifest.v1+json' \
| jq -r '.layers[0].digest'

You can see the response below. You’ll see under the layers there is a digest. Since we uploaded a binary, there’s only one layer.

{
9 collapsed lines
"schemaVersion": 2,
"mediaType": "application/vnd.oci.image.manifest.v1+json",
"artifactType": "application/vnd.unknown.artifact.v1",
"config": {
"mediaType": "application/vnd.oci.empty.v1+json",
"digest": "sha256:44136fa355b3678a1146ad16f7e8649e94fb4fc21fe77e8310c060f61caaff8a",
"size": 2,
"data": "e30="
},
"layers": [
{
"mediaType": "application/vnd.oci.image.layer.v1.tar",
"digest": "sha256:aa437812b9bd7457a9d1945680c453d2ef217264fbf5ed896053991d2b212d15",
9 collapsed lines
"size": 7602468,
"annotations": {
"org.opencontainers.image.title": "straico"
}
}
],
"annotations": {
"org.opencontainers.image.created": "2025-12-18T04:10:33Z"
}
}

To download this, we can just download it directly:

Terminal window
wget https://docker.mydomain.tld/v2/myApp/blobs/sha256:aa437812b9bd7457a9d1945680c453d2ef217264fbf5ed896053991d2b212d15 -O /ram/myApp

Automated

Ok. But can I just download it as easily as providing the same kind of url like in docker? Something like this?

Terminal window
./download.sh docker.mydomain.tld/myApp:v1

We need to follow through on a few steps

  1. (Optional) List all tags
  2. Get manifest associated with a specific tag
  3. Get first layer associated with that manifest
  4. Download the layer, which we know is our binary

For far more details, go here, but we’ll just implement it here really quick:

download.sh
#!/usr/bin/env bash
url="$(echo "$1" | cut -d '/' -f1)"
path="$(echo "$1" | cut -d '/' -f2)"
package="$(echo "$path" | cut -d ':' -f1)"
tag="$(echo "$path" | cut -d ':' -f2)"
# Step 1, List all tags
curl "https://$url/v2/$package/tags/list"
# Step 2, get the manifest for the tag we want (Plus an extra filename variable
manifest="$(curl -sL "https://$url/v2/$package/manifests/$tag" -H 'Accept: application/vnd.oci.image.manifest.v1+json')"
filename="$(echo $manifest | jq -r '.layers[0].annotations."org.opencontainers.image.title"')"
# Step 3, we get the digest of the first layer
digest="$(echo $manifest | jq -r '.layers[0].digest')"
# Finally, we download the binary
wget "https://$url/v2/$package/blobs/$digest" -O "$filename"

At the start, I said I wanted to be able to share this with non-technical users, well sharing this bash script doesn’t solve that either.

Realistically, you could just echo out the url and share that instead. It would look something like

https://docker.mydomain.tld/v2/myApp/blobs/sha256:aa437812b9bd7457a9d1945680c453d2ef217264fbf5ed896053991d2b212d15

Did you find this interesting?

Consider subscribing 😊

No AI-generated content or SEO garbage.

Unsubscribe anytime