Skip to the content.

Setting up your own private Docker registry

If you’re using your own private Docker registry.

First install htpasswd

sudo yum install httpd-tools

Run this on the server where your private docker registry is running:

htpasswd -Bbn andres mypassword > /home/nginx/conf.d/nginx.htpasswd

Use nginx as proxy in front of your private docker registry in order to secure it with SSL and http authentication scheme.

Create a project directory for this private docker registry:

mkdir /home/andres/driver-studios/docker-registry/

Create the following files in the docker-registry directory. Update the sshHost variable in tasks.sh before proceeding.

Then run:

./tasks deploy_production

to deploy your registry to the server.

docker-registry/docker-compose.production.yml:

================================================

version: '3.7'
services:
  registry:
    image: registry:2
    container_name: docker-registry
    ports:
    - "5000:5000"
    networks:
      - web
networks:
  web:
    external: true

docker-registry/tasks.sh

================================================

#!/bin/bash

# Build script to build and push the docker image to the remote repository

# Usage:
# ./task.sh deploy_production

sshHost=andres@dev.driverdigital.com

function main() {
  case $1 in
    deploy_production)
      deploy_production
    ;;
  esac
}

function deploy_production() {
  DIR="docker-registry"
  DIR=/tmp/$DIR
  ssh ${sshHost} "
  if [ ! -d $DIR ]; then
    mkdir -p $DIR
  fi"

  rsync -avz ./docker-compose.production.yml ${sshHost}:$DIR
  ssh ${sshHost} "cd $DIR && docker-compose -f docker-compose.production.yml up -d"
}

main $@

nginx configuration

Next we need to go to your nginx project folder in Visual Studio Code. This is the directory containing your ngninx hosts configurations.

cd /home/andres/driver-studios/nginx-dev

In your nginx project, create a configuration for the domain where your registry will be hosted (see below). Then upload the configuration via:

./build-tools/tasks.sh reload_production

Then generate an authentic SSL certificate from LetsEncrypt via

./build-tools/tasks.sh ssl_production

Then run this to generate the http password:

docker run --rm --entrypoint htpasswd registry:2 -Bbn andres mypassword > conf.d/nginx.htpasswd

Then run ./build-tools/tasks.sh reload_production again to upload the generate htpasswd file.

conf.d/docker-registry-01.driver-studios.com.conf

server {
    listen 80;
    server_name docker-registry-01.driver-studios.com;
    location / {
        return 301 https://$host$request_uri;
    }
    location /.well-known/acme-challenge/ {
        root /var/www/certbot;
    }
}
## Set a variable to help us decide if we need to add the
## 'Docker-Distribution-Api-Version' header.
## The registry always sets this header.
## In the case of nginx performing auth, the header is unset
## since nginx is auth-ing before proxying.
map $upstream_http_docker_distribution_api_version $docker_distribution_api_version {
'' 'registry/2.0';
}

server {
    listen 443 http2 ssl;
    server_name docker-registry-01.driver-studios.com;
    
    access_log /var/log/nginx/docker-registry-01.driver-studios.com.access.log;
    error_log /var/log/nginx/docker-registry-01.driver-studios.com.error.log;

    ssl_certificate /etc/letsencrypt/live/docker-registry-01.driver-studios.com/fullchain.pem;
    ssl_certificate_key /etc/letsencrypt/live/docker-registry-01.driver-studios.com/privkey.pem;
    include /etc/letsencrypt/options-ssl-nginx.conf;
    ssl_dhparam /etc/letsencrypt/ssl-dhparams.pem;

    

    # Insure that UTF-8 encoding is specified in the Content-Type HTTP response
    # header for most of our text responses
    charset_types text/xml text/plain text/vnd.wap.wml application/javascript application/rss+xml text/css;
    charset utf-8;

    # 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 / {
        root   /usr/share/nginx/html;
        index  index.html index.htm;
    }

    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.
      auth_basic "Registry realm";
      auth_basic_user_file /etc/nginx/conf.d/nginx.htpasswd;

      ## If $docker_distribution_api_version is empty, the header is not added.
      ## See the map directive above where this variable is defined.
      add_header 'Docker-Distribution-Api-Version' $docker_distribution_api_version always;


      resolver 127.0.0.11 valid=3600s;

      # ads-web resolves to the ip of the docker container running node app
      set $upstream_channels docker-registry:5000;
      proxy_pass                          http://$upstream_channels;
      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;
    }
}

Finally

Then to login to your registry in order to push or pull images to it

docker login -u=andres --password-stdin docker-registry-01.driver-studios.com

mypassword (Enter Ctrl+D after the “d” to submit the password) Note: Password I’m using for driver-studios registry is “a****”

To push an image from your machine to your private registry:

docker push docker-registry-01.driver-studios.com/test

To pull:

docker pull docker-registry-01.driver-studios.com/test