How to Run a Private Docker Registry on Raspberry Pi

How to Run a Private Docker Registry on Raspberry Pi

A private Docker registry lets you store your own container images locally instead of relying on Docker Hub. This is useful for home lab projects, CI/CD pipelines, and keeping your images on your own network. The official registry:2 image is lightweight and runs well on a Raspberry Pi.

Prerequisites

Before getting started, make sure you have:

  • A Raspberry Pi 3 or newer running Raspberry Pi OS (64-bit recommended).
  • Docker and Docker Compose installed. If you need to set these up, see our Docker setup guide.
  • SSH access to your Raspberry Pi.
  • Another machine on your network with Docker installed for testing pushes and pulls.

Step 1: Create the Project Directory

SSH into your Raspberry Pi and create a directory for the registry:

Bash
mkdir -p ~/docker-registry && cd ~/docker-registry

Step 2: Create the Docker Compose File

Create a docker-compose.yml file with the following content:

YAML
version: "3"

services:
  registry:
    image: registry:2
    container_name: docker-registry
    restart: always
    ports:
      - "5000:5000"
    volumes:
      - ./registry-data:/var/lib/registry
    environment:
      - REGISTRY_STORAGE_DELETE_ENABLED=true

This maps port 5000 on the Pi to the registry container and stores all image data in the ./registry-data directory on disk.

Step 3: Start the Registry

Launch the registry container and verify it is running:

Bash
cd ~/docker-registry
docker compose up -d
docker compose ps
curl http://localhost:5000/v2/_catalog

You should see {"repositories":[]} since no images have been pushed yet.

Step 4: Configure Docker for Insecure Registries

By default, Docker requires HTTPS for registry communication. Since we are running on a local network without TLS, you need to configure Docker to allow insecure connections. On each machine that will push or pull images, edit or create the daemon configuration file:

Bash
sudo nano /etc/docker/daemon.json

Add the following content, replacing <your-pi-ip> with the actual IP address of your Raspberry Pi:

JSON
{
  "insecure-registries": ["<your-pi-ip>:5000"]
}

Then restart Docker for the change to take effect:

Bash
sudo systemctl restart docker

Step 5: Tag, Push, and Pull Images

Now you can push images to your private registry. Pull a test image, tag it, and push:

Bash
docker pull alpine:latest
docker tag alpine:latest <your-pi-ip>:5000/my-alpine:latest
docker push <your-pi-ip>:5000/my-alpine:latest

Verify it was stored:

Bash
curl http://<your-pi-ip>:5000/v2/_catalog

You should now see {"repositories":["my-alpine"]}. To pull from another machine (after configuring insecure registries on that machine), run docker pull <your-pi-ip>:5000/my-alpine:latest.

You can also list tags for a specific image:

Bash
curl http://<your-pi-ip>:5000/v2/my-alpine/tags/list

Step 6: Add Basic Authentication

For additional security, you can protect your registry with basic authentication using htpasswd. Install the tool, create an auth directory, and generate a password file:

Bash
sudo apt install -y apache2-utils
mkdir -p ~/docker-registry/auth
htpasswd -Bc ~/docker-registry/auth/htpasswd myuser

You will be prompted to enter and confirm a password. Now update your docker-compose.yml to enable authentication:

YAML
version: "3"

services:
  registry:
    image: registry:2
    container_name: docker-registry
    restart: always
    ports:
      - "5000:5000"
    volumes:
      - ./registry-data:/var/lib/registry
      - ./auth:/auth
    environment:
      - REGISTRY_STORAGE_DELETE_ENABLED=true
      - REGISTRY_AUTH=htpasswd
      - REGISTRY_AUTH_HTPASSWD_REALM=Registry Realm
      - REGISTRY_AUTH_HTPASSWD_PATH=/auth/htpasswd

Restart the registry to apply the changes:

Bash
cd ~/docker-registry
docker compose down
docker compose up -d

Now, before pushing or pulling images, you must log in:

Bash
docker login <your-pi-ip>:5000

Enter the username and password you created with htpasswd. If you have authentication enabled and want to use the API with curl, add credentials: curl -u myuser:mypassword http://<your-pi-ip>:5000/v2/_catalog.

Troubleshooting

  • "http: server gave HTTP response to HTTPS client": You need to add your registry to insecure-registries in /etc/docker/daemon.json and restart Docker.
  • Push fails with authentication error: Run docker login <your-pi-ip>:5000 before pushing.
  • Cannot connect to port 5000: Check your firewall settings. If UFW is enabled, run sudo ufw allow 5000.
  • Registry data growing too large: Enable the garbage collector by running docker exec docker-registry bin/registry garbage-collect /etc/docker/registry/config.yml to clean up unreferenced layers.
  • Container won't start after enabling auth: Verify the auth/htpasswd file exists and the path in the compose file is correct.

Conclusion

You now have a private Docker registry running on your Raspberry Pi. This gives you a local, fast, and private place to store your container images. Combined with a CI/CD pipeline, your registry can serve as the backbone of a fully self-hosted development workflow right from your home network.