---
title: "How to Create a Private Docker Registry on Ubuntu Linux"
description: "Learn how to create and host your own private Docker registry in this how-to tutorial!"
canonical: "https://adamtheautomator.com/private-docker-registry/"
---

# How to Create a Private Docker Registry on Ubuntu Linux

> Learn how to create and host your own private Docker registry in this how-to tutorial!

Source: https://adamtheautomator.com/private-docker-registry/

---

ATA Learning

Tap to hide

[

ATA Learning

](/)

*   [Home](/)
*   [Tutorials](/tutorials/)
*   [Instructors](/author/)
*   [Advertising](/advertising/)
*   [Recommended Resources](/resources/)
*   [About Adam](/about-adam/)

Search for:  

*   [](https://twitter.com/adbertram)
*   [](https://github.com/Adam-the-Automator)
*   [](https://www.linkedin.com/company/adam-the-automator-llc)
*   [](/feed/)

![How to Create a Private Docker Registry on Ubuntu Linux](https://adamtheautomator.com/wp-content/uploads/2022/02/How-to-Create-a-Private-Docker-Registry-on-Ubuntu-Linux.jpg)

# How to Create a Private Docker Registry on Ubuntu Linux

[![](https://secure.gravatar.com/avatar/ff757e22f03c8a84ce8a2b8f87c53481ba627afec8bd1c8fda5c576097ea027c?s=192&d=mm&r=g)Joseph Eshiett](https://adamtheautomator.com/author/joseph-eshiett/)8 February 20229 min. read

Categories: [IT Ops](/category/it-ops/), [Uncategorized](/category/uncategorized/)

Tags:[Docker](/tag/docker/)[Linux](/tag/linux/)[Ubuntu Linux](/tag/ubuntu-linux/)

Table of Contents

*   [Prerequisites](#prerequisites)
*   [Setting up the Docker Registry](#setting-up-the-docker-registry)
*   [Setting up NGINX](#setting-up-nginx)
*   [Configuring the NGINX File Upload Size](#configuring-the-nginx-file-upload-size)
*   [Setting up SSL](#setting-up-ssl)
*   [Setting up HTTP Authentication for Docker Registry](#setting-up-http-authentication-for-docker-registry)
*   [Pushing Image to Private Docker Repository](#pushing-image-to-private-docker-repository)
*   [Pulling Image from Private Docker Repository](#pulling-image-from-private-docker-repository)
*   [Conclusion](#conclusion)

Are you looking for a place to safely store your custom-built Docker images for your personal projects or use them within your organization? Or maybe a centralized system to manage your Continuous Integration and Deployment processes? A place where you can pull and upload your custom-built images for your application at will and improve the deployment rate?

Not a reader? Watch this related video tutorial!

**_Not seeing the video? Make sure your ad blocker is disabled._**

Look no further! A private Docker registry makes all of that possible! This step-by-step tutorial will guide you on building your own private Docker registry. Dive in to get started!

## **Prerequisites**

To follow along with this tutorial, be sure to have the following:

*   Two **[Ubuntu 20.04 LTS](https://ubuntu.com/download/desktop)** devices. One will host the Docker registry, and the other will act as a client machine to send requests to your Docker registry.

Related:[How to Install Ubuntu 20.04 \[Step-by-Step\]](https://adamtheautomator.com/install-ubuntu/)

*   **NGINX** is required only in the host machine for setting up SSL encryption and HTTP authentication.
*   A registered domain name. NGINX will route traffic to your registered domain name to your Docker registry running in a container.
*   You need Docker in both the host and client machine for this tutorial. The default install is enough.

Related:[How to Install and Use Docker on Ubuntu (In the Real World)](https://adamtheautomator.com/docker-ubuntu/)

*   **Docker-compose** in the Docker registry host machine to set up and start up the components for your Docker registry. _**Related: [Everything You Need to Know about Using Docker Compose](https://adamtheautomator.com/docker-compose-tutorial/)**_

Related:[Everything You Need to Know about Using Docker Compose](https://adamtheautomator.com/docker-compose-tutorial/)

## **Setting up the Docker Registry**

The first step you need to take is to set up the Docker [registry](https://docs.docker.com/registry/introduction/) on the host device, provided as a free image on [Docker Hub](https://hub.docker.com/_/registry).

Instead of being restricted to issuing Docker commands to accomplish this task, you will create a `docker-compose.yml` file. The file uses the [docker-compose file format](https://docs.docker.com/compose/compose-file/) and can set up the components required for the Docker registry with relative ease.

To follow along, open your favorite SSH client, and connect to the device that will be the registry server.

Related:[A Windows Guy in a Linux World: Setting up SSH in Linux](https://adamtheautomator.com/ssh-command-in-linux/)

1\. In the _/home_ directory, create a directory called _docker-registry_ with the `mkdir` command:

```bash
# Creating working directory
mkdir docker-registry
```

2\. Navigate to the _docker-registry_ directory:

```bash
# Navigate to the working directory
cd ~/docker-registry
```

3\. Inside of the _docker-registry_ directory, create a subdirectory called _data_. Inside the _data_ directory is where the Docker registry will store the Docker images. The _data_ directory acts as a file system for the Docker registry to preserve the Docker images.

```bash
# Create filesystem to persist data
mkdir data
```

4\. Inside of the _docker-registry_ directory, create a _docker-compose.yml_ file:

```bash
# Create the docker-compose file
nano docker-compose.yml 
```

You will find configurations for setting the registry in the newly created file. Among the settings, you will notice:

*   Sets the `registry` service with the `registry:latest` image with the `latest` tag.
*   Sets the restart policy for the `registry` service to `always`. Provided that the Docker engine is running, the `registry` service will always restart when stopped.
*   The `ports` section maps the port `5000` in the registry container to port `5000` on the host machine.
*   The `registry` service in the `environment` section sets the environment variable `REGISTRY_STORAGE_FILESYSTEM_ROOTDIRECTORY` to the _data_ directory created earlier.
*   Docker requires a volume to persist the Docker images in the `registry` container. You will add the `volumes` object to map the /_data_ directory on the host machine to the `/data` directory in the container.

The mapping will store the data on the host machine’s file system instead of inside the container. Since Docker now stores the data on the host machine, it can be backed up, migrated, encrypted, or replaced.

Below you can see the full YAML configuration for the components needed to start and run the Docker registry. Copy the YAML code below and paste it into the _docker-compose.yml_ file you created and save it with `CTRL + S` and press `CTRL + X` to exit:

```yaml
version: '3.3' 
services:
  registry:
    image: registry:latest 
		restart: always
    ports:
    - "5000:5000"
    environment:
      REGISTRY_STORAGE_FILESYSTEM_ROOTDIRECTORY: /data 
    volumes:
      - ./data:/data 
```

> _Docker-compose runs multiple containers as a single application. The `docker-compose.yml` file configures these multiple containers as part of services._

5\. Now run the docker-compose file and create and start the Docker registry:

```bash
# Start docker registry application
sudo docker-compose up
```

In the command output below, you can see the Docker registry running. To close the application, press `CTRL + C`.

![Docker registry running, press CTRL + C to close it](https://adamtheautomator.com/wp-content/uploads/2022/02/image-83.png)

Docker registry running, press `CTRL + C` to close it

## Setting up NGINX

Now that you have the Docker registry set up, it is time to set up NGINX on the host machine. NGINX will forward traffic from client devices to the Docker registry. For this tutorial, you will forward the traffic via a domain name. Read on to tackle this step.

Related:[How To Serve NGINX Subdomains or Multiple Domains](https://adamtheautomator.com/nginx-subdomain/)

Initially, you will have to set up NGINX port forwarding for your domain via the _/etc/nginx/sites-available/your\_domain\_name_ file.

1\. Create the file with the following command:

```bash
# Creating configuration for your domain
sudo nano /etc/nginx/sites-available/your_domain_name 
```

> _You will find many references to_ your\_domain\_name _in the following sections_. _This string is a placeholder. Do not forget to replace it with a domain name you own when trying the commands yourself. In the examples_ [registry.joeshiett.xyz](http://registry.joeshiett.xyz) _is the selected domain name._

2\. Copy the following NGINX configuration and add it to the _your\_domain\_name_ file you created:

```yaml
server {
	listen 80;
  # Replace your_domain_name with your domain name
  server_name your_domain_name;

    location / {
      # 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://localhost:5000;
      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;
    }
}
```

3\. Port forwarding isn’t complete yet. You need to attach a symbolic link first. You will link _/etc/nginx/sites-available/your\_domain\_name_ configuration file to _/etc/nginx/sites-enabled/._ The full command follows:

```bash
cd /etc/nginx/sites-enabled/ && sudo ln -s /etc/nginx/sites-available/your_domain_name .
```

4\. Restart the NGINX service after creating the symbolic link:

```bash
# Restarting Nginx service
sudo systemctl restart nginx 
```

Open the browser on your client device and navigate to `https://your_domain_name/v2/` to access the `v2` endpoint, the Docker HTTP API.

The image below shows the expected output from the host terminal after navigating to the link. The image also shows that the browser sent a `GET` request to the Docker HTTP API via the `v2` endpoint.

![GET request sent to /v2 endpoint ](https://adamtheautomator.com/wp-content/uploads/2022/02/image-84.png)

GET request sent to /v2 endpoint

After navigating to the `v2` endpoint via the link, an empty JSON object – `{}` – will be displayed on the browser’s window.

### Configuring the NGINX File Upload Size

NGINX’s default upload size limit is `1m`, where `m` stands for megabyte. The default limit is not nearly enough for uploading Docker images to your registry. To change this value, edit _nginx.conf,_ so NGINX accepts large file uploads.

1\. Once again, log into the host machine.

2\. Edit the _/etc/nginx/nginx.conf_ file:

3\. Under the `http` section in `nginx.conf` file, add the following line to increase the upload size to accommodate 8,192 megabytes, and save the file:

```powershell
client_max_body_size 8192m;
```

The configuration will look somewhat like this:

![](https://adamtheautomator.com/wp-content/uploads/2022/02/image-85.png)

The modified _nginx.conf_ file, which allows larger file uploads

## Setting up SSL

An SSL certificate is required to secure the connection to the Docker registry. To add an SSL certificate, you need to install [Certbot](https://certbot.eff.org/) on the registry host.

You can install Certbot with the [snap package manager](https://snapcraft.io/docs/installing-snap-on-ubuntu). Snap package manager already comes pre-installed in Ubuntu 20.04.

1\. To install Certbot, run the following command on your host machine:

```bash
# Install certbot using the snap package manager
sudo snap install --classic certbot
```

2\. Add Certbot to [PATH](http://www.linfo.org/path_env_var.html#:~:text=PATH%20is%20an%20environmental%20variable,commands%20issued%20by%20a%20user.) so Certbot commands can work by creating a symbolic link:

```bash
# Attaching certbot to PATH
sudo ln -s /snap/bin/certbot /usr/bin/certbot
```

3\. Run this command to get a certificate and have Certbot edit your NGINX configuration automatically, and also enable HTTPS access for your domain:

```bash
# Creating a certificate and enabling HTTPS access
sudo certbot --nginx
```

Certbot will modify the _your\_domain\_name_ configuration file at the _/etc/nginx/sites-enabled/_ directory and apply an SSL certificate to your domain name.

The image below shows what the final configuration for your _/etc/nginx/sites-enabled/your\_domain\_name_ file will look like after Certbot has applied the certificates.

![Certbot successfully applied the certificates.](https://adamtheautomator.com/wp-content/uploads/2022/02/image-86.png)

Certbot successfully applied the certificates.

If you browse to [`https://your_domain_name/`](https://your_domain_name/v2/), you will find it is secure with the certificate from Certbot.

## **Setting up HTTP Authentication for Docker Registry**

It is always a good idea to have good security practices. So you will secure your Docker registry and restrict access to only you and some other users you may wish to add. You will use HTTP authentication, provided by **[HTTP Basic Auth](https://www.ibm.com/docs/en/cics-ts/5.4?topic=concepts-http-basic-authentication)**.

1\. HTTP authentication requires a _htpasswd_ file with your username and password. To create the _htpasswd_ authentication, you will need to install the `apache2-utils` package:

```powershell
# Installing apache2-utils package
sudo apt-get install apache2-utils
```

2\. After installing `apache2-utils` create a _registry.credentials_ file in the _/etc/nginx_ directory with the `httpasswd` command. Add a preferred username by replacing `your_username` in the command below. Next, add a password of your preference when prompted. The full command and its output are below.

```bash
# Creating to creating auth credentials
sudo htpasswd -c /etc/nginx/registry.credentials your_username
```

![Adding password for example user joeshiett](https://adamtheautomator.com/wp-content/uploads/2022/02/image-87.png)

Adding password for example user `joeshiett`

3\. After creating the _registry.credentials_ file, navigate to your NGINX configuration file for your Docker registry at _/etc/nginx/sites-available/your\_domain\_name_. Add the following text to the NGINX configuration file in the `server` section:

```bash
server {
	...
	...
	location / {
			...
			auth_basic            "Basic Auth";
      auth_basic_user_file  "/etc/nginx/registry.credentials";
			...
	}
}
```

4\. Restart NGINX once again and re-apply the configurations with the following command:

```bash
# Restarting nginx
sudo systemctl restart nginx
```

The image below shows that when you try to access [**https://your\_domain\_name/**](https://your_domain_name/v2/) on your browser, you will be prompted to input your username and password. For this tutorial, the Docker registry runs at a subdomain **registry.joeshiett.xyz**, with SSL enabled.

![HTTP authentication set up successfully!](https://adamtheautomator.com/wp-content/uploads/2022/02/image-88.png)

HTTP authentication set up successfully!

## **Pushing Image to Private Docker Repository**

Now that your Docker registry is running and capable of accepting larger file uploads, you will push an image to it. To push an image to your Docker registry, you must access your client machine.

> _If you do not have a Docker image stored locally on your client machine, you can pull a free one from Docker Hub._

1\. Run the following command to pull an Alpine Linux image from the Docker hub, set the name of the image as `test-image`, and run the container in the interactive shell:

```bash
# Pull and run Alpine container
sudo docker run --name test-image -it alpine:latest /bin/sh
```

2\. Once inside the Alpine container, create a file called _TEST_. This file will then be the confirmation the image you will pull from your Docker registry is the one you are modifying now:

```bash
# create a file called TEST
touch /TEST
```

3\. Exit out of the interactive shell by typing `exit` on your terminal.

4\. Now create a Docker image from the Alpine Docker container named `test-image`, you just customized:

```bash
# Creating Docker image
sudo docker commit test-image your_domain_name/test-image:latest
```

The Alpine image that you pulled and customized earlier is now available locally with a repository called `your_domain_name/test-image` and tag `latest`.

5\. To push your newly created image to your Docker registry, log in to your Docker registry with the command below:

```bash
# Login to Docker registry
sudo docker login https://your_domain_name 
```

You will be prompted to input your username and password that you set up in the previous section. The output will be:

```bash
...
Login Succeeded
...
```

6\. After logging in, push the tagged image to your Docker repository: `# Pushing docker image sudo docker push your_domain_name/test-image:latest`

```bash
# Pushing docker image
sudo docker push your_domain_name/test-image:latest
```

The output of the command will look like this:

![Successfully pushed tagged image](https://adamtheautomator.com/wp-content/uploads/2022/02/image-89.png)

Successfully pushed tagged image

## **Pulling Image from Private Docker Repository**

Now that you have successfully pushed your Docker image to your private Docker repository, it is time to pull the image you just pushed.

1\. First off, you need to login into your private Docker repository: `# Login to Docker registry sudo docker login https://your_domain_name`

```bash
# Login to Docker registry
sudo docker login https://your_domain_name 
```

2\. After logging in, you will pull the Docker image created earlier:

```adoc
# Pull Docker image from Docker registry
sudo docker pull your_domain_name/test-image
```

From the image below, you can see that Docker pulls the `test-image` successfully.

![Docker image pulled successfully](https://adamtheautomator.com/wp-content/uploads/2022/02/image-90.png)

Docker image pulled successfully

3\. Time to run an interactive shell:

```bash
sudo docker run -it your_domain_name/test-image /bin/sh
```

4\. While in the interactive shell, run the following command:

```bash
ls
```

From the image below, you can see that the _TEST_ file created earlier is inside the container.

![TEST file available in the alpine container](https://adamtheautomator.com/wp-content/uploads/2022/02/image-91.png)

TEST file available in the alpine container

Now you have successfully tested your Docker registry and are good to go!

## **Conclusion**

In this tutorial, you created your very own private Docker registry. You installed the necessary prerequisites; set up SSL and HTTP authentication. And then you finally pushed and pulled an image from your registry.

So, what packages do you plan to push to your registry?

Share this article

[Share on X](https://twitter.com/intent/tweet?url=https%3A%2F%2Fadamtheautomator.com%2Fprivate-docker-registry%2F&text=How%20to%20Create%20a%20Private%20Docker%20Registry%20on%20Ubuntu%20Linux)[Share on Facebook](https://www.facebook.com/sharer/sharer.php?u=https%3A%2F%2Fadamtheautomator.com%2Fprivate-docker-registry%2F)[Share on LinkedIn](https://www.linkedin.com/sharing/share-offsite/?url=https%3A%2F%2Fadamtheautomator.com%2Fprivate-docker-registry%2F)

## Related Posts

![](https://adamtheautomator.com/wp-content/uploads/2022/02/Network-Troubleshooting-from-the-Command-Line-with-TShark.jpg)

### [Network Troubleshooting from the Command-Line with TShark](/tshark/)

Learn how to set up TShark on your machine to capture packets for effective network troubleshooting in this step-by-step tutorial!

![](https://adamtheautomator.com/wp-content/uploads/2022/01/How-To-Set-Up-WireGuard-VPN-on-Linux.jpg)

### [How To Set Up WireGuard VPN on Linux](/wireguard-vpn/)

Learn how to set up WireGuard VPN on Linux to securely connect and access your network, in this step-by-step tutorial!

![](https://adamtheautomator.com/wp-content/uploads/2024/03/openldap-4.jpg)

### [How to Install and Configure an OpenLDAP Ubuntu Server](/openldap/)

Unlock the power of OpenLDAP on Ubuntu for centralized user authentication, seamless access control management, and enhanced directory services!

## Categories

*   [IT Ops](/category/it-ops/)
*   [Cloud](/category/cloud/)
*   [DevOps](/category/devops/)
*   [Home Ops](/category/home-ops/)
*   [Information Security](/category/infosec/)
*   [Software Development](/category/software-development/)

## Site

*   [Home](/)
*   [Tutorials](/tutorials/)
*   [Instructors](/author/)
*   [Advertising](/advertising/)
*   [Recommended Resources](/resources/)
*   [About Adam](/about-adam/)

Copyright 2026© ATA Learning | [Privacy Policy](/privacy/)
