Self-hosted GitLab container registry
Introduction
When we expand homelab infrastructure we often get to the point when custom solutions are necessary. A common example is creating custom Docker images to run containers. To develop images on one machine and then deploy them to others container registry is needed. In this post I will show how to run self-hosted container registry using GitLab.
OS used: Debian 12
Software used: GitLab Community Edition 17.11.1
Source
- Official GitLab container registry administration documentation.
- Official GitLab container registry documentation.
- Official GitLab Authenticate with the container registry documentation.
Initial requirements
For GitLab container registry you need working GitLab instance. To create GitLab instance you can use instruction from post Installing GitLab as a self-hosted git repository.
Configure container registry
To run container registry under the same domain as GitLab instance perform following actions.
In /etc/gitlab/gitlab.rb set registry URL with port and path to TLC certificate:
$ sudo vim /etc/gitlab/gitlab.rb
(...)
registry_external_url '<https://your_domain_name:5050>'
(...)
registry_nginx['ssl_certificate'] = "/etc/letsencrypt/live/<your_domain_name>/fullchain.pem"
registry_nginx['ssl_certificate_key'] = "/etc/letsencrypt/live/<your_domain_name>/privkey.pem"
(...)
In lines
registry_nginx['ssl_..]you need to enter path to Let's Encrypt TLS certificate and private key obtained using certbot for your GitLab instance.
Reconfigure GitLab:
$ sudo gitlab-ctl reconfigure
Container registry is now active for all projects.
Firewall configuration
Container registry uses port 5050. Open that port on firewall.
Using iptables
Use following command to add rule to iptables:
$ sudo iptables -A INPUT -p tcp --dport 5050 -j ACCEPT && \
sudo iptables -L -v && \
sudo netfilter-persistent save
Using nftables
Add following line to your input chain in nftables config file:
$ sudo vim /etc/nftables.conf
table inet filter {
chain input {
(...)
tcp dport { 5050 } ct state new accept
(...)
}
Reload rules after changing /etc/nftables.conf with command:
$ sudo systemctl restart nftables.service
In order to see firewall ruleset and counters statistics use command:
$ sudo nft list ruleset
Path to stored images
When using the GitLab container registry, images are stored in the following path: /var/opt/gitlab/gitlab-rails/shared/registry.
Test of container registry
Create Personal Access Token
First create GitLab Personal Access Token for your user. You will use this token when logging in to the container registry with Docker. In GitLab:
- Click your avatar (left up corner) >
Preferences>Access tokens> clickAdd new token: - set
Token name - set
Expiration date(If you do not enter an expiry date, the expiry date is automatically set to 365 days later than the current date.) - check
read_registry - check
write_registry - Click
Create personal access token
Authenticate to container registry using Docker
Use created Personal Access Token to authenticate to the container registry using command docker login. Use read command to prevent the token from being saved in the terminal history:
$ read -p "Enter token: " token && \
echo "$token" | docker login <https://your_domain_name.com:5050> -u <username> --password-stdin
Create Docker image
Create Dockerfile in a folder for example /docker-test-image/:
$ mkdir ~/docker-test-image && \
vim ~/docker-test-image/Dockerfile
FROM alpine:latest
LABEL description="Test image"
# Install some packages:
RUN apk update --no-cache && \
apk add bash curl
# Add user:
RUN adduser testuser --disabled-password
USER testuser
WORKDIR /home/testuser
# Run echo command:
ENTRYPOINT [ "echo", "Hello it's alpine-test" ]
Important
Your container images must follow this naming convention: <registry server>/<namespace>/<project>[/<optional path>]
GitLab has two types of namespaces (you will see this name in URL when you are inside repository on GitLab web page):
- User: Your personal namespace is based on your username.
- Group: A group or subgroup namespace is based on the group or subgroup name.
Examples:
your_domain_name.com/user_name/project_name:tagyour_domain_name.com/user_name/project_name/image:tag
Build an image with a tag that contains registry name (remember about adding port 5050 in the registry path):
$ container_name=alpine-test:0.1 && \
registry_name=your_domain_name.com:5050/user_name/project_name && \
docker build -t ${registry_name}/${container_name} \
-f ~/docker-test-image/Dockerfile .
. - dot at the end of above command is important
Check if the image was built:
$ docker images
Push Docker image to container registry
Push Docker image to the container registry:
$ docker push your_domain_name.com:5050/user_name/project_name/alpine-test:0.1
Run container using custom image
Delete image from your host:
$ docker image prune -a && \
docker images
Run a new container using your custom image from the container registry. First it will pull image from the registry and then run the container using specified image:
$ docker run -it --rm --name alpine-test your_domain_name.com/user_name/project_name/alpine-test:0.1
If the container was pulled and run successfully you will see a message "Hello it's alpine-test" at the end of the command.
If you have access denied:
- check if your user role can push to container registry
- check if tag of image has correct path to repository (you can see that path in
your repository>Deploy>Container registry> CLI Commands)
Delete image from container registry
You can delete image from the container registry using GitLab page:
your repository>Deploy>Container registry> Clicking the trash can icon you will delete the image
Deleting the image doesn't immediately free up storage, it's only marked for deletion. To delete unreferenced container images and recover storage space garbage collection must be run.
To run garbage collection you must log in to your GitLab machine and run command:
$ sudo gitlab-ctl registry-garbage-collect -m
-m- remove untagged images
Warning
Command sudo gitlab-ctl registry-garbage-collect shuts down the container registry prior to the garbage collection and only starts it again after garbage collection completes.
For more information read this part of GitLab documentation.
Use cron for periodic garbage collection:
$ sudo vim /etc/cron.d/registry-garbage-collect
SHELL=/bin/sh
PATH=/usr/local/sbin:/usr/local/bin:/sbin:/bin:/usr/sbin:/usr/bin
# Run every Sunday at 05:00 in the morning
0 5 * * 0 root gitlab-ctl registry-garbage-collect -m