Why Run a Private Registry?
Docker Hub is convenient, but there are strong reasons to run your own private registry:
- Privacy — keep proprietary application code off public infrastructure.
- Speed — a registry co-located with your build servers or cluster means faster pushes and pulls.
- Control — enforce retention policies, vulnerability scanning, and access controls.
- Cost — avoid per-seat fees for private repositories at scale.
Option 1: Docker Registry (Open Source)
The official registry:2 image is a lightweight, open-source registry you can run anywhere.
docker run -d --name registry --restart always -p 5000:5000 -v registry_data:/var/lib/registry registry:2Push and pull locally:
docker tag my-app:latest localhost:5000/my-app:latest
docker push localhost:5000/my-app:latest
docker pull localhost:5000/my-app:latestThis works for local testing but has no authentication or TLS — not suitable for production as-is.
Option 2: Registry with TLS and Basic Auth
For a production-grade self-hosted registry, add TLS and authentication.
Generate a self-signed certificate (or use Let's Encrypt for a real domain):
mkdir -p certs auth
# Generate self-signed cert for local testing
openssl req -newkey rsa:4096 -nodes -sha256 -keyout certs/registry.key -x509 -days 365 -out certs/registry.crt -subj "/CN=registry.example.com"
# Create htpasswd credentials
docker run --rm --entrypoint htpasswd httpd:2 -Bbn myuser mypassword > auth/htpasswddocker-compose.yml for a secure registry:
services:
registry:
image: registry:2
restart: always
ports:
- "443:5000"
volumes:
- registry_data:/var/lib/registry
- ./certs:/certs:ro
- ./auth:/auth:ro
environment:
REGISTRY_HTTP_TLS_CERTIFICATE: /certs/registry.crt
REGISTRY_HTTP_TLS_KEY: /certs/registry.key
REGISTRY_AUTH: htpasswd
REGISTRY_AUTH_HTPASSWD_REALM: Registry
REGISTRY_AUTH_HTPASSWD_PATH: /auth/htpasswd
REGISTRY_STORAGE_DELETE_ENABLED: "true"
volumes:
registry_data:docker compose up -d
# Log in before pushing
docker login registry.example.com
docker tag my-app:latest registry.example.com/my-app:latest
docker push registry.example.com/my-app:latestOption 3: GitHub Container Registry (ghcr.io)
GitHub Container Registry is the easiest private registry if you're already on GitHub — free for public images, included with GitHub packages for private ones.
# Authenticate with a personal access token
echo $GITHUB_TOKEN | docker login ghcr.io -u USERNAME --password-stdin
# Tag and push
docker tag my-app:latest ghcr.io/your-org/my-app:latest
docker push ghcr.io/your-org/my-app:latestIn CI/CD (GitHub Actions):
# .github/workflows/build.yml
- name: Log in to GHCR
uses: docker/login-action@v3
with:
registry: ghcr.io
username: ${{ github.actor }}
password: ${{ secrets.GITHUB_TOKEN }}
- name: Build and push
uses: docker/build-push-action@v5
with:
push: true
tags: ghcr.io/${{ github.repository }}:${{ github.sha }}Image Retention and Cleanup
Unmanaged registries fill up fast. Set up automated cleanup:
# List all tags for a repository via the registry API
curl -u myuser:mypassword https://registry.example.com/v2/my-app/tags/list
# Delete a specific image digest
DIGEST=$(curl -u myuser:mypassword -sI -H "Accept: application/vnd.docker.distribution.manifest.v2+json" https://registry.example.com/v2/my-app/manifests/old-tag | grep Docker-Content-Digest | awk '{print $2}' | tr -d #x27;
')
curl -u myuser:mypassword -X DELETE https://registry.example.com/v2/my-app/manifests/$DIGESTFor automated retention, tools like registry-garbage-collector or crane simplify cleanup:
# Keep only the 10 most recent tags
crane delete $(crane ls ghcr.io/your-org/my-app | head -n -10 | xargs -I {} echo ghcr.io/your-org/my-app:{})Deploying from a Private Registry on PandaStack
PandaStack supports container deployments from GitHub — connect your GitHub repository and PandaStack builds your image directly from the Dockerfile in your repo. No external registry management required.
npm install -g @pandastack/cli
panda deploy --type container --repo your-org/your-repoThis integrates with private GitHub repositories using GitHub App authentication — your source code never leaves your control. Visit [dashboard.pandastack.io](https://dashboard.pandastack.io) to connect your GitHub account, or see the full guide at [docs.pandastack.io](https://docs.pandastack.io).