Back to Blog
Tutorial7 min read2026-05-01

Private Docker Registry: Setup and Best Practices

Set up a private Docker registry to host your container images securely, with authentication, TLS, and image retention policies.

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:2

Push 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:latest

This 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/htpasswd

docker-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:latest

Option 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:latest

In 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/$DIGEST

For 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-repo

This 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).

Ready to deploy?

Start free on PandaStack — no credit card required.

Start free on PandaStack

More in Tutorial

Browse all Tutorial articles →

See also

' curl -u myuser:mypassword -X DELETE https://registry.example.com/v2/my-app/manifests/$DIGEST For automated retention, tools like registry-garbage-collector or crane simplify cleanup: bash 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. bash npm install -g @pandastack/cli panda deploy --type container --repo your-org/your-repo This integrates with private GitHub repositories using GitHub App authentication — your source code never leaves your control. Visit dashboard.pandastack.iohttps://dashboard.pandastack.io to connect your GitHub account, or see the full guide at docs.pandastack.iohttps://docs.pandastack.io.","wordCount":525,"datePublished":"2026-05-01","dateModified":"2026-05-01","author":{"@type":"Organization","name":"PandaStack","url":"https://pandastack.io"},"publisher":{"@type":"Organization","name":"PandaStack","url":"https://pandastack.io","logo":{"@type":"ImageObject","url":"https://pandastack.io/logo.png","width":512,"height":512}},"mainEntityOfPage":{"@type":"WebPage","@id":"https://pandastack.io/blog/private-docker-registry-setup"},"image":{"@type":"ImageObject","url":"https://pandastack.io/og-pandastack.png","width":1200,"height":630},"isPartOf":{"@type":"Blog","@id":"https://pandastack.io/blog","name":"PandaStack Blog"},"keywords":"Tutorial"} PandaStack — Deploy Everything, Everywhere