Docker – Host Your Own Private Container Registry

Docker Hub ( provides a great and Free service for pushing, and pulling of course, Docker containers. Many of the major software providers use Docker Hub for their primary container distribution, and you yourself can create an account and push your own images.

They do, however, only allow one free private repository. This may not be an issue if you are only looking for hosting your images public. And that you also have taken the precaution not to include any secrets in the container image, accidental or otherwise…

Luckily, it’s a walk in the park to deploy an Ubuntu server and roll your own!

First, deploy your VM where you see fit. I can personally vouch for Linode.

Second, Install Docker Engine and Docker-Compose. Follow the Official Guide For Docker Engine and install Docker-Compose with:

~$ sudo apt install -y docker-compose

Third, Create directory structure and paste the following Docker-Compose YAML:

~$ mkdir -p ~/docker-registry/{auth,data}
~$ cat <<EOF > ~/docker-registry/docker-compose.yaml
> version: '3'

    restart: always
    image: registry:2
    - "5000:5000"
      REGISTRY_AUTH: htpasswd
      REGISTRY_AUTH_HTPASSWD_PATH: /auth/registry.password
      - ./auth:/auth
      - ./data:/data

Forth, Install htpasswd and create your first docker-registry user.

~$ sudo apt install apache2-utils -y
~$ htpasswd -Bc ~/docker-registry/auth/registry.password [USERNAME]
... <Password>

Fifth, Its recommended to use NGinx as a, with HTTPS, reverse proxy. Here’s the configuration i use.

~$ sudo apt install nginx -y
~$ sudo cat <<EOF > /etc/nginx/sites-available/dockerreg
server {
    listen 443 ssl http2;
    listen [::]:443 ssl http2;
    server_name dockerreg.tld;
    error_log /var/log/nginx/dockerreg-tld-error.log;
    access_log /var/log/nginx/dockerreg-tld-access.log;
    ssl_certificate /etc/letsencrypt/live/dockerreg.tld/fullchain.pem; # managed by Certbot
    ssl_certificate_key /etc/letsencrypt/live/dockerreg.tld/privkey.pem; # managed by Certbot
    ssl_dhparam /etc/letsencrypt/ssl-dhparams.pem; # managed by Certbot
    ssl_session_timeout 1d;
    ssl_session_cache shared:MozSSL:10m;  # about 40000 sessions
    ssl_session_tickets off;
    ssl_protocols TLSv1.2 TLSv1.3;
    ssl_prefer_server_ciphers off;

    location / {
    if ($http_user_agent ~ "^(docker\/1\.(3|4|5(?!\.[0-9]-dev))|Go ).*$" ) {
      return 404;

    proxy_pass                          http://localhost:5000;
    proxy_set_header  Host              $http_host;   # required for docker client's sake
    proxy_set_header  X-Real-IP         $remote_addr; # pass on real client's IP
    proxy_set_header  X-Forwarded-For   $proxy_add_x_forwarded_for;
    proxy_set_header  X-Forwarded-Proto $scheme;
    proxy_read_timeout                  900;
} > EOF
~$ sudo ln -s /etc/nginx/sites-available/dockerreg /etc/nginx/site-enabled/dockerreg
~$ sudo systemctl restart nginx

(Replace dockerreg with your own domain, and certificate of course)

Sixth, start the Docker Registry as daemon

~$ cd ~/docker-registry/ && docker-compose up -d

Seventh, test login to registry

~$ curl -X GET -u <user>:<pass> https://dockerreg.tld/v2/_catalog

You should get an “{}” as response

Eighth, login to your new Docker Registry! 🙂

~$ docker login dockerreg.tld
Username: ...
Password: ...

Ninth, push your first image!

# docker build.. docker tag..
~$ docker push dockerreg.tld/imagename

Tenth, your are done!

You can now pull the image when authenticated to your hosts/servers.

Build away 👾

Leave a Reply

Your email address will not be published. Required fields are marked *