Private Docker Registry in Ubuntu Server 16.04

Posted: October 23, 2016 in docker, Linux

The Registry is server side application that stores and lets you distribute Docker images.

In my previous post when i created  Docker Web Server (docker run –name web –hostname web -m 2g -p 80:80 -P -i -t ubuntu /bin/bash) ubuntu image is pulled from online repository,it’s perfectly OK for test purpose,but it’s not appropriate when we are dealing with Docker containers in working environment.Not only bandwith would be the issue,but also security.In this post we’ll create our own,Private Docker Registry and will configure authentication.

Because we’ll use Nginx (HTTP and reverse proxy server),for configuring security,we need to install appache2-utils package,which will generate passwords for Nginx.Also we need to install docker-compose (tool for defining and running multi-container Docker applications) and curl (a tool to transfer data from or to a server, using one of the supported protocols (DICT, FILE, FTP, FTPS, GOPHER, HTTP, HTTPS, IMAP, IMAPS, LDAP, LDAPS, POP3, POP3S, RTMP, RTSP, SCP, SFTP, SMB, SMBS, SMTP, SMTPS, TELNET and TFTP)) packages

apt-get install -y docker-compose apache2-utils curl

We need a folder for our containers and it’s volumes:

mkdir /docker-registry
mkdir  /docker-registry/data
mkdir /docker-registry/nginx
chown root:root /docker-registry
cd /docker-registry

Create docker-compose.yml file (for defining docker container properties)

vi docker-compose.yml:
  image: "nginx:1.9"
    - 443:443
    - registry:registry
    - /docker-registry/nginx/:/etc/nginx/conf.d
  image: registry:2
    - /docker-registry/data:/data

First registry container will be created,it’ll listen on port 5000,REGISTRY_STORAGE_FILESYSTEM_ROOTDIRECTORY variable instructs the registry docker (derived from registry:2) image to store data to /data volume (mapped from /docker-registry/data).

Then nginx container will be made next,it will know how to reach registry container thanks to –link directive (registry container IP will be mapped in nginx /etc/hosts)

Now start containers:

docker-compose up

If everything gone fine,we should see picture below:


Stop the registry (CTRL+C)

Now would be good time to convert docker-compose into service:

Create docker-registry.service file in /etc/systemd/system folder:

nano /etc/systemd/system/docker-registry.service

Description=Starting docker registry

Environment= MY_ENVIRONMENT_VAR = /docker-registry/docker-compose.yml
ExecStart=/usr/bin/docker-compose up


Test if it works:

service docker-registry start
root@ubuntu:~/docker-registry# docker ps


From now on,instead docker-compose up and terminating process,we’ll use service docker-registry start/stop/restart command

Now we need to configure nginx server:

Open new terminal:

vi  /docker-registry/nginx/registry.conf

