How to setup private docker registry for your projects to save money

fuksito profile image Vitaliy Yanchuk Updated on ・3 min read

Develop Privately (2 Part Series)

1) How to setup private docker registry for your projects to save money 2) Private CI using private docker registry with Drone.io (almost free)

I got to use docker for my projects which are not open-sourced, some of them are hobby projects or try-outs which I don't want yet to publish, so I need to store them privately.

DockerHub only allows 1 private image to be stored in the Free plan, 5 images for 7$ and 10 for 12$ which is decent price for production projects, but for personal hobby projects may be a lot.

I have a 5$ server on DigitalOcean for personal needs, which I want also to use as a docker registry, and CI for my private projects.

The steps in this guide are not the only one possible, there are many ways to achieve same result, but I tried to make it as quick and as simple as possible, I will describe each configuration why it is required.


  1. I am using Ubuntu 18.04, so all commands will be related to this system.

  2. You need to have Docker and Docker Compose installed on your host Official docs:

  3. You need to have an installed nginx.

  4. Examples assume you have a domain: example.com, and that you want to start registry on: registry.example.com

Step 1: Setup nginx

I don't use nginx as docker container as I have also some other static sites on same machine, which I don't want to wrap into containers. And also I find it easier to have nginx outside containers and on the host machine.

ssh to host machine and install nginx, if you don't have it yet

apt install nginx

Then create nginx configuration for your domain:


upstream docker-registry {

server {
    server_name registry.example.com;

    server_name_in_redirect off;

    # disable any limits to avoid HTTP 413 for large image uploads
    client_max_body_size 0;

    # required to avoid HTTP 411: see Issue #1486 (https://github.com/moby/moby/issues/1486)
    chunked_transfer_encoding on;

    location /v2/ {
      # Do not allow connections from docker 1.5 and earlier
      # docker pre-1.6.0 did not properly set the user agent on ping, catch "Go *" user agents
      if ($http_user_agent ~ "^(docker\/1\.(3|4|5(?!\.[0-9]-dev))|Go ).*$" ) {
        return 404;

      proxy_pass                          http://docker-registry;
      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;

Because we would use BasicAuth to authenticate to registry we need to setup SSL so that our login/password is not taken by man in the middle attack.
Currently this nginx config does not support SSL which is required to make our registry accessible from outside.
We would use CertBot to update our nginx configuration to support SSL.

Step 2: CertBot

Install CertBot as described here: https://certbot.eff.org/lets-encrypt/ubuntubionic-nginx

Now you can run command:

certbot --nginx -d registry.example.com

It would modify /etc/nginx/sites-enabled/registry.example.com so it supports SSL.

Step 3: Docker Registry

Official doc is here: https://docs.docker.com/registry/deploying/
The official doc is quite long, and describes lots of options for different use cases

Just connect via ssh to your host, go to /root folder.

mkdir docker-volumes
mkdir docker-volumes/registry/
mkdir docker-volumes/registry/registry
mkdir docker-volumes/registry/auth

Create there such docker-compose.yml file:

version: "3"
    restart: always
    image: 'registry:2'
      - "5000:5000"
      REGISTRY_AUTH: htpasswd
      REGISTRY_AUTH_HTPASSWD_PATH: /auth/htpasswd
      - /root/docker-volumes/registry/registry:/var/lib/registry
      - /root/docker-volumes/registry/auth:/auth

Step 4: Authentication

Then we need to restrict access to registry by login/password, we would use BasicAuth. To do so we need to generate htpasswd file which we would put into registry container. Change testuser/testpassword to your own.

docker run --entrypoint htpasswd registry:2 -Bbn testuser testpassword > ./docker-volumes/registry/auth/htpasswd

Step 5: Starting it all up

Now we can start registry, run:

docker-compose up -d

Also restart nginx so it loads new config for registry.example.com

/etc/init.d/nginx restart

Step 6: Verify

In order to check that we configured everything correctly, go to console on your local machine, which you would use to build images (your laptop probably), and try to login:

docker login registry.example.com

Use credentials from Step 4

Now it should be all up. In upcoming posts I will write how to setup your private CI using Drone.io so it can use your private registry in its builds.

Develop Privately (2 Part Series)

1) How to setup private docker registry for your projects to save money 2) Private CI using private docker registry with Drone.io (almost free)

Posted on Jul 17 '19 by:

fuksito profile

Vitaliy Yanchuk


Started dev 10+ years ago with Perl and JavaScript, switched to Ruby 8 years ago, and love it still. Now really into ReactJS, GraphQL and Automation


markdown guide

Nice article!
Btw, you can do this

mkdir docker-volumes
mkdir docker-volumes/registry/
mkdir docker-volumes/registry/registry

In one single command in bash :)

mkdir -p docker-volumes/registry/registry