Learn On CoreOS, Setup Your Own Docker Registry

March 9, 2019

Table of Contents

We all know and love Docker, a platform to create, manage and distribute application containers across multiple machines.
Docker Inc. provides a service to host open source containers to be downloaded (or pulled) like a git repository known as the “Docker Registry.”
Think of it like a GitHub for Docker containers.

But what if you want to host your own registry separated from the public one? Well, Docker Inc. has open sourced their Registry application on GitHub.

This tutorial will take you though the process of setting up a private Docker registry using CoreOS on a new VPS.

CoreOS + Docker

We won’t be spending a ton of time going over exactly what Docker and CoreOS can do, as it’s outside the scope of this tutorial.
In essence, CoreOS is designed for massive server clusters, it’s small, fast and gets regular security updates automatically. Its root file system is also read-only, meaning you must use Docker to run any kind of software that is not included with the base install.

This makes Core OS a perfect host system for Docker!

Pulling and running the latest registry

Docker Inc. has provided the Registry as a top level image, this means that we can pull it down with a simple:

docker pull registry

This can take a few minutes depending on the connection speed.

A plus of being a top level image also means it gets regular support and updates.

Now let’s test out the registry. We can create a new container using the registry image:

docker run -p 5000:5000 -d --name=basic_registry registry

For those who haven’t used Docker too much, the -p flag stands for PORT, meaning we are exposing port 5000 on from the container onto host port 5000.

The -d flag stands for daemon, this will cause the container to run in the background and not print output to the current SSH session, we also want to name this basic test container using the --name option so that we can easily manage it later.

Make sure that your basic registry container is running using docker ps. The output should look similar to:

CONTAINER ID        IMAGE               COMMAND                CREATED             STATUS              PORTS                    NAMES
edfb54e4a8c4        registry:0.8.1      "/bin/sh -c 'exec do   7 seconds ago       Up 6 seconds>5000/tcp   basic_registry

Also, visit http://YOUR_IP:5000 in your web browser, and you should get a message like the following:

"docker-registry server (dev) (v0.8.1)"

Notice the word dev in the parenthesis. This means that the server is currently running the dev config. We’ll look over more configuration soon.

You now have your own (very basic) container registry running! But we’re not done yet.

Maybe you want to keep this private from prying eyes, or maybe store your images on Amazon S3 instead of local storage. Let’s go over the various configuration options in the next section.

Before we move on, let’s kill the test container so that we don’t run into conflicting ports.

docker kill basic_registry

Registry configuration

There are two ways that we can pass configuration to the Docker Registry. One way is by passing environment variables to a new container, and the other is to add a configuration file.

Here’s a few of the common configuration options we’ll be using:

  • loglevel – The minimum amount of information to log to the console. Default is info.
  • standalone – Should this Registry act on its own? (Never queries the public registry.) Default is true.
  • index_endpoint – If not standalone, what other index will we query? Default index.docker.io.
  • cache and cache_lru – Options pertaining to using a Redis cache for small files, we’ll touch on this later.
  • storage – What storage backend should we use for this server? (In this tutorial we’ll be using local).
  • storage_path – If using local storage, what directory should we use to keep files in?

Before we get working with the configuration, we need a base file to work with. The file from the Docker Registry repository on GitHub will work just fine:

wget https://raw.githubusercontent.com/docker/docker-registry/0.8/config/config_sample.yml

The file should save successfully with an output such as:

2014-09-14 14:09:01 (156 MB/s) - 'config_sample.yml' saved [5384/5384]

Great! Now we can modify this file to fit our needs.

The only text editor that comes with Core OS is vim, but don’t worry if you’ve never used it before, this will explain step by step what to edit and how to do it.

vim config_sample.yml

Once you have the file open, hit the I and the bottom right corner should display: -- INSERT -- for insert mode. Scroll all the way to the bottom of the file using your arrow keys, you should see a section labeled prod.

We’ll be changing the two lines, the changes are below

    <<: *local
    storage_path: _env:STORAGE_PATH:/data

What we’ve done is change the prod configuration to derive from the local section instead of the s3 section. Then we overwrote the storage_path to use the path /data inside the new container.

Once you’ve confirmed that all the changes are correct, hit ESC to exit insert mode and type :wq (this means write the changes to the file, and quit vim.)

Now let’s rename the file to just config.yml

mv config_sample.yml config.yml

Redis caching (optional)

If you’d like to use redis to speed up your container registry, it’s as simple as pulling a new container from the public registry and adding a few more lines of configuration.

First, pull the top level image of Redis:

docker pull redis

Once the image is pulled successfully, we can run it and name it just like we did with the test registry:

docker run -d --name registry-redis redis

