Integral Review

Welcome to my personal blog! I use it to share what I'm currently learning or thinking about, usually on topics related to technology, business, and health.

Setting up a free private Docker registry for deploying with Kamal

If you're getting started with Kamal, you saw that a container registry is necessary to store your Docker images. If your code is public, that's easy enough to find, but if it's private, you might not want to get a subscription only for a Docker registry. Or you might enjoy the idea of handling everything from end-to-end... in any case, here's how to do it.

Going with Cloudflare R2 and Workers

We'll be using Cloudflare's R2. It is their response to Amazon's S3. It offers free egress bandwidth and a decent free plan with 5GB of storage (over this threshold, the pricing is $0.015 per GB per month, which is super low anyway). At the end of the article, I'll show how to make sure you stay within the free plan.

Since R2 is just the storage portion, we also need the system that will process all the Docker push, pull, and other commands we'll need. There are multiple options available to us, but until now, they all relied on us hosting a Docker container, which was a bit more work and maintenance than I liked.

Thankfully, Cloudflare released an OCI-compliant registry that runs entirely on their workers platform. The big advantage for us is that Cloudflare Workers have a very large free plan, the deployment is easy, and once deployed, we do not have to do any kind of maintenance work.

Deploying the container registry

To get started, clone the repository:

git clone https://github.com/cloudflare/serverless-registry.git
lang-bash

Note that by directly cloning the repo, we'll just have to pull future updates and redeploy when we want to update our registry.

To get it running, just follow the instructions in their readme, it won't take more than a few minutes. The most important task is setting up an R2 bucket to store your images and configuring credentials.

Once done, you'll have a dedicated domain to access your private container registry. Pretty neat!

Configuring Kamal to use your new registry

Now that you have the registry's URL and credentials, open up the deploy.yml configuration.

registry:
  server: YOUR.REGISTRY-DOMAIN.workers.dev
  username: yourusername
  password:
  - KAMAL_REGISTRY_PASSWORD
lang-yaml

And finally, update the secrets file to ensure Kamal has access to your registry.

Kamal is now fully set up with your own container registry. I've read some complaints that Kamal requires hosting Docker images somewhere, but considering how easy it is to do, I don't think that's a big deal.

Caveats and keeping things free

The only real limitation with this approach is that Cloudflare limits the request body size. With a free plan, you can't exceed 100MB. There's a simple workaround available, but it won't work with Kamal out of the box. In any case, the 100MB limit is per layer, not per image. For most web applications, this won't ever be a problem.

Finally, to make sure you do not accumulate images and stay within the free plan, you should periodically clean up your registry. If you like automation, a potential solution could be to add a Kamal post-deploy hook. Here's the file I use:

echo "$KAMAL_PERFORMER deployed $KAMAL_VERSION to $KAMAL_DESTINATION in $KAMAL_RUNTIME seconds"

# Get the directory where the script is located
SCRIPT_DIR="$( cd "$( dirname "${BASH_SOURCE[0]}" )" && pwd )"

# Load environment variables from .env
set -a
source "$SCRIPT_DIR/.env.registry"
set +a

echo "[$(date '+%Y-%m-%d %H:%M:%S')] Fetching credentials from 1Password..."

# Get credentials from 1Password
USERNAME=$(op read "op://$PASSWORD_VAULT/$ITEM_NAME/$USERNAME_FIELD")
PASSWORD=$(op read "op://$PASSWORD_VAULT/$ITEM_NAME/$PASSWORD_FIELD")

if [ -z "$USERNAME" ] || [ -z "$PASSWORD" ]; then
    echo "Failed to retrieve credentials from 1Password"
    exit 1
fi

echo "[$(date '+%Y-%m-%d %H:%M:%S')] Starting registry cleanup..."

# Run registry cleanup
docker run --rm \
    anoxis/registry-cli \
    -r "$REGISTRY_URL" \
    -l "$USERNAME:$PASSWORD" \
    -i velism \
    --delete \
    --keep-tags-like latest

status=$?

if [ $status -eq 0 ]; then
    echo "[$(date '+%Y-%m-%d %H:%M:%S')] Cleanup completed successfully."
else
    echo "[$(date '+%Y-%m-%d %H:%M:%S')] Cleanup failed with exit status: $status"
    exit $status
fi
lang-bash

You're now fully setup to stay under the 5GB limit :)

#cloudflare #infrastructure #tutorial

💬 Comments
Subscribe to future posts