Set up a private Docker registry

Having mastered deploying WordPress sites with Docker and Compose, I set up a blog for my lovely wife on one of our many demonstration/prototyping domains. Once her site was configured to her liking, I purchased a dedicated domain with the intent of moving her site over. This is simple enough, but I wanted a more comprehensive solution. One that would allow me to backup the changes she makes to her site (and database) periodically. As well, she wanted to establish a basic WordPress image from which she could launch new projects without having to go through the whole set up and configuration rigamarole over and over again.

With all that in mind, the following outlines how I set up our private Docker registery. The procedure was adapted and condensed from here.

Get a certificate for your domain

Always with the certificates!

I like to use startssl.com because they’re free. startssl.com provides an intermediate certificate, so remember to chain them. Alternatively, you can sign your own certificates.

However you decide to obtain your certificates, install them somewhere on your domain registry’s server. E.g.,

1
2
cd ~
mkdir certs

You should have two certificates named something like this:

  • ~/certs/myregistrydomain.com.crt
  • ~/certs/myregistrydomain.com.key

Restrict access with password

Make an auth/ directory:

1
2
cd ~
mkdir auth

Then set a user named someguy whose password is someAlphaNum3r1cPassword (or whatever):

1
docker run --entrypoint htpasswd registry:2 -Bbn someguy someAlphaNum3r1cPassword > auth/htpasswd

Note: at the time of writing, the password must be alphanumeric. Special symbols do not work. Assuming all else is configured correctly, using non-alphanumerics will result in this error:

1
basic auth attempt to https://myregistrydomain.com:5000/v2/ realm "Registry Realm" failed with status: 401 Unauthorized

I’m not sure if this is by oversight or by design. Either way, all this stuff is pretty wild and woolly and will likely change as the Docker product continues to evolve.

Set up local file system storage

1
2
cd ~
docker run -d -p 5000:5000 --restart=always --name registry -v `pwd`/data:/var/lib/registry registry:2

Configure Compose

Create a directory in which to write your docker-compose.yml file:

1
2
3
4
cd ~
mkdir registry
cd registry
vim docker-compose.yml

Copy and save the following:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
registry:
restart: always
image: registry:2
ports:
- 5000:5000
environment:
REGISTRY_HTTP_SECRET: SomePseudoRandomString
REGISTRY_HTTP_TLS_CERTIFICATE: /certs/myregistrydomain.com.crt
REGISTRY_HTTP_TLS_KEY: /certs/myregistrydomain.com.key
REGISTRY_STORAGE_FILESYSTEM_ROOTDIRECTORY: /var/lib/registry
REGISTRY_AUTH: htpasswd
REGISTRY_AUTH_HTPASSWD_PATH: /auth/htpasswd
REGISTRY_AUTH_HTPASSWD_REALM: Registry Realm
volumes:
- /path/to/data:/var/lib/registry
- /path/to/certs:/certs
- /path/to/auth:/auth

Change the /path/to/ directory to point to your certs and auth directories. This will be your account’s home directory, if following the steps above to the letter (cf., cd ~).

Also, execute the following to generate a pseudo-random string for the REGISTRY_HTTP_SECRET option:

1
cat /dev/urandom | tr -dc 'a-zA-Z0-9' | head -c 32

Start up the registry

From the ~/registry directory:

1
docker-compose up -d

Commit the images

I have two Docker images that need committing. These are hosted on a server different than my Docker registry server.

Obtain their container IDs:

1
docker ps

Supposing output similar to this:

1
2
3
CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES
c8cb170d95bb wordpress "/entrypoint.sh apach" 15 minutes ago Up 10 minutes 80/tcp examplecom_wordpress
69b59e19aadc mysql:5.7 "/entrypoint.sh mysql" 53 minutes ago Up 10 minutes 3306/tcp examplecom_mysql

First, I commit the WordPress image:

1
docker commit -p c8cb170d95bb somenewdomaincom_wordpress

Now I have a snapshot of the WordPress container saved as _somenewdomaincomwordpress.

Now commit the associated MySQL container:

1
docker commit -p 69b59e19aadc somenewdomaincom_mysql

Push the images

Authentication has been set up, so log in first:

1
docker login myregistrydomain.com:5000

Then push:

1
2
docker push myregistrydomain.com:5000/somenewdomaincom_wordpress
docker push myregistrydomain.com:5000/somenewdomaincom_mysql

Tag the images

Having committed the images, I now have two snapshots that need tagging. First, I tag the WordPress image:

1
docker tag examplecom_wordpress myregistrydomain.com:5000/somenewdomaincom_wordpress

Now I tag the associated MySQL image:

1
docker tag examplecom_mysql myregistrydomain.com:5000/somenewdomaincom_mysql

Push the images

Authentication has been set up, so log in first:

1
docker login myregistrydomain.com:5000

Then push:

1
2
docker push myregistrydomain.com:5000/examplecom_wordpress
docker push myregistrydomain.com:5000/examplecom_mysql

Redeploy (with Compose)

The whole purpose of this exercise was to move my wife’s site from one domain to another. We use an Nginx proxy to let us host a bunch of different WordPress sites on a single machine. Supposing that configuration with domain-appropriate security certificates pre-installed, I can use Compose to pull images from my new Docker registry.

First, create a directory on the host machine:

1
2
3
4
cd ~
mkdir somenewdomain.com
cd somenewdomain.com
vim docker-compose.yml

Copy and save the following:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
wordpress:
image: myregistrydomain.com:5000/examplecom_wordpress
links:
- mysql
environment:
- WORDPRESS_DB_PASSWORD=secretp@ssword
- VIRTUAL_HOST=somenewdomain.com
expose:
- 80
mysql:
image: myregistrydomain.com:5000/examplecom_mysql
environment:
- MYSQL_ROOT_PASSWORD=secretp@ssword
- MYSQL_DATABASE=wordpress

Fire ‘er up!

1
docker-compose up -d