upstream docker-registry {
  server registry:5000;

server {
  listen 443;

  # SSL
  # ssl on;
  # ssl_certificate /etc/nginx/conf.d/domain.crt;
  # ssl_certificate_key /etc/nginx/conf.d/domain.key;

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

  # required to avoid HTTP 411: see Issue #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;

    # To add basic authentication to v2 use auth_basic setting plus add_header
    # auth_basic "registry.localhost";
    # auth_basic_user_file /etc/nginx/conf.d/registry.password;
    # add_header 'Docker-Distribution-Api-Version' 'registry/2.0' always;

    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;

Test if we can access to Docker Registry  and to Nginx:

service docker-registry restart
curl http://localhost:5000/v2/

We should get output below:



Now we need to set up authentication,create user:

cd /docker-registry/nginx
htpasswd -c registry.password mydocker
New password:
Re-type new password:
Adding password for user mydocker

Open again registry.conf:

vi /docker-registry/nginx/registry.conf

Uncomment following lines to configure Nginx for basic HTTP authnrtication :

auth_basic "registry.localhost"; auth_basic_user_file /etc/nginx/conf.d/registry.password; add_header 'Docker-Distribution-Api-Version' 'registry/2.0' always;

Test again:

service docker-registry restart
curl http://localhost:443/v2/
root@ubuntu:~/docker-registry/nginx# curl http://localhost:5043/v2/401 Authorization Required
401 Authorization Required

Test with username/password,we should get same output as earlier:

curl http://mydocker:123456@localhost:443/v2/

Setting SSL authentication:

Open,againg registry.conf

vi  /docker-registry/nginx/registry.conf

uncomment lines below and set domain name:

upstream docker-registry {
server registry:5000;

server {
listen 443;

ssl on;
ssl_certificate /etc/nginx/conf.d/domain.crt;
ssl_certificate_key /etc/nginx/conf.d/domain.key;

Creating our own Certification Authority

cd /docker-registry/nginx

Generate a new root key:

openssl genrsa -out dockerCA.key 2048

Generate a root certificate (enter for Common Name,whatever You want for other fileds):

openssl req -x509 -new -nodes -key dockerCA.key -days 10000 -out dockerCA.crt

Generate server key (this is the file referenced by ssl_certificate_key in Nginx):

openssl genrsa -out domain.key 2048

Request a new certificate (again,enter for Common Name,don’t enter a password):

openssl req -new -key domain.key -out

Sign a certificate request:

openssl x509 -req -in -CA dockerCA.crt -CAkey dockerCA.key -CAcreateserial -out domain.crt -days 10000

Because we created our own CA,by default it wouldn’t be verified by any other CA’s,so we need to “force” computers which will be connecting to our Docker Private Registry.

Do this on our Docker Registry Server (for testing purposes):

cd /docker-registry/nginx
cp dockerCA.crt /usr/local/share/ca-certificates/

By copying root certificate to /usr/local/share/ca-certificates folder we told hosts to “trust” our Certification Authority

update-ca-certificates && service docker restart && service docker-registry restart
#output should be

On client machine,(if  dosen’t exist),create folder /usr/local/share/ca-certificates/,of course,install docker if it’s not installed already.

Then copy dockerCA.crt to client machine

scp dockerCA.crt ja@
ja@'s password:
dockerCA.crt 100% 1302 1.3KB/s 00:00

On client,update CA certificate

update-ca-certificates && service docker restart
#test login to fresh created repository:
docker login
Username: mydocker
Login Succeeded

On the client create test container

docker run -it ubuntu
#re-tag images DOMAIN-NAME/NEW-TAG
docker tag ubuntu
#push image to repository:
docker push


Now remove image from host  and pull it from repository

docker rmi -f
docker pull


That’s it !!!,we have now operational repository,in case of any errors please refer to docker logs:

journalctl -u docker for docker logs in the systemd journal
journalctl | grep docker for system logs that mention docker

  1. Sergi cañas says:

    Very useful post working perfect with ubuntu 16.04. But I’m trying to download the dockerCA.crt to a uselss Mac laptop.. and i don’t know where i sould place because the folder /usr/local/share/ca-certificate doesnt exists in this shitty laptop, do you know where i should place the certificate on a mac then?



    • DrEmmettBrowni says:

      Hi Sergi, you can’t find a directory called “ca-certificate” on your mac. This is all about design.
      any way to make it run, please grab a valid one of copy your certificate onto your mac then double click on’ll request authorization from you. if you confirm then you’ll be able to see this certification record in your keychain access list.You can change trust level of your certificate. There is one thing to do. You have to restart your docker on your mac.Voila! if it works without any problem so you will able to reach your private docker service through your terminal on your mac perfectly.


Leave a Reply

Fill in your details below or click an icon to log in: Logo

You are commenting using your account. Log Out /  Change )

Google photo

You are commenting using your Google account. Log Out /  Change )

Twitter picture

You are commenting using your Twitter account. Log Out /  Change )

Facebook photo

You are commenting using your Facebook account. Log Out /  Change )

Connecting to %s