Journal

Self-hosted Docker Registry instead of Docker Hub — why it's a better solution

Docker Hub is the default place to store Docker images. But it has its limitations — and there's a better way, especially if you already have your own infrastructure.

The problem with Docker Hub

Docker Hub on the free plan:

  • Pull rate limits — 100 pulls per 6 hours for anonymous users, 200 for logged-in users. A production server that restarts containers frequently can hit this limit fast
  • No code integration — you manage code in one place and images in another
  • Namespace — popular names are taken, hub.docker.com/u/yourname/app doesn't feel like your own infrastructure
  • Privacy — your images sit on someone else's servers

Self-hosted registry from GitLab

GitLab Community Edition has a built-in Container Registry. After setting up your own GitLab, you immediately get a registry under your own domain — no extra configuration needed.

Example from my project minio-dash — a MinIO admin panel written in Python/Flask. The image lives on my own registry:

# Pull from own registry
docker pull registry.racis.dev/marceliracis/minio-dash:latest

# Run
docker run -d \
  --name minio-dash \
  --restart unless-stopped \
  -p 7474:7474 \
  -e MINIO_ENDPOINT=https://s3.example.com \
  -e SECRET_KEY=changeme \
  registry.racis.dev/marceliracis/minio-dash:latest

Instead of hub.docker.com/u/marceliracis/minio-dash I have registry.racis.dev/marceliracis/minio-dash — under my own domain, on my own server.

Advantages of self-hosted registry

Integration with code — repo and images in one place. A CI/CD pipeline builds the image and immediately pushes it to the registry without leaving your own infrastructure. In minio-dash I have a .gitlab-ci.yml that automatically builds and pushes the image on every commit.

No pull limits — your server can pull images as many times as it wants, no throttling. With frequent deploys, this matters.

Privacy — images are only accessible to people with access to your GitLab.

Deploy tokens — you can create a read-only registry token for the production server, without giving it full access to the code.

Multi-arch in one place — I build images for both amd64 and arm64 simultaneously and push to my own registry:

docker buildx build \
  --platform linux/amd64,linux/arm64 \
  -t registry.racis.dev/marceliracis/minio-dash:latest \
  --push \
  .

How to push images?

# Log in to the registry
docker login registry.racis.dev

# Build and push in one command (buildx)
docker buildx build \
  --platform linux/amd64,linux/arm64 \
  -t registry.racis.dev/project/app:v1 \
  -t registry.racis.dev/project/app:latest \
  --push \
  .

Summary

A self-hosted registry is a natural extension of self-hosted GitLab. Code, pipelines, and images in one place, under your own domain, without limits and without dependency on external services. If you already have a self-hosted GitLab — you get the registry for free.