Because redis is in memory, we don’t need to do any configuration for it, as we will link it to the registry container in later steps.

Once again, ensure it’s running by using docker ps:

CONTAINER ID        IMAGE               COMMAND             CREATED             STATUS              PORTS               NAMES
54f65641eccf        redis:2.8           "redis-server"      2 seconds ago       Up 1 seconds        6379/tcp            registry-redis

Now re-open the config.yml in vim and enter insert mode just like the first time we edited it.

Add the following lines below the prod section, being sure to indent properly. This time we’re only adding cache and cache_lru.

    host: _env:REDIS_PORT_6379_TCP_ADDR
    port: _env:REDIS_PORT_6379_TCP_PORT
    db: 0
    host: _env:REDIS_PORT_6379_TCP_ADDR
    port: _env:REDIS_PORT_6379_TCP_PORT
    db: 1

The environment variables REDIS_PORT_6379_TCP_ADDR and REDIS_PORT_6379_TCP_PORT are passed to the registry container upon linkage with the Redis container.

With that, you now have setup a Redis container that will work hand-in-hand with your registry container. Now onto building the registry!

Building the container

We have all the configuration set and ready, now we need to build the actual registry container.

Fire up vim Dockerfile to create a new Dockerfile. Enter insert mode and follow the edits below.

FROM registry:latest
# Add the custom configuration file we made 
ADD ./config.yml /docker-registry/config/config.yml
# Create the data directory
RUN mkdir /data
# Set the configuration file to config.yml
env DOCKER_REGISTRY_CONFIG /docker-registry/config/config.yml
# Make sure we use the prod configuration settings 

What we’ve done above is essentially extend the registry image so it will use our configuration file and settings. A Dockerfile is a set of build instructions for Docker to read and build. If you’d like to learn more on Dockerfiles and their syntax, take a look at the official Docker site documentation.

Next we need to build the container for use.

docker build -t my_registry .
Sending build context to Docker daemon 13.82 kB
Sending build context to Docker daemon 
Step 0 : FROM registry
 ---> e42d15ec8417
Step 1 : ADD ./config.yml /docker-registry/config/config.yml
 ---> 4339f026d459
Removing intermediate container 2d5138fbcd34
Step 2 : RUN mkdir /data
 ---> Running in a090f0bdbfd1
 ---> 8eb27ba6e12a
Removing intermediate container a090f0bdbfd1
Step 3 : env DOCKER_REGISTRY_CONFIG /docker-registry/config/config.yml
 ---> Running in 565b5bfb2b22
 ---> 914462e46dc0
Removing intermediate container 565b5bfb2b22
Step 4 : env SETTINGS_FLAVOR prod
 ---> Running in 31a92847b851
 ---> b5949575c374
Removing intermediate container 31a92847b851
Successfully built b5949575c374

Now we are ready to run!

Let’s make a directory on our host system to mount in the container as the /data volume.

mkdir registry-data

Now we can spin up a new container. If you plan on using the Redis cache, use the 2nd command below.

# For a non-Redis cache registry
docker run -d -p 5000:5000 -v /home/core/registry-data:/data --name=private_reg my_registry
# For a Redis cached registry (Must have followed Redis Caching section above)
docker run -d -p 5000:5000 -v /home/core/registry-data:/data --name=private_reg --link registry-redis:redis my_registry

To ensure that your server is running correctly, visit http://YOUR_IP:5000. You will see the following message:

"docker-registry server (prod) (v0.8.1)"

Notice the (prod) meaning our configuration changes were successful!

Configure your local Docker client

Now that we have our own running registry, we want the Docker client on our local machines to start using it. Usually you would use the command: docker login, but for our use, we need to add one more argument to the login command:

docker login YOUR_IP:5000

Enter a username and password (think of this like making a new account) and ignore the message that states you must activate it.

Next, let’s pull a stock image, and push it up to our own repository.

# Pull the busybox image from the public registry
docker pull busybox
# Tag it with our IP/URL
docker tag busybox YOUR_IP:5000/busybox
# Push it to our newly made registry
docker push YOUR_IP:5000/busybox

If everything pushes correctly the final message should be along the lines of:

Pushing tag for rev [a9eb17255234] on 

Congratulations! You have setup your very own docker repository.

What’s next?

Here’s some ideas on how to improve your new private registry:

  • Reverse proxy using Nginx or Apache to place additional security in front of it, like simple HTTP auth.
  • Get a domain for your server and setup it up so you may access your registry with something like: registry.mysite.com
  • Purchase (or self-sign) an SSL certificate to add even more protection if your containers contain sensitive information.

Need help?

Do you need help setting up this on your own service?
Please contact us and we’ll provide you the best possible quote!