Skip to content

Latest commit

 

History

7 Commits

Folders and files

NameName
Last commit message
Last commit date
 
 
 
 
 
 
 
 
 
 
 
 
 
 

Repository files navigation

Local Docker Registry

A small, Make-driven deployment for running a private HTTP Docker Registry with Docker Compose.

The project provides:

  • Compose-based Registry lifecycle management
  • Persistent Registry storage on the host
  • Idempotent client configuration for Docker Engine
  • Safe merging of /etc/docker/daemon.json
  • Configuration validation, backup, and rollback
  • A basic push smoke test

This deployment uses plain HTTP without authentication. It is intended only for trusted, firewall-restricted internal networks.

Project Layout

.
├── compose.yaml
├── Makefile
├── .env.example
├── README.md
└── scripts/
    └── configure-client.sh

Registry blobs and manifests are stored under /var/lib/docker-registry by default. This keeps persistent application data under /var/lib rather than placing it in /etc, which should remain reserved for configuration.

Docker client configuration is managed through the standard Linux Docker Engine configuration file:

/etc/docker/daemon.json

Requirements

Registry server

  • Linux
  • Docker Engine
  • Docker Compose v2 (docker compose)
  • GNU Make
  • sudo
  • curl for make status

Docker clients

  • Linux Docker Engine
  • GNU Make
  • Python 3
  • sudo
  • systemctl or service for restarting Docker

Quick Start

Create the local environment file:

cp .env.example .env

Review and edit it as needed:

nano .env

Start the Registry:

make up

Check the container and API status:

make ps
make status

The default endpoint is:

http://SERVER_IP:5000

Server Management

Start or update the Registry service:

make up

Stop and remove the Compose container and network:

make down

Persistent Registry data is not removed by make down.

Restart the Registry container:

make restart

Follow the Registry logs:

make logs

Show the current service state:

make ps

Render the fully resolved Compose configuration:

make config

Pull the configured Registry image without starting the service:

make pull

Client Configuration

Because this Registry uses plain HTTP, every Docker host that pushes to or pulls from it must list the Registry endpoint under Docker Engine's insecure-registries configuration.

Run the following command on each Docker client:

make connect REGISTRY=172.20.4.171:5000

When the port is omitted, port 5000 is used automatically:

make connect REGISTRY=172.20.4.171

Hostnames are also supported:

make connect REGISTRY=registry.internal:5000

Bracketed IPv6 addresses are supported:

make connect REGISTRY='[2001:db8::10]:5000'

Remove an endpoint from Docker's insecure-registries list:

make disconnect REGISTRY=172.20.4.171:5000

Display the insecure registries currently reported by Docker Engine:

make client-config

Client configuration behavior

The client configuration script performs the following operations:

  1. Reads the existing /etc/docker/daemon.json, or starts with an empty JSON object if the file does not exist.
  2. Preserves all unrelated Docker daemon settings.
  3. Adds or removes only the requested insecure-registries entry.
  4. Removes duplicate entries while preserving their original order.
  5. Creates a timestamped backup beside the original configuration file.
  6. Validates the complete generated configuration with dockerd --validate when dockerd is available.
  7. Installs the updated file with root ownership and mode 0644.
  8. Restarts Docker through systemctl or service.
  9. Restores the previous configuration automatically if Docker fails to restart.

The operation is idempotent. Adding an existing endpoint or removing an absent endpoint produces no configuration change and does not restart Docker.

Testing the Registry

Run the built-in smoke test on the Registry server:

make smoke

The smoke test pulls alpine:latest, tags it for the local Registry, pushes it, and removes the temporary local tag.

A manual test from a configured Docker client can be performed as follows:

docker pull alpine:latest
docker tag alpine:latest 172.20.4.171:5000/alpine:latest
docker push 172.20.4.171:5000/alpine:latest
docker pull 172.20.4.171:5000/alpine:latest

To query the Registry API directly:

curl http://172.20.4.171:5000/v2/

A successful Registry normally returns an empty JSON object:

{}

Configuration

The deployment is configured through .env.

REGISTRY_IMAGE=registry:2
CONTAINER_NAME=registry
RESTART_POLICY=unless-stopped

REGISTRY_BIND_ADDRESS=0.0.0.0
REGISTRY_PORT=5000
REGISTRY_DATA_DIR=/var/lib/docker-registry

REGISTRY_STORAGE_DELETE_ENABLED=false

REGISTRY_IMAGE

Registry container image to run.

REGISTRY_IMAGE=registry:2

CONTAINER_NAME

Explicit Docker container name.

CONTAINER_NAME=registry

RESTART_POLICY

Docker restart policy applied to the Registry container.

RESTART_POLICY=unless-stopped

REGISTRY_BIND_ADDRESS

Host address on which the Registry port is published.

The default listens on every interface:

REGISTRY_BIND_ADDRESS=0.0.0.0

For an internal deployment, binding only to the server's LAN address is safer:

REGISTRY_BIND_ADDRESS=172.20.4.171

REGISTRY_PORT

Host port mapped to port 5000 inside the Registry container.

REGISTRY_PORT=5000

REGISTRY_DATA_DIR

Absolute host path used for persistent Registry storage.

REGISTRY_DATA_DIR=/var/lib/docker-registry

make prepare creates this directory as root:root with mode 0750.

REGISTRY_STORAGE_DELETE_ENABLED

Controls whether the Registry API permits image deletion.

REGISTRY_STORAGE_DELETE_ENABLED=false

Keep deletion disabled unless a deliberate retention and garbage-collection procedure has been established.

Available Make Targets

make help           Show available targets
make init           Create .env from .env.example when missing
make check          Verify Docker Engine and Docker Compose
make prepare        Create the persistent storage directory
make config         Print the resolved Compose configuration
make pull           Pull the configured Registry image
make up             Create or update and start the Registry
make down           Stop the Registry while preserving data
make restart        Restart the Registry container
make logs           Follow Registry logs
make ps             Show Compose service status
make status         Check the local Registry HTTP API
make smoke          Push a temporary Alpine image
make connect        Add an insecure Registry endpoint to Docker
make disconnect     Remove an insecure Registry endpoint from Docker
make client-config  Show Docker's insecure Registry configuration

Make variables can be overridden per invocation:

make up ENV_FILE=.env.production PROJECT_NAME=lab-registry

Persistent Data

The Registry storage directory is bind-mounted into the container:

/var/lib/docker-registry  ->  /var/lib/registry

Removing or recreating the container does not delete stored images. Back up this directory using the same policy applied to other persistent service data.

To inspect its disk usage:

sudo du -sh /var/lib/docker-registry

Do not manually edit files inside the Registry storage directory while the service is running.

Security Considerations

This configuration intentionally provides no TLS and no authentication. As a result:

  • Registry traffic is unencrypted.
  • Docker credentials must not be sent to this endpoint.
  • Any reachable host may be able to push or pull images.
  • Image contents and tags may be modified by unauthorized internal clients.

Use this deployment only when all of the following are true:

  • The Registry is on a trusted internal network.
  • Firewall rules restrict access to approved hosts or subnets.
  • The Registry port is not exposed to the public Internet.
  • Stored images do not contain embedded secrets.

For production or multi-user environments, place the Registry behind TLS and add an authentication layer. Once TLS is enabled with a trusted certificate, remove the endpoint from Docker's insecure-registries list.

Troubleshooting

Registry does not become ready

Inspect the service state and logs:

make ps
make logs

Validate the resolved Compose configuration:

make config

Check whether the configured port is already in use:

sudo ss -lntp | grep ':5000'

Push fails with an HTTPS error

A typical message is:

http: server gave HTTP response to HTTPS client

The Docker client has not been configured to trust the HTTP Registry. Run:

make connect REGISTRY=SERVER_IP:5000

Then verify the applied configuration:

make client-config

Docker fails to restart after client configuration

The configuration script automatically restores the previous daemon.json when restart fails. Inspect Docker logs for the underlying error:

sudo journalctl -u docker --no-pager -n 200

Existing backups are stored beside the Docker daemon configuration using names similar to:

/etc/docker/daemon.json.backup-20260729-204000-12345

Permission denied under the data directory

Recreate the directory with the expected ownership and mode:

make prepare

Then restart the service:

make restart

About

Personal Docker registry service container script

Topics

Resources

Stars

0 stars

Watchers

0 watching

Forks

Releases

Packages

Contributors

Languages