Quarkus - Supersonic Subatomic Java, trying out Quarkus guide “Quarkus - Kubernetes extension” (reinvestigated, part 3), using a local private registry with Quarkus and K3s lameriks 2025 06 52fv4

Quarkus – Supersonic Subatomic Java, trying out Quarkus guide “Quarkus – Kubernetes extension” (reinvestigated, part 3), using a local private registry with Quarkus and K3s

In my previous article, I shared with you the steps I took, trying out the Quarkus guide “Quarkus – Kubernetes extension” (again 😊) and also the steps to install a local private registry in my demo environment and using it with Quarkus. I used the registry Docker Official Image, a Distribution implementation for storing and distributing of container images and artifacts.
[https://technology.amis.nl/software-development/java/quarkus-supersonic-subatomic-java-trying-out-quarkus-guide-quarkus-kubernetes-extension-reinvestigated-part-2-setting-up-a-local-private-registry-and-using-it-with-qua/]

Some years ago, I also wrote articles about the Quarkus Kubernetes Extension using the default public Docker image registry. This time, I wanted to try out using a local private registry.

In this article, I will take you with me on a “ journey”. You can read more about the steps I took to secure a local private registry in my demo environment and using it with Quarkus and K3s (a lightweight certified Kubernetes distribution).

In a next article, you can read more about the steps I took to further automate setting up my demo environment (including the shell script local-registry.sh).

In my previous article about the “Quarkus – Kubernetes extension” and using a local private registry with Quarkus, I ended with an error.
[https://technology.amis.nl/software-development/java/quarkus-supersonic-subatomic-java-trying-out-quarkus-guide-quarkus-kubernetes-extension-reinvestigated-part-2-setting-up-a-local-private-registry-and-using-it-with-qua/]

First I will give you a short recap of the last steps I described in that article.

On my Windows laptop, in my shared folder, I navigated to code-with-quarkus\src\main\resources and added the file application.properties with the following content:

quarkus.container-image.registry=localhost:5000
quarkus.container-image.group=quarkus

Next, I used the following commands on the Linux Command Prompt:

cd /mnt/mysharedfolder/kubernetes-quickstart

mvn clean install

This resulted in the following container image of the Pod being used in the Deployment part of the
generated target/kuberetes.yml Kubernetes manifest:

image: localhost:5000/quarkus/kubernetes-quickstart:1.0.0-SNAPSHOT

Next, I used the following command on the Linux Command Prompt:

mvn clean install -Dquarkus.container-image.build=true -Dquarkus.container-image.push=true

With the following (shorted) output:

[ERROR] Caused by: com.google.cloud.tools.jib.api.InsecureRegistryException: Failed to verify the server at https://localhost:5000/v2/ because only secure connections are allowed.

So, only secure connections are allowed. Quarkus uses the HTTPS protocol, to connect to my local private registry. So, I had to secure it using TLS.

Quarkus - Supersonic Subatomic Java, trying out Quarkus guide “Quarkus - Kubernetes extension” (reinvestigated, part 3), using a local private registry with Quarkus and K3s lameriks 2025 06 53

Run an externally-accessible registry

I had a further look at the documentation about the registry image, “Deploy a registry server” section, “Run an externally-accessible registry” part.

Running a registry only accessible on localhost has limited usefulness. In order to make your registry accessible to external hosts, you must first secure it using TLS.
[https://distribution.github.io/distribution/about/deploying/#run-an-externally-accessible-registry]

$ docker run -d \
  --restart=always \
  --name registry \
  -v "$(pwd)"/certs:/certs \
  -e REGISTRY_HTTP_ADDR=0.0.0.0:443 \
  -e REGISTRY_HTTP_TLS_CERTIFICATE=/certs/domain.crt \
  -e REGISTRY_HTTP_TLS_KEY=/certs/domain.key \
  -p 443:443 \
  registry:3

Also, I looked at the documentation about the registry image, “Deploy a registry server” section, “Restricting access” part.

Restricting access
Except for registries running on secure local networks, registries should always implement access restrictions.

Native basic auth
The simplest way to achieve access restriction is through basic authentication (this is very similar to other web servers’ basic authentication mechanism). This example uses native basic authentication using htpasswd to store the secrets.
[https://distribution.github.io/distribution/about/deploying/#native-basic-auth]

$ docker run -d \
  -p 5000:5000 \
  --restart=always \
  --name registry \
  -v "$(pwd)"/auth:/auth \
  -e "REGISTRY_AUTH=htpasswd" \
  -e "REGISTRY_AUTH_HTPASSWD_REALM=Registry Realm" \
  -e REGISTRY_AUTH_HTPASSWD_PATH=/auth/htpasswd \
  -v "$(pwd)"/certs:/certs \
  -e REGISTRY_HTTP_TLS_CERTIFICATE=/certs/domain.crt \
  -e REGISTRY_HTTP_TLS_KEY=/certs/domain.key \
  registry:3

Securing my local private registry via TLS

After reading more about this subject on the Internet, I used the following commands on the Linux Command Prompt:

cd /mnt/mysharedfolder/
mkdir -p registry
cd registry
mkdir -p certs
mkdir -p auth

Remark about mkdir and the -p flag:
-p, –parents
no error if existing, make parent directories as needed
[https://manpages.ubuntu.com/manpages/trusty/man1/mkdir.1.html]

In order to create the file domain.key (a private key), to be used by environment variable REGISTRY_HTTP_TLS_KEY, I used the following commands on the Linux Command Prompt:

cd certs

openssl genrsa 2048 > domain.key
chmod 400 domain.key

In order to create the file domain.crt (a self-signed certificate for an existing private key), to be used by environment variable REGISTRY_HTTP_TLS_CERTIFICATE, I used the following command on the Linux Command Prompt:

openssl req -new -x509 -nodes -sha1 -days 365 -key domain.key -out domain.crt

This command will ask to enter information that will be incorporated into your certificate request.

Country Name (2 letter code) [AU]:NL
State or Province Name (full name) [Some-State]:Utrecht
Locality Name (eg, city) []:Nieuwegein
Organization Name (eg, company) [Internet Widgits Pty Ltd]:AMIS Conclusion
Organizational Unit Name (eg, section) []:AMIS Technology Blog
Common Name (e.g. server FQDN or YOUR name) []:localhost
Email Address []:

Remark about asking for information:
It is also possible to pass these values via the command line.
Later on, I will be doing this in a new shell script local-registry.sh that I will create. You can read more about this in my next article.

Remark about using sha1:
As you can see, in the command above I used sha1, because of some examples I found.
Later on in this article (when using java keytool) you will see the following warning because of the use of sha1:

Warning:
The input uses the SHA1withRSA signature algorithm which is considered a security risk.

I thought it wise to mention this fact in this article, for your benefit, and as it was a part of my “journey”.

The warning message “The certificate uses the SHA1withRSA signature algorithm which is considered a security risk” is a common one, particularly when dealing with certificates or keystores. The primary issue is the use of the SHA1 hash algorithm, which is considered outdated and vulnerable to collision attacks. This means an attacker could potentially forge a certificate that appears legitimate, compromising the security of encrypted communications. 

What you can do, upgrade to more secure signature algorithms like:

  • SHA256withRSA
  • SHA512withRSA
  • PSS (Probabilistic Signature Scheme) signatures

[AI overview]
AI responses may include mistakes. Learn more

To further automate the manual steps, described in this article, I will be using the shell script local-registry.sh. You can read more about this in my next article.
I will make sure to use sha256 instead of sha1 for my demo environment.

Using basic authentication for my local private registry

I followed the steps, as described in the documentation about the registry image, “Deploy a registry server” section, “Restricting access” part.
[https://distribution.github.io/distribution/about/deploying/#native-basic-auth]

For the basic authentication, I wanted to use the username mylocregusername, with password mylocregpassword.

In order to create a password file with one entry for the user and password, I used the following commands on the Linux Command Prompt:

cd ../auth

docker run --rm --entrypoint htpasswd httpd:2.4 -Bbn mylocregusername mylocregpassword > htpasswd

With the following output:

Unable to find image 'httpd:2.4' locally
2.4: Pulling from library/httpd
61320b01ae5e: Already exists
be60498bea0a: Pull complete
4f4fb700ef54: Pull complete
8f86928406fd: Pull complete
162ef2c73af1: Pull complete
8dbbd44856ed: Pull complete
Digest: sha256:09cb4b94edaaa796522c545328b62e9a0db60315c7be9f2b4e02204919926405
Status: Downloaded newer image for httpd:2.4

Remark about some of the used docker run flags:
–rm Automatically remove the container and its associated anonymous volumes when it exits
[https://docs.docker.com/reference/cli/docker/container/run/#rm]

–entrypoint Overwrite the default ENTRYPOINT of the image
[https://docs.docker.com/reference/cli/docker/container/run/]
[https://docs.docker.com/engine/containers/run/#default-entrypoint]

-Bbn
The distribution registry only supports htpasswd credentials in bcrypt format, so if you omit the -B option when generating the credential using htpasswd, all authentication attempts will fail.
[https://distribution.github.io/distribution/about/deploying/#native-basic-auth]

-B Use bcrypt hashing for passwords. This is currently considered to be very secure.
-b Use batch mode; i.e., get the password from the command line rather than prompting for it.
This option should be used with extreme care, since the password is clearly visible on the command line. For script use see the -i option. Available in 2.4.4 and later.
-n Display the results on standard output rather than updating a file.
This is useful for generating password records acceptable to Apache for inclusion in non-text data stores. This option changes the syntax of the command line, since the passwdfile argument (usually the first one) is omitted. It cannot be combined with the -c option.
[https://httpd.apache.org/docs/trunk/programs/htpasswd.html]

In order to see the content of the password file, I used the following command on the Linux Command Prompt:

cat htpasswd

With the following output:

mylocregusername:$2y$05$bnUGfBaWtCwbACEMB/Br5.4Y9MR4UYUX235aTf7ko0kQilN8G1CVC

Removing the existing local private registry (at localhost:5000)

In order to stop and remove the container registry, I used the following commands on the Linux Command Prompt:

docker container stop registry
docker container rm -v registry

Remark about some of the used docker container rm flags:
-v, –volumes Remove anonymous volumes associated with the container
[https://docs.docker.com/reference/cli/docker/container/rm/]

Quarkus - Supersonic Subatomic Java, trying out Quarkus guide “Quarkus - Kubernetes extension” (reinvestigated, part 3), using a local private registry with Quarkus and K3s lameriks 2025 06 54

Starting the secured local private registry (at localhost:8443)

In order to start my local private registry with basic authentication and securing it using TLS, I used the following commands on the Linux Command Prompt:
[in bold, I highlighted some changes I made]

cd /mnt/mysharedfolder/registry

docker run -d \
  --restart=always \
  --name local-registry \
  -v "$(pwd)"/auth:/auth \
  -e "REGISTRY_AUTH=htpasswd" \
  -e "REGISTRY_AUTH_HTPASSWD_REALM=Registry Realm" \
  -e REGISTRY_AUTH_HTPASSWD_PATH=/auth/htpasswd \
  -e REGISTRY_HTTP_ADDR=0.0.0.0:443 \
  -v "$(pwd)"/certs:/certs \
  -e REGISTRY_HTTP_TLS_CERTIFICATE=/certs/domain.crt \
  -e REGISTRY_HTTP_TLS_KEY=/certs/domain.key \
  -p 8443:443 \
  registry:3

With the following output:

b727a0dc31d20fd4597b63e13bda4bd92c6916600a33eb994383b24a27c331b9

This command bind-mounts the certs/ directory into the container at /certs/, and sets environment variables that tell the container where to find the domain.crt and domain.key file. The registry runs on port 443, the default HTTPS port.
[https://distribution.github.io/distribution/about/deploying/#run-an-externally-accessible-registry]

Remark about some of the used docker run flags:
-v, –volume Bind mount a volume
This mounts a directory into the container at a specified path
[https://docs.docker.com/reference/cli/docker/container/run/#volume]

-p, –publish Publish a container’s port(s) to the host
This binds port 443 of the container to TCP port 8443 on 127.0.0.1 of the host.
[https://docs.docker.com/reference/cli/docker/container/run/#publish]

As you can see, I gave the container the name local-registry and also I used port 8443, because of the port forwarding setting, from my Windows laptop to the VM (via the Vagrantfile).

Next, I checked the list of the running docker containers:

docker container ls

With the following output:

CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES
b727a0dc31d2 registry:3 “/entrypoint.sh /etc…” 20 seconds ago Up 20 seconds 5000/tcp, 0.0.0.0:8443->443/tcp local-registry

Now, my local private registry was setup with basic authentication and using TLS.

Copying an image to my secured local private registry

In order to create an additional tag for the existing image, I used the following command on the Linux Command Prompt:

docker tag localhost:5000/nginx:latest localhost:8443/nginx:latest

In order to get a list of all the docker images, I used the following command on the Linux Command Prompt:

docker image ls

With the following output:

REPOSITORY             TAG       IMAGE ID       CREATED        SIZE
localhost:5000/nginx   latest    be69f2940aaf   7 weeks ago    192MB
localhost:8443/nginx   latest    be69f2940aaf   7 weeks ago    192MB
registry               3         3dec7d02aaea   2 months ago   57.7MB
httpd                  2.4       958373fdd7e8   4 months ago   148MB
hello-world            latest    74cc54e27dc4   4 months ago   10.1kB

In line with the documentation, now I tried to push the image to my secured local private registry running at localhost:8443. This was expected to fail.

I used the following command on the Linux Command Prompt:

docker push localhost:8443/nginx:latest

With the following output:

The push refers to repository [localhost:8443/nginx]
941dd9dd8ee4: Preparing
f6e33ee35fd0: Preparing
9fd8b974f616: Preparing
a8b606cdf152: Preparing
cb857378ec55: Preparing
deb7d8874f38: Waiting
ace34d1d784c: Waiting
no basic auth credentials

And indeed it failed.

So, first I had to login (including providing the password):

docker login --username mylocregusername https://localhost:8443
Password:

With the following output:


WARNING! Your credentials are stored unencrypted in '/home/vagrant/.docker/config.json'.
Configure a credential helper to remove this warning. See
https://docs.docker.com/go/credential-store/

Login Succeeded

Remark:
For now, for my demo environment, I ignored the warning mentioned above.

Again, I tried to push the image to my secured local private registry:

docker push localhost:8443/nginx:latest

With the following output:

The push refers to repository [localhost:8443/nginx]
941dd9dd8ee4: Pushed
f6e33ee35fd0: Pushed
9fd8b974f616: Pushed
a8b606cdf152: Pushed
cb857378ec55: Pushed
deb7d8874f38: Pushed
ace34d1d784c: Pushed
latest: digest: sha256:e5e2c4be5cea9bf49b2c976c65b4fca33d9d094b276a5e517d8e5748100a3c73 size: 1778

This time, it worked 😊.

In order to logout, I used the following command on the Linux Command Prompt:

docker logout https://localhost:8443

With the following output:

Removing login credentials for localhost:8443

Below, you see an overview of the steps with regard to the images described above:

Quarkus - Supersonic Subatomic Java, trying out Quarkus guide “Quarkus - Kubernetes extension” (reinvestigated, part 3), using a local private registry with Quarkus and K3s lameriks 2025 06 55

Using curl to connect to my secured local private registry

In order to get a list of all the docker images inside my secured local private registry, I used the following command on the Linux Command Prompt:

curl localhost:8443/v2/_catalog

With the following output:

Client sent an HTTP request to an HTTPS server.

So, this obviously had to be changed.

Next, I tried:

curl https://localhost:8443/v2/_catalog

With the following output:

curl: (60) SSL certificate problem: self-signed certificate
More details here: https://curl.se/docs/sslcerts.html

curl failed to verify the legitimacy of the server and therefore could not
establish a secure connection to it. To learn more about this situation and
how to fix it, please visit the web page mentioned above.

I had a look at the website mentioned above, and opted for using the –cacert [file] command line flag.

Remark about the –cacert [file] flag:
–cacert <file>
(TLS) Use the specified certificate file to verify the peer. The file may contain multiple CA certificates. The certificate(s) must be in PEM format. Normally curl is built to use a default file for this, so this option is typically used to alter that default file.
[https://curl.se/docs/manpage.html]

Privacy-Enhanced Mail (PEM) is a de facto file format for storing and sending cryptographic keys, certificates, and other data, based on a set of 1993 IETF standards defining “privacy-enhanced mail.” While the original standards were never broadly adopted and were supplanted by PGP and S/MIME, the textual encoding they defined became very popular.
[https://en.wikipedia.org/wiki/Privacy-Enhanced_Mail]

So, what I needed was the certificate in PEM format.

Of course, there are several ways to achieve this.
For example, with the help off:

openssl s_client -showcerts -connect localhost:8443

And then using:

openssl s_client -showcerts -connect localhost:8443 </dev/null | sed -n -e '/-.BEGIN/,/-.END/ p' > local-registry.pem

But, I opted for using:

cd /mnt/mysharedfolder

openssl x509 -in registry/certs/domain.crt -out local-registry.pem -outform PEM

In order to get a list of all the docker images inside my secured local private registry, I used the following commands on the Linux Command Prompt:

cd /mnt/mysharedfolder

curl --cacert local-registry.pem --user mylocregusername https://localhost:8443/v2/_catalog
Enter host password for user 'mylocregusername':

With the following output:

{"repositories":["nginx"]}

Quarkus and the Jib Container Image extension

So, now my secured local private registry was in place and I wanted to try using it, in combination with Quarkus.

Next, on my Windows laptop, in my shared folder, I navigated to kubernetes-quickstart\src\main\resources and changed the content of file application.properties to:
[in bold, I highlighted the changes]

quarkus.container-image.registry=localhost:8443
quarkus.container-image.username=mylocregusername
quarkus.container-image.password=mylocregpassword
quarkus.container-image.group=quarkus

Next, I used the following commands on the Linux Command Prompt:

cd /mnt/mysharedfolder/kubernetes-quickstart

mvn clean install

This resulted in the following container image of the Pod being used in the Deployment part of the
generated target/kuberetes.yml Kubernetes manifest:

image: localhost:8443/quarkus/kubernetes-quickstart:1.0.0-SNAPSHOT

Next, I used the following command on the Linux Command Prompt:

cd /mnt/mysharedfolder/kubernetes-quickstart

mvn clean install -Dquarkus.container-image.build=true -Dquarkus.container-image.push=true

With the following output:

WARNING: A restricted method in java.lang.System has been called
WARNING: java.lang.System::load has been called by org.fusesource.jansi.internal.JansiLoader in an unnamed module (file:/opt/apache-maven-3.9.9/lib/jansi-2.4.1.jar)
WARNING: Use --enable-native-access=ALL-UNNAMED to avoid a warning for callers in this module
WARNING: Restricted methods will be blocked in a future release unless native access is enabled

WARNING: A terminally deprecated method in sun.misc.Unsafe has been called
WARNING: sun.misc.Unsafe::objectFieldOffset has been called by com.google.common.util.concurrent.AbstractFuture$UnsafeAtomicHelper (file:/opt/apache-maven-3.9.9/lib/guava-33.2.1-jre.jar)
WARNING: Please consider reporting this to the maintainers of class com.google.common.util.concurrent.AbstractFuture$UnsafeAtomicHelper
WARNING: sun.misc.Unsafe::objectFieldOffset will be removed in a future release
[INFO] Scanning for projects...
[INFO]
[INFO] -------------------< org.acme:kubernetes-quickstart >-------------------
[INFO] Building kubernetes-quickstart 1.0.0-SNAPSHOT
[INFO]   from pom.xml
[INFO] --------------------------------[ jar ]---------------------------------
[INFO]
[INFO] --- clean:3.2.0:clean (default-clean) @ kubernetes-quickstart ---
[INFO] Deleting /mnt/mysharedfolder/kubernetes-quickstart/target
[INFO]
[INFO] --- resources:3.3.1:resources (default-resources) @ kubernetes-quickstart ---
[INFO] Copying 1 resource from src/main/resources to target/classes
[INFO]
[INFO] --- quarkus:3.22.3:generate-code (default) @ kubernetes-quickstart ---
[INFO]
[INFO] --- compiler:3.14.0:compile (default-compile) @ kubernetes-quickstart ---
[INFO] Recompiling the module because of changed source code.
[INFO] Compiling 1 source file with javac [debug parameters release 21] to target/classes
[INFO]
[INFO] --- quarkus:3.22.3:generate-code-tests (default) @ kubernetes-quickstart ---
[INFO]
[INFO] --- resources:3.3.1:testResources (default-testResources) @ kubernetes-quickstart ---
[INFO] skip non existing resourceDirectory /mnt/mysharedfolder/kubernetes-quickstart/src/test/resources
[INFO]
[INFO] --- compiler:3.14.0:testCompile (default-testCompile) @ kubernetes-quickstart ---
[INFO] Recompiling the module because of changed dependency.
[INFO] Compiling 2 source files with javac [debug parameters release 21] to target/test-classes
[INFO]
[INFO] --- surefire:3.5.2:test (default-test) @ kubernetes-quickstart ---
[INFO] Using auto detected provider org.apache.maven.surefire.junitplatform.JUnitPlatformProvider
[INFO]
[INFO] -------------------------------------------------------
[INFO]  T E S T S
[INFO] -------------------------------------------------------
WARNING: A terminally deprecated method in sun.misc.Unsafe has been called
WARNING: sun.misc.Unsafe::objectFieldOffset has been called by org.jboss.threads.JBossExecutors (file:/home/vagrant/.m2/repository/org/jboss/threads/jboss-threads/3.8.0.Final/jboss-threads-3.8.0.Final.jar)
WARNING: Please consider reporting this to the maintainers of class org.jboss.threads.JBossExecutors
WARNING: sun.misc.Unsafe::objectFieldOffset will be removed in a future release
[INFO] Running org.acme.GreetingResourceTest
2025-06-07 14:59:07,301 INFO  [io.quarkus] (main) kubernetes-quickstart 1.0.0-SNAPSHOT on JVM (powered by Quarkus 3.22.3) started in 5.377s. Listening on: http://localhost:8081
2025-06-07 14:59:07,307 INFO  [io.quarkus] (main) Profile test activated.
2025-06-07 14:59:07,308 INFO  [io.quarkus] (main) Installed features: [cdi, compose, kubernetes, rest, smallrye-context-propagation, vertx]
[INFO] Tests run: 1, Failures: 0, Errors: 0, Skipped: 0, Time elapsed: 3.359 s -- in org.acme.GreetingResourceTest
2025-06-07 14:59:09,938 INFO  [io.quarkus] (main) kubernetes-quickstart stopped in 0.040s
[INFO]
[INFO] Results:
[INFO]
[INFO] Tests run: 1, Failures: 0, Errors: 0, Skipped: 0
[INFO]
[INFO]
[INFO] --- jar:3.4.1:jar (default-jar) @ kubernetes-quickstart ---
[INFO] Building jar: /mnt/mysharedfolder/kubernetes-quickstart/target/kubernetes-quickstart-1.0.0-SNAPSHOT.jar
[INFO]
[INFO] --- quarkus:3.22.3:build (default) @ kubernetes-quickstart ---
[INFO] [io.quarkus.kubernetes.deployment.PropertyUtil] Kubernetes manifests are generated with 'The container port http' having default value '8080'. The app and manifests will get out of sync if the property 'quarkus.http.port' is changed at runtime.
[INFO] [io.quarkus.container.image.jib.deployment.JibProcessor] Starting (local) container image build for jar using jib.
[WARNING] [io.quarkus.container.image.jib.deployment.JibProcessor] Base image 'registry.access.redhat.com/ubi9/openjdk-21-runtime:1.21' does not use a specific image digest - build may not be reproducible
[WARNING] [io.quarkus.container.image.jib.deployment.JibProcessor] GET https://localhost:8443/v2/ failed and will be retried
[INFO] [io.quarkus.container.image.jib.deployment.JibProcessor] Using base image with digest: sha256:360822c35c5741f542ab78fe123e6c4d9b68e0113a88d6e0250bb1f377b17f29
[WARNING] [io.quarkus.container.image.jib.deployment.JibProcessor] GET https://localhost:8443/v2/ failed and will be retried
[WARNING] [io.quarkus.container.image.jib.deployment.JibProcessor] GET https://localhost:8443/v2/ failed and will be retried
[WARNING] [io.quarkus.container.image.jib.deployment.JibProcessor] GET https://localhost:8443/v2/ failed and will be retried
[WARNING] [io.quarkus.container.image.jib.deployment.JibProcessor] GET https://localhost:8443/v2/ failed and will be retried
[WARNING] [io.quarkus.container.image.jib.deployment.JibProcessor] GET https://localhost:8443/v2/ failed and will be retried
[WARNING] [io.quarkus.container.image.jib.deployment.JibProcessor] GET https://localhost:8443/v2/ failed and will be retried
[WARNING] [io.quarkus.container.image.jib.deployment.JibProcessor] GET https://localhost:8443/v2/ failed and will be retried
[WARNING] [io.quarkus.container.image.jib.deployment.JibProcessor] GET https://localhost:8443/v2/ failed and will be retried
[WARNING] [io.quarkus.container.image.jib.deployment.JibProcessor] GET https://localhost:8443/v2/ failed and will be retried
[WARNING] [io.quarkus.container.image.jib.deployment.JibProcessor] GET https://localhost:8443/v2/ failed and will NOT be retried
[ERROR] [io.quarkus.container.image.jib.deployment.JibProcessor] I/O error for image [localhost:8443/quarkus/kubernetes-quickstart]:
[ERROR] [io.quarkus.container.image.jib.deployment.JibProcessor]     javax.net.ssl.SSLHandshakeException
[ERROR] [io.quarkus.container.image.jib.deployment.JibProcessor]     (certificate_unknown) PKIX path building failed: sun.security.provider.certpath.SunCertPathBuilderException: unable to find valid certification path to requested target
[INFO] ------------------------------------------------------------------------
[INFO] BUILD FAILURE
[INFO] ------------------------------------------------------------------------
[INFO] Total time:  01:29 min
[INFO] Finished at: 2025-06-07T15:00:18Z
[INFO] ------------------------------------------------------------------------
[ERROR] Failed to execute goal io.quarkus.platform:quarkus-maven-plugin:3.22.3:build (default) on project kubernetes-quickstart: Failed to build quarkus application: io.quarkus.builder.BuildException: Build failure: Build failed due to errors
[ERROR]         [error]: Build step io.quarkus.container.image.jib.deployment.JibProcessor#buildFromJar threw an exception: java.lang.RuntimeException: Unable to create container image
[ERROR]         at io.quarkus.container.image.jib.deployment.JibProcessor.containerize(JibProcessor.java:258)
[ERROR]         at io.quarkus.container.image.jib.deployment.JibProcessor.buildFromJar(JibProcessor.java:185)
[ERROR]         at java.base/java.lang.invoke.MethodHandle.invokeWithArguments(MethodHandle.java:735)
[ERROR]         at io.quarkus.deployment.ExtensionLoader$3.execute(ExtensionLoader.java:856)
[ERROR]         at io.quarkus.builder.BuildContext.run(BuildContext.java:255)
[ERROR]         at org.jboss.threads.ContextHandler$1.runWith(ContextHandler.java:18)
[ERROR]         at org.jboss.threads.EnhancedQueueExecutor$Task.doRunWith(EnhancedQueueExecutor.java:2675)
[ERROR]         at org.jboss.threads.EnhancedQueueExecutor$Task.run(EnhancedQueueExecutor.java:2654)
[ERROR]         at org.jboss.threads.EnhancedQueueExecutor.runThreadBody(EnhancedQueueExecutor.java:1627)
[ERROR]         at org.jboss.threads.EnhancedQueueExecutor$ThreadBody.run(EnhancedQueueExecutor.java:1594)
[ERROR]         at java.base/java.lang.Thread.run(Thread.java:1447)
[ERROR]         at org.jboss.threads.JBossThread.run(JBossThread.java:499)
[ERROR] Caused by: com.google.cloud.tools.jib.api.InsecureRegistryException: Failed to verify the server at https://localhost:8443/v2/ because only secure connections are allowed.
[ERROR]         at com.google.cloud.tools.jib.registry.RegistryEndpointCaller.call(RegistryEndpointCaller.java:179)
[ERROR]         at com.google.cloud.tools.jib.registry.RegistryEndpointCaller.call(RegistryEndpointCaller.java:114)
[ERROR]         at com.google.cloud.tools.jib.registry.RegistryClient.callRegistryEndpoint(RegistryClient.java:623)
[ERROR]         at com.google.cloud.tools.jib.registry.RegistryClient.doBearerAuth(RegistryClient.java:318)
[ERROR]         at com.google.cloud.tools.jib.registry.RegistryClient.doPushBearerAuth(RegistryClient.java:308)
[ERROR]         at com.google.cloud.tools.jib.builder.steps.AuthenticatePushStep.call(AuthenticatePushStep.java:66)
[ERROR]         at com.google.cloud.tools.jib.builder.steps.AuthenticatePushStep.call(AuthenticatePushStep.java:35)
[ERROR]         at com.google.common.util.concurrent.TrustedListenableFutureTask$TrustedFutureInterruptibleTask.runInterruptibly(TrustedListenableFutureTask.java:128)
[ERROR]         at com.google.common.util.concurrent.InterruptibleTask.run(InterruptibleTask.java:74)
[ERROR]         at com.google.common.util.concurrent.TrustedListenableFutureTask.run(TrustedListenableFutureTask.java:80)
[ERROR]         at java.base/java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1095)
[ERROR]         at java.base/java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:619)
[ERROR]         at java.base/java.lang.Thread.run(Thread.java:1447)
[ERROR] Caused by: javax.net.ssl.SSLHandshakeException: (certificate_unknown) PKIX path building failed: sun.security.provider.certpath.SunCertPathBuilderException: unable to find valid certification path to requested target
[ERROR]         at java.base/sun.security.ssl.Alert.createSSLException(Alert.java:130)
…
[ERROR]         at java.base/sun.security.validator.PKIXValidator.doBuild(PKIXValidator.java:382)
[ERROR]         ... 44 more
[ERROR] -> [Help 1]
[ERROR]
[ERROR] To see the full stack trace of the errors, re-run Maven with the -e switch.
[ERROR] Re-run Maven using the -X switch to enable full debug logging.
[ERROR]
[ERROR] For more information about the errors and possible solutions, please read the following articles:
[ERROR] [Help 1] http://cwiki.apache.org/confluence/display/MAVEN/MojoExecutionException

As you can see in the output above, the following error messages are shown:

[ERROR] [io.quarkus.container.image.jib.deployment.JibProcessor] I/O error for image [localhost:8443/quarkus/kubernetes-quickstart]:
[ERROR] [io.quarkus.container.image.jib.deployment.JibProcessor]     javax.net.ssl.SSLHandshakeException
[ERROR] [io.quarkus.container.image.jib.deployment.JibProcessor]     (certificate_unknown) PKIX path building failed: sun.security.provider.certpath.SunCertPathBuilderException: unable to find valid certification path to requested target

So, now I got an SSLHandshakeException.

Quarkus and allowing using an insecure registry

Next, on my Windows laptop, in my shared folder, I navigated to kubernetes-quickstart\src\main\resources and changed the content of file application.properties to:
[in bold, I highlighted the changes]

quarkus.container-image.registry=localhost:8443
quarkus.container-image.username=mylocregusername
quarkus.container-image.password=mylocregpassword
quarkus.container-image.group=quarkus
quarkus.container-image.insecure=true

Remark about quarkus.container-image.insecure:

Container Image property Type Default
quarkus.container-image.insecure
Whether or not insecure registries are allowed
Environment variable: QUARKUS_CONTAINER_IMAGE_INSECURE
boolean false

[https://quarkus.io/guides/all-config]

Next, I used the following command on the Linux Command Prompt:

cd /mnt/mysharedfolder/kubernetes-quickstart

mvn clean install -Dquarkus.container-image.build=true -Dquarkus.container-image.push=true

With the following output:

WARNING: A restricted method in java.lang.System has been called
WARNING: java.lang.System::load has been called by org.fusesource.jansi.internal.JansiLoader in an unnamed module (file:/opt/apache-maven-3.9.9/lib/jansi-2.4.1.jar)
WARNING: Use --enable-native-access=ALL-UNNAMED to avoid a warning for callers in this module
WARNING: Restricted methods will be blocked in a future release unless native access is enabled

WARNING: A terminally deprecated method in sun.misc.Unsafe has been called
WARNING: sun.misc.Unsafe::objectFieldOffset has been called by com.google.common.util.concurrent.AbstractFuture$UnsafeAtomicHelper (file:/opt/apache-maven-3.9.9/lib/guava-33.2.1-jre.jar)
WARNING: Please consider reporting this to the maintainers of class com.google.common.util.concurrent.AbstractFuture$UnsafeAtomicHelper
WARNING: sun.misc.Unsafe::objectFieldOffset will be removed in a future release
[INFO] Scanning for projects...
[INFO]
[INFO] -------------------< org.acme:kubernetes-quickstart >-------------------
[INFO] Building kubernetes-quickstart 1.0.0-SNAPSHOT
[INFO]   from pom.xml
[INFO] --------------------------------[ jar ]---------------------------------
[INFO]
[INFO] --- clean:3.2.0:clean (default-clean) @ kubernetes-quickstart ---
[INFO] Deleting /mnt/mysharedfolder/kubernetes-quickstart/target
[INFO]
[INFO] --- resources:3.3.1:resources (default-resources) @ kubernetes-quickstart ---
[INFO] Copying 1 resource from src/main/resources to target/classes
[INFO]
[INFO] --- quarkus:3.22.3:generate-code (default) @ kubernetes-quickstart ---
[INFO]
[INFO] --- compiler:3.14.0:compile (default-compile) @ kubernetes-quickstart ---
[INFO] Recompiling the module because of changed source code.
[INFO] Compiling 1 source file with javac [debug parameters release 21] to target/classes
[INFO]
[INFO] --- quarkus:3.22.3:generate-code-tests (default) @ kubernetes-quickstart ---
[INFO]
[INFO] --- resources:3.3.1:testResources (default-testResources) @ kubernetes-quickstart ---
[INFO] skip non existing resourceDirectory /mnt/mysharedfolder/kubernetes-quickstart/src/test/resources
[INFO]
[INFO] --- compiler:3.14.0:testCompile (default-testCompile) @ kubernetes-quickstart ---
[INFO] Recompiling the module because of changed dependency.
[INFO] Compiling 2 source files with javac [debug parameters release 21] to target/test-classes
[INFO]
[INFO] --- surefire:3.5.2:test (default-test) @ kubernetes-quickstart ---
[INFO] Using auto detected provider org.apache.maven.surefire.junitplatform.JUnitPlatformProvider
[INFO]
[INFO] -------------------------------------------------------
[INFO]  T E S T S
[INFO] -------------------------------------------------------
WARNING: A terminally deprecated method in sun.misc.Unsafe has been called
WARNING: sun.misc.Unsafe::objectFieldOffset has been called by org.jboss.threads.JBossExecutors (file:/home/vagrant/.m2/repository/org/jboss/threads/jboss-threads/3.8.0.Final/jboss-threads-3.8.0.Final.jar)
WARNING: Please consider reporting this to the maintainers of class org.jboss.threads.JBossExecutors
WARNING: sun.misc.Unsafe::objectFieldOffset will be removed in a future release
[INFO] Running org.acme.GreetingResourceTest
2025-06-07 15:29:04,743 INFO  [io.quarkus] (main) kubernetes-quickstart 1.0.0-SNAPSHOT on JVM (powered by Quarkus 3.22.3) started in 4.482s. Listening on: http://localhost:8081
2025-06-07 15:29:04,747 INFO  [io.quarkus] (main) Profile test activated.
2025-06-07 15:29:04,747 INFO  [io.quarkus] (main) Installed features: [cdi, compose, kubernetes, rest, smallrye-context-propagation, vertx]
[INFO] Tests run: 1, Failures: 0, Errors: 0, Skipped: 0, Time elapsed: 2.931 s -- in org.acme.GreetingResourceTest
2025-06-07 15:29:07,258 INFO  [io.quarkus] (main) kubernetes-quickstart stopped in 0.045s
[INFO]
[INFO] Results:
[INFO]
[INFO] Tests run: 1, Failures: 0, Errors: 0, Skipped: 0
[INFO]
[INFO]
[INFO] --- jar:3.4.1:jar (default-jar) @ kubernetes-quickstart ---
[INFO] Building jar: /mnt/mysharedfolder/kubernetes-quickstart/target/kubernetes-quickstart-1.0.0-SNAPSHOT.jar
[INFO]
[INFO] --- quarkus:3.22.3:build (default) @ kubernetes-quickstart ---
[INFO] [io.quarkus.kubernetes.deployment.PropertyUtil] Kubernetes manifests are generated with 'The container port http' having default value '8080'. The app and manifests will get out of sync if the property 'quarkus.http.port' is changed at runtime.
[INFO] [io.quarkus.container.image.jib.deployment.JibProcessor] Starting (local) container image build for jar using jib.
[WARNING] [io.quarkus.container.image.jib.deployment.JibProcessor] Base image 'registry.access.redhat.com/ubi9/openjdk-21-runtime:1.21' does not use a specific image digest - build may not be reproducible
[WARNING] [io.quarkus.container.image.jib.deployment.JibProcessor] Cannot verify server at https://localhost:8443/v2/. Attempting again with no TLS verification.
[WARNING] [io.quarkus.container.image.jib.deployment.JibProcessor] Cannot verify server at https://localhost:8443/v2/quarkus/kubernetes-quickstart/blobs/sha256:9e9102ea23fb71372121adc48b6d9065f928be523e170280452241a7dbf97d6c. Attempting again with no TLS verification.
[WARNING] [io.quarkus.container.image.jib.deployment.JibProcessor] Cannot verify server at https://localhost:8443/v2/quarkus/kubernetes-quickstart/blobs/sha256:8e5d1a3d1ca5c311156c309a1ccaa8ff398e2808a2fb7788b447cf4455e5d223. Attempting again with no TLS verification.
[WARNING] [io.quarkus.container.image.jib.deployment.JibProcessor] Cannot verify server at https://localhost:8443/v2/quarkus/kubernetes-quickstart/blobs/sha256:b9967da8c7373da5a2345950ed2a0c7d587b67ad7019494ae766370df5632bce. Attempting again with no TLS verification.
[WARNING] [io.quarkus.container.image.jib.deployment.JibProcessor] Cannot verify server at https://localhost:8443/v2/quarkus/kubernetes-quickstart/blobs/sha256:820c709b55d58b68dbb57dcbcb9ef66a0d7fca8be015128ff9a0a0bca2ea9a1e. Attempting again with no TLS verification.
[WARNING] [io.quarkus.container.image.jib.deployment.JibProcessor] Cannot verify server at https://localhost:8443/v2/quarkus/kubernetes-quickstart/blobs/uploads/. Attempting again with no TLS verification.
[WARNING] [io.quarkus.container.image.jib.deployment.JibProcessor] Cannot verify server at https://localhost:8443/v2/quarkus/kubernetes-quickstart/blobs/uploads/. Attempting again with no TLS verification.
[WARNING] [io.quarkus.container.image.jib.deployment.JibProcessor] Cannot verify server at https://localhost:8443/v2/quarkus/kubernetes-quickstart/blobs/uploads/. Attempting again with no TLS verification.
[WARNING] [io.quarkus.container.image.jib.deployment.JibProcessor] Cannot verify server at https://localhost:8443/v2/quarkus/kubernetes-quickstart/blobs/uploads/. Attempting again with no TLS verification.
[INFO] [io.quarkus.container.image.jib.deployment.JibProcessor] Using base image with digest: sha256:360822c35c5741f542ab78fe123e6c4d9b68e0113a88d6e0250bb1f377b17f29
[INFO] [io.quarkus.container.image.jib.deployment.JibProcessor] Container entrypoint set to [/opt/jboss/container/java/run/run-java.sh]
[INFO] [io.quarkus.container.image.jib.deployment.JibProcessor] Pushed container image localhost:8443/quarkus/kubernetes-quickstart:1.0.0-SNAPSHOT (sha256:7f39ab901107863a75d4ca299dacbc5806e829c149498e68e36f45fe6da73936)

[INFO] [io.quarkus.deployment.QuarkusAugmentor] Quarkus augmentation completed in 19006ms
[INFO]
[INFO] --- failsafe:3.5.2:integration-test (default) @ kubernetes-quickstart ---
[INFO] Tests are skipped.
[INFO]
[INFO] --- quarkus:3.22.3:native-image-agent (default) @ kubernetes-quickstart ---
[INFO] Missing native-image-agent-base-config directory with native image agent configuration to transform
[INFO]
[INFO] --- failsafe:3.5.2:verify (default) @ kubernetes-quickstart ---
[INFO] Tests are skipped.
[INFO]
[INFO] --- install:3.1.2:install (default-install) @ kubernetes-quickstart ---
[INFO] Installing /mnt/mysharedfolder/kubernetes-quickstart/pom.xml to /home/vagrant/.m2/repository/org/acme/kubernetes-quickstart/1.0.0-SNAPSHOT/kubernetes-quickstart-1.0.0-SNAPSHOT.pom
[INFO] Installing /mnt/mysharedfolder/kubernetes-quickstart/target/kubernetes-quickstart-1.0.0-SNAPSHOT.jar to /home/vagrant/.m2/repository/org/acme/kubernetes-quickstart/1.0.0-SNAPSHOT/kubernetes-quickstart-1.0.0-SNAPSHOT.jar
[INFO] ------------------------------------------------------------------------
[INFO] BUILD SUCCESS
[INFO] ------------------------------------------------------------------------
[INFO] Total time:  36.111 s
[INFO] Finished at: 2025-06-07T15:29:26Z
[INFO] ------------------------------------------------------------------------

In order to get a list of all the docker images inside my secured local private registry, I used the following commands on the Linux Command Prompt:

cd /mnt/mysharedfolder

curl --cacert local-registry.pem --user mylocregusername https://localhost:8443/v2/_catalog
Enter host password for user 'mylocregusername':

With the following output:

{"repositories":["nginx","quarkus/kubernetes-quickstart"]}

So, indeed this worked. But this was not what I wanted, because now an insecure registry was allowed!

So, on my Windows laptop, in my shared folder, I navigated to kubernetes-quickstart\src\main\resources and changed the content of file application.properties to:
[in bold, I highlighted the changes]

quarkus.container-image.registry=localhost:8443
quarkus.container-image.username=mylocregusername
quarkus.container-image.password=mylocregpassword
quarkus.container-image.group=quarkus
quarkus.container-image.insecure=true

As you can see, I removed the property quarkus.container-image.insecure, because it defaults to false.

Making the connection secure from Quarkus (and it’s JDK) to my local private registry.

The next step was to make the connection secure from Quarkus (and it’s JDK) to my local private registry.

Remember that I created the file domain.crt, a self-signed certificate (for an existing private key) for my local private registry. I also needed that certificate in PEM format, so with openssl I created the file local-registry.pem.

What we want is for Quarkus (and it’s JDK), a.k.a. the “Client”, to trust the local private registry, a.k.a. the “Server”, that it’s going to communicate with. After all, if the certificate presented by the “Server” isn’t in the “Client’s” truststore, we’ll get an SSLHandshakeException, as we already have seen.

In order to import the “Server”s self-signed certificate (local-registry.pem) into file cacerts, the default Java truststore, in an entry with an alias of mylocregcert, I used the following commands on the Linux Command Prompt:

cd /mnt/mysharedfolder

sudo "$JAVA_HOME/bin/keytool" -noprompt -keystore "$JAVA_HOME/lib/security/cacerts" -importcert -alias mylocregcert -file local-registry.pem

With the following output:

Warning: use -cacerts option to access cacerts keystore
Owner: CN=localhost, OU=AMIS Technology Blog, O=AMIS Conclusion, L=Nieuwegein, ST=Utrecht, C=NL
Issuer: CN=localhost, OU=AMIS Technology Blog, O=AMIS Conclusion, L=Nieuwegein, ST=Utrecht, C=NL
Serial number: 1943fbcf6ab830b407f13dde0e9decbd316dc16c
Valid from: Sat Jun 07 06:31:02 UTC 2025 until: Sun Jun 07 06:31:02 UTC 2026
Certificate fingerprints:
         SHA1: 83:55:6D:0E:F8:24:E5:83:F8:CF:75:03:FC:F9:58:56:6B:EA:D9:1C
         SHA256: 9B:01:DC:D7:8C:8F:F4:1C:EC:26:A0:C1:8C:8E:5A:11:11:55:62:56:44:02:E8:35:B3:7E:3C:E7:5F:EA:08:EC
Signature algorithm name: SHA1withRSA (weak)
Subject Public Key Algorithm: 2048-bit RSA key
Version: 3

Extensions:

#1: ObjectId: 2.5.29.35 Criticality=false
AuthorityKeyIdentifier [
KeyIdentifier [
0000: E0 BC C4 0E 57 6E EA E6   95 9C 19 92 E6 16 AD 3A  ....Wn.........:
0010: 06 13 99 94                                        ....
]
]

#2: ObjectId: 2.5.29.19 Criticality=true
BasicConstraints:[
  CA:true
  PathLen: no limit
]

#3: ObjectId: 2.5.29.14 Criticality=false
SubjectKeyIdentifier [
KeyIdentifier [
0000: E0 BC C4 0E 57 6E EA E6   95 9C 19 92 E6 16 AD 3A  ....Wn.........:
0010: 06 13 99 94                                        ....
]
]


Warning:
The input uses the SHA1withRSA signature algorithm which is considered a security risk.

Trust this certificate? [no]:  yes
Certificate was added to keystore

Remark about keytool:
As you can see, you will be asked if you trust this certificate. By the way, by using the -noprompt flag on the command line, you aren’t prompted for input yes/no and yes is assumed.

The keytool command is a key and certificate management utility. It enables users to administer their own public/private key pairs and associated certificates for use in self-authentication (where a user authenticates themselves to other users and services) or data integrity and authentication services, by using digital signatures. The keytool command also enables users to cache the public keys (in the form of certificates) of their communicating peers.
A certificate is a digitally signed statement from one entity (person, company, and so on), which says that the public key (and some other information) of some other entity has a particular value. When data is digitally signed, the signature can be verified to check the data integrity and authenticity. Integrity means that the data hasn’t been modified or tampered with, and authenticity means that the data comes from the individual who claims to have created and signed it.
[https://cr.openjdk.org/~jjg/8261930/docs/specs/man/keytool.html]

In the command above, First, I didn’t use sudo but then I got the following error:

keytool error: java.io.FileNotFoundException: /opt/jdk-24.0.1/lib/security/cacerts (Permission denied)

Remark about sha1 (SHA1withRSA):
In the output you can see the following warning because of the use of sha1 earlier on:

Warning:
The input uses the SHA1withRSA signature algorithm which is considered a security risk.

As I mentioned before, to further automate the manual steps described in this article, I will be using the shell script local-registry.sh. You can read more about this in my next article.
I will make sure to use sha256 instead of sha1 when creating a self-signed certificate for my demo environment.

Next, in order to print the contents of the truststore entry identified by -alias , I used the following command on the Linux Command Prompt:

keytool -list -keystore "$JAVA_HOME/lib/security/cacerts" -alias mylocregcert

With the following output:

Warning: use -cacerts option to access cacerts keystore
mylocregcert, Jun 9, 2025, trustedCertEntry,
Certificate fingerprint (SHA-256): 9B:01:DC:D7:8C:8F:F4:1C:EC:26:A0:C1:8C:8E:5A:11:11:55:62:56:44:02:E8:35:B3:7E:3C:E7:5F:EA:08:EC

Next, I used the following commands on the Linux Command Prompt:

cd /mnt/mysharedfolder/kubernetes-quickstart

mvn clean install -Dquarkus.container-image.build=true -Dquarkus.container-image.push=true

With the following output:

WARNING: A restricted method in java.lang.System has been called
WARNING: java.lang.System::load has been called by org.fusesource.jansi.internal.JansiLoader in an unnamed module (file:/opt/apache-maven-3.9.9/lib/jansi-2.4.1.jar)
WARNING: Use --enable-native-access=ALL-UNNAMED to avoid a warning for callers in this module
WARNING: Restricted methods will be blocked in a future release unless native access is enabled

WARNING: A terminally deprecated method in sun.misc.Unsafe has been called
WARNING: sun.misc.Unsafe::objectFieldOffset has been called by com.google.common.util.concurrent.AbstractFuture$UnsafeAtomicHelper (file:/opt/apache-maven-3.9.9/lib/guava-33.2.1-jre.jar)
WARNING: Please consider reporting this to the maintainers of class com.google.common.util.concurrent.AbstractFuture$UnsafeAtomicHelper
WARNING: sun.misc.Unsafe::objectFieldOffset will be removed in a future release
[INFO] Scanning for projects...
[INFO]
[INFO] -------------------< org.acme:kubernetes-quickstart >-------------------
[INFO] Building kubernetes-quickstart 1.0.0-SNAPSHOT
[INFO]   from pom.xml
[INFO] --------------------------------[ jar ]---------------------------------
[INFO]
[INFO] --- clean:3.2.0:clean (default-clean) @ kubernetes-quickstart ---
[INFO] Deleting /mnt/mysharedfolder/kubernetes-quickstart/target
[INFO]
[INFO] --- resources:3.3.1:resources (default-resources) @ kubernetes-quickstart ---
[INFO] Copying 1 resource from src/main/resources to target/classes
[INFO]
[INFO] --- quarkus:3.22.3:generate-code (default) @ kubernetes-quickstart ---
[INFO]
[INFO] --- compiler:3.14.0:compile (default-compile) @ kubernetes-quickstart ---
[INFO] Recompiling the module because of changed source code.
[INFO] Compiling 1 source file with javac [debug parameters release 21] to target/classes
[INFO]
[INFO] --- quarkus:3.22.3:generate-code-tests (default) @ kubernetes-quickstart ---
[INFO]
[INFO] --- resources:3.3.1:testResources (default-testResources) @ kubernetes-quickstart ---
[INFO] skip non existing resourceDirectory /mnt/mysharedfolder/kubernetes-quickstart/src/test/resources
[INFO]
[INFO] --- compiler:3.14.0:testCompile (default-testCompile) @ kubernetes-quickstart ---
[INFO] Recompiling the module because of changed dependency.
[INFO] Compiling 2 source files with javac [debug parameters release 21] to target/test-classes
[INFO]
[INFO] --- surefire:3.5.2:test (default-test) @ kubernetes-quickstart ---
[INFO] Using auto detected provider org.apache.maven.surefire.junitplatform.JUnitPlatformProvider
[INFO]
[INFO] -------------------------------------------------------
[INFO]  T E S T S
[INFO] -------------------------------------------------------
WARNING: A terminally deprecated method in sun.misc.Unsafe has been called
WARNING: sun.misc.Unsafe::objectFieldOffset has been called by org.jboss.threads.JBossExecutors (file:/home/vagrant/.m2/repository/org/jboss/threads/jboss-threads/3.8.0.Final/jboss-threads-3.8.0.Final.jar)
WARNING: Please consider reporting this to the maintainers of class org.jboss.threads.JBossExecutors
WARNING: sun.misc.Unsafe::objectFieldOffset will be removed in a future release
[INFO] Running org.acme.GreetingResourceTest
2025-06-09 08:14:20,752 INFO  [io.quarkus] (main) kubernetes-quickstart 1.0.0-SNAPSHOT on JVM (powered by Quarkus 3.22.3) started in 10.596s. Listening on: http://localhost:8081
2025-06-09 08:14:20,763 INFO  [io.quarkus] (main) Profile test activated.
2025-06-09 08:14:20,764 INFO  [io.quarkus] (main) Installed features: [cdi, compose, kubernetes, rest, smallrye-context-propagation, vertx]
[INFO] Tests run: 1, Failures: 0, Errors: 0, Skipped: 0, Time elapsed: 6.654 s -- in org.acme.GreetingResourceTest
2025-06-09 08:14:26,235 INFO  [io.quarkus] (main) kubernetes-quickstart stopped in 0.070s
[INFO]
[INFO] Results:
[INFO]
[INFO] Tests run: 1, Failures: 0, Errors: 0, Skipped: 0
[INFO]
[INFO]
[INFO] --- jar:3.4.1:jar (default-jar) @ kubernetes-quickstart ---
[INFO] Building jar: /mnt/mysharedfolder/kubernetes-quickstart/target/kubernetes-quickstart-1.0.0-SNAPSHOT.jar
[INFO]
[INFO] --- quarkus:3.22.3:build (default) @ kubernetes-quickstart ---
[INFO] [io.quarkus.kubernetes.deployment.PropertyUtil] Kubernetes manifests are generated with 'The container port http' having default value '8080'. The app and manifests will get out of sync if the property 'quarkus.http.port' is changed at runtime.
[INFO] [io.quarkus.container.image.jib.deployment.JibProcessor] Starting (local) container image build for jar using jib.
[WARNING] [io.quarkus.container.image.jib.deployment.JibProcessor] Base image 'registry.access.redhat.com/ubi9/openjdk-21-runtime:1.21' does not use a specific image digest - build may not be reproducible
[INFO] [io.quarkus.container.image.jib.deployment.JibProcessor] Using base image with digest: sha256:360822c35c5741f542ab78fe123e6c4d9b68e0113a88d6e0250bb1f377b17f29
[INFO] [io.quarkus.container.image.jib.deployment.JibProcessor] Container entrypoint set to [/opt/jboss/container/java/run/run-java.sh]
[INFO] [io.quarkus.container.image.jib.deployment.JibProcessor] Pushed container image localhost:8443/quarkus/kubernetes-quickstart:1.0.0-SNAPSHOT (sha256:6afa788e876f2ae299dd7e5e630540989db2c3297629b38d0d99ab247fec5949)

[INFO] [io.quarkus.deployment.QuarkusAugmentor] Quarkus augmentation completed in 22411ms
[INFO]
[INFO] --- failsafe:3.5.2:integration-test (default) @ kubernetes-quickstart ---
[INFO] Tests are skipped.
[INFO]
[INFO] --- quarkus:3.22.3:native-image-agent (default) @ kubernetes-quickstart ---
[INFO] Missing native-image-agent-base-config directory with native image agent configuration to transform
[INFO]
[INFO] --- failsafe:3.5.2:verify (default) @ kubernetes-quickstart ---
[INFO] Tests are skipped.
[INFO]
[INFO] --- install:3.1.2:install (default-install) @ kubernetes-quickstart ---
[INFO] Installing /mnt/mysharedfolder/kubernetes-quickstart/pom.xml to /home/vagrant/.m2/repository/org/acme/kubernetes-quickstart/1.0.0-SNAPSHOT/kubernetes-quickstart-1.0.0-SNAPSHOT.pom
[INFO] Installing /mnt/mysharedfolder/kubernetes-quickstart/target/kubernetes-quickstart-1.0.0-SNAPSHOT.jar to /home/vagrant/.m2/repository/org/acme/kubernetes-quickstart/1.0.0-SNAPSHOT/kubernetes-quickstart-1.0.0-SNAPSHOT.jar
[INFO] ------------------------------------------------------------------------
[INFO] BUILD SUCCESS
[INFO] ------------------------------------------------------------------------
[INFO] Total time:  01:02 min
[INFO] Finished at: 2025-06-09T08:14:50Z
[INFO] ------------------------------------------------------------------------

In order to get a list of all the docker images inside my secured local private registry, I used the following commands on the Linux Command Prompt:

cd /mnt/mysharedfolder

curl --cacert local-registry.pem --user mylocregusername https://localhost:8443/v2/_catalog
Enter host password for user 'mylocregusername':

With the following output:

{"repositories":["nginx","quarkus/kubernetes-quickstart"]}

In order to get a list of all the tags of docker image quarkus/kubernetes-quickstart inside my secured local private registry, I used the following command on the Linux Command Prompt:

cd /mnt/mysharedfolder

curl --cacert local-registry.pem --user mylocregusername https://localhost:8443/v2/quarkus/kubernetes-quickstart/tags/list

With the following output:

Enter host password for user 'mylocregusername':
{"name":"quarkus/kubernetes-quickstart","tags":["1.0.0-SNAPSHOT"]}

Quarkus - Supersonic Subatomic Java, trying out Quarkus guide “Quarkus - Kubernetes extension” (reinvestigated, part 3), using a local private registry with Quarkus and K3s lameriks 2025 06 56

So, using the self-signed certificate (local-registry.pem) from Quarkus’s truststore to connect to the secured local private registry worked 😊.

K3s Private Registry Configuration

Now, I wanted to let K3s also communicate with my secured local private registry.

So, I had a look at the “K3s” documentation, “Private Registry Configuration” section.

Containerd can be configured to connect to private registries and use them to pull images as needed by the kubelet.

Upon startup, K3s will check to see if /etc/rancher/k3s/registries.yaml exists. If so, the registry configuration contained in this file is used when generating the containerd configuration.
[https://docs.k3s.io/installation/private-registry]

Default Endpoint Fallback
Containerd has an implicit “default endpoint” for all registries. The default endpoint is always tried as a last resort, even if there are other endpoints listed for that registry in registries.yaml.

The default endpoint for docker.io is https://index.docker.io/v2.
The default endpoint for all other registries is https://<REGISTRY>/v2, where <REGISTRY> is the registry hostname and optional port.

In order to be recognized as a registry, the first component of the image name must contain at least one period or colon. For historical reasons, images without a registry specified in their name are implicitly identified as being from docker.io.
[https://docs.k3s.io/installation/private-registry]

Registries Configuration File

The file consists of two top-level keys, with subkeys for each registry:

mirrors:
  <REGISTRY>:
    endpoint:
      - https://<REGISTRY>/v2
configs:
  <REGISTRY>:
    auth:
      username: <BASIC AUTH USERNAME>
      password: <BASIC AUTH PASSWORD>
      token: <BEARER TOKEN>
    tls:
      ca_file: <PATH TO SERVER CA>
      cert_file: <PATH TO CLIENT CERT>
      key_file: <PATH TO CLIENT KEY>
      insecure_skip_verify: <SKIP TLS CERT VERIFICATION BOOLEAN>

[https://docs.k3s.io/installation/private-registry]

In line with the documentation, in order to create the file registries.yaml, I used the following commands on the Linux Command Prompt:

cd /etc/rancher/k3s

sudo nano registries.yaml

And added the following content:

mirrors:
  localhost:8443:
    endpoint:
      - https://localhost:8443/v2
configs:
  localhost:8443:
    auth:
      username: mylocregusername
      password: mylocregpassword
    tls:
      insecure_skip_verify: true

Remark about the configs section:
insecure_skip_verify Boolean that defines if TLS verification should be skipped for the registry
[https://docs.k3s.io/installation/private-registry#configs]

As you can see, TLS verification is disabled for registry localhost:8443.

In order for K3s to recognize this change, I restarted it, with the following commands on the Linux Command Prompt:

sudo systemctl stop k3s
sudo systemctl start k3s

After the restart there is subdirectory with a hosts.toml file for each <REGISTRY>:

/var/lib/rancher/k3s/agent/etc/containerd/<REGISTRY>/hosts.toml

In order to check if the change was implemented, I used the following command on the Linux Command Prompt:

sudo cat /var/lib/rancher/k3s/agent/etc/containerd/certs.d/localhost:8443/hosts.toml

With the following output:

# File generated by k3s. DO NOT EDIT.
server = "http://localhost:8443/v2"
capabilities = ["pull", "resolve", "push"]
skip_verify = true

[host]

[host."https://localhost:8443/v2"]
  capabilities = ["pull", "resolve"]
  skip_verify = true

In order to apply the generated manifest kubernetes.yml to the Kubernetes cluster from the project root, I used the following commands on the Linux Command Prompt:

cd /mnt/mysharedfolder/kubernetes-quickstart

kubectl apply -f target/kubernetes/kubernetes.yml

Then, in order the check whether the Pod was running successfully (without the need to use the Kubernetes Dashboard for this), I used the following command on the Linux Command Prompt:

sudo cat /var/lib/rancher/k3s/agent/containerd/containerd.log

With the following output:

…
time="2025-06-09T12:23:52.753651199Z" level=info msg="RunPodSandbox for &PodSandboxMetadata{Name:kubernetes-quickstart-6fcddbfb9c-thnzl,Uid:1645d8e8-4348-411d-b476-fd5498ed1b31,Namespace:default,Attempt:0,}"

map[string]interface {}{"cniVersion":"1.0.0", "forceAddress":true, "hairpinMode":true, "ipMasq":false, "ipam":map[string]interface {}{"ranges":[][]map[string]interface {}{[]map[string]interface {}{map[string]interface {}{"subnet":"10.42.0.0/24"}}}, "routes":[]types.Route{types.Route{Dst:net.IPNet{IP:net.IP{0xa, 0x2a, 0x0, 0x0}, Mask:net.IPMask{0xff, 0xff, 0x0, 0x0}}, GW:net.IP(nil), MTU:0, AdvMSS:0, Priority:0, Table:(*int)(nil), Scope:(*int)(nil)}}, "type":"host-local"}, "isDefaultGateway":true, "isGateway":true, "mtu":(*uint)(0xc00008ad10), "name":"cbr0", "type":"bridge"}
delegateAdd: netconf sent to delegate plugin:
{"cniVersion":"1.0.0","forceAddress":true,"hairpinMode":true,"ipMasq":false,"ipam":{"ranges":[[{"subnet":"10.42.0.0/24"}]],"routes":[{"dst":"10.42.0.0/16"}],"type":"host-local"},"isDefaultGateway":true,"isGateway":true,"mtu":1450,"name":"cbr0","type":"bridge"}time="2025-06-09T12:23:52.911903236Z" level=info msg="connecting to shim 9604e4b5845019b0c8cba8f2e50edb5f7290092154d90a314a5045974c354ee5" address="unix:///run/containerd/s/9f9d78e8c74806a7cea7c3ec8223b807700715bca1e078c9f91b9ec10ba9e4b4" namespace=k8s.io protocol=ttrpc version=3
time="2025-06-09T12:23:53.046302773Z" level=info msg="RunPodSandbox for &PodSandboxMetadata{Name:kubernetes-quickstart-6fcddbfb9c-thnzl,Uid:1645d8e8-4348-411d-b476-fd5498ed1b31,Namespace:default,Attempt:0,} returns sandbox id \"9604e4b5845019b0c8cba8f2e50edb5f7290092154d90a314a5045974c354ee5\""
time="2025-06-09T12:23:53.051621401Z" level=info msg="PullImage \"localhost:8443/quarkus/kubernetes-quickstart:1.0.0-SNAPSHOT\""
time="2025-06-09T12:23:53.056285483Z" level=info msg="host will try HTTPS first since it is configured for HTTP with a TLS configuration, consider changing host to HTTPS or removing unused TLS configuration" host="localhost:8443"
time="2025-06-09T12:23:53.074533021Z" level=info msg="host will try HTTPS first since it is configured for HTTP with a TLS configuration, consider changing host to HTTPS or removing unused TLS configuration" host="localhost:8443"
time="2025-06-09T12:24:06.247201703Z" level=info msg="ImageCreate event name:\"localhost:8443/quarkus/kubernetes-quickstart:1.0.0-SNAPSHOT\"  labels:{key:\"io.cri-containerd.image\"  value:\"managed\"}"
time="2025-06-09T12:24:06.249907999Z" level=info msg="stop pulling image localhost:8443/quarkus/kubernetes-quickstart:1.0.0-SNAPSHOT: active requests=0, bytes read=150143507"
time="2025-06-09T12:24:06.252984430Z" level=info msg="ImageCreate event name:\"sha256:abe749a34858fc46e3503ca3353f23e01e83993b160b30c4fb2c0f5c98dca646\"  labels:{key:\"io.cri-containerd.image\"  value:\"managed\"}"
time="2025-06-09T12:24:06.258436765Z" level=info msg="ImageCreate event name:\"localhost:8443/quarkus/kubernetes-quickstart@sha256:6afa788e876f2ae299dd7e5e630540989db2c3297629b38d0d99ab247fec5949\"  labels:{key:\"io.cri-containerd.image\"  value:\"managed\"}"
time="2025-06-09T12:24:06.259707655Z" level=info msg="Pulled image \"localhost:8443/quarkus/kubernetes-quickstart:1.0.0-SNAPSHOT\" with image id \"sha256:abe749a34858fc46e3503ca3353f23e01e83993b160b30c4fb2c0f5c98dca646\", repo tag \"localhost:8443/quarkus/kubernetes-quickstart:1.0.0-SNAPSHOT\", repo digest \"localhost:8443/quarkus/kubernetes-quickstart@sha256:6afa788e876f2ae299dd7e5e630540989db2c3297629b38d0d99ab247fec5949\", size \"150143507\" in 13.208057796s"
time="2025-06-09T12:24:06.259827088Z" level=info msg="PullImage \"localhost:8443/quarkus/kubernetes-quickstart:1.0.0-SNAPSHOT\" returns image reference \"sha256:abe749a34858fc46e3503ca3353f23e01e83993b160b30c4fb2c0f5c98dca646\""
time="2025-06-09T12:24:06.266939409Z" level=info msg="CreateContainer within sandbox \"9604e4b5845019b0c8cba8f2e50edb5f7290092154d90a314a5045974c354ee5\" for container &ContainerMetadata{Name:kubernetes-quickstart,Attempt:0,}"
time="2025-06-09T12:24:06.296540486Z" level=info msg="Container 36ee2ccc39f32a2d192d114e32fedeaa655b322dce0efc430091d0d8a22bcbe9: CDI devices from CRI Config.CDIDevices: []"
time="2025-06-09T12:24:06.317714300Z" level=info msg="CreateContainer within sandbox \"9604e4b5845019b0c8cba8f2e50edb5f7290092154d90a314a5045974c354ee5\" for &ContainerMetadata{Name:kubernetes-quickstart,Attempt:0,} returns container id \"36ee2ccc39f32a2d192d114e32fedeaa655b322dce0efc430091d0d8a22bcbe9\""
time="2025-06-09T12:24:06.319805186Z" level=info msg="StartContainer for \"36ee2ccc39f32a2d192d114e32fedeaa655b322dce0efc430091d0d8a22bcbe9\""
time="2025-06-09T12:24:06.320766873Z" level=info msg="connecting to shim 36ee2ccc39f32a2d192d114e32fedeaa655b322dce0efc430091d0d8a22bcbe9" address="unix:///run/containerd/s/9f9d78e8c74806a7cea7c3ec8223b807700715bca1e078c9f91b9ec10ba9e4b4" protocol=ttrpc version=3
time="2025-06-09T12:24:06.462210326Z" level=info msg="StartContainer for \"36ee2ccc39f32a2d192d114e32fedeaa655b322dce0efc430091d0d8a22bcbe9\" returns successfully"

So, this worked.

However, it was not quit what I wanted, because TLS verification is disabled for registry localhost:8443.

Removing the created Kubernetes objects and changing the K3s Private Registry Configuration

So, the Quarkus generated manifest kubernetes.yml, was applied to the Kubernetes cluster and created the following Kubernetes objects (in the default namespace):

  • Service
  • Deployment
  • Replica Set
  • Pod

Most of these objects have kubernetes-quickstart as name (defined in kubernetes.yml), but the names of the Replica Set and Pod(s) are determined by Kubernetes.

In order to find out the name of the Replica Set (in the default namespace), I used the following command on the Linux Command Prompt:

kubectl get replicasets

With the following output:

NAME                               DESIRED   CURRENT   READY   AGE
kubernetes-quickstart-6fcddbfb9c   1         1         1       25m

In order to find out the name of the Pod (in the default namespace), I used the following command on the Linux Command Prompt:

kubectl get pods

With the following output:

NAME                                     READY   STATUS    RESTARTS   AGE
kubernetes-quickstart-6fcddbfb9c-thnzl   1/1     Running   0          25m

In order to conveniently get the name of the Replica Set (in the default namespace), I used the following command on the Linux Command Prompt:

kubectl get replicasets -o=jsonpath='{range .items..metadata}{.name}{"\n"}{end}' | grep kubernetes-quickstart-

With the following output:

kubernetes-quickstart-6fcddbfb9c

Next, in order to delete the Replica Set and all of the dependent Pods (in the default namespace), I used the following command on the Linux Command Prompt:

kubectl delete -n default replicaset $( kubectl get replicasets -o=jsonpath='{range .items..metadata}{.name}{"\n"}{end}' | grep kubernetes-quickstart- | awk '{print $1}')

With the following output:

replicaset.apps "kubernetes-quickstart-6fcddbfb9c" deleted

To delete a ReplicaSet and all of its Pods, use kubectl delete. The Garbage collector automatically deletes all of the dependent Pods by default.
[https://kubernetes.io/docs/concepts/workloads/controllers/replicaset/#deleting-a-replicaset-and-its-pods]

In order to delete the Deployment, I used the following command on the Linux Command Prompt:

kubectl delete -n default deployment kubernetes-quickstart

With the following output:

deployment.apps "kubernetes-quickstart" deleted

In order to delete the Service, I used the following command on the Linux Command Prompt:

kubectl delete -n default service kubernetes-quickstart

With the following output:

service "kubernetes-quickstart" deleted

Next, in order to change the K3s Private Registry Configuration, I changed the content of file registries.yaml to:
[in bold, I highlighted the changes]

mirrors:
  localhost:8443:
    endpoint:
      - "https://localhost:8443"
configs:
  localhost:8443:
    auth:
      username: mylocregusername
      password: mylocregpassword
    tls:
      insecure_skip_verify: false
      ca_file: /mnt/mysharedfolder/local-registry.pem

Remark about the configs section:
ca_file Defines the CA certificate path to be used to verify the registry’s server cert file
[https://docs.k3s.io/installation/private-registry#configs]

In order for K3s to recognize this change, I restarted it, with the following commands on the Linux Command Prompt:

sudo systemctl stop k3s
sudo systemctl start k3s

After the restart there is subdirectory with a hosts.toml file for each <REGISTRY>:

/var/lib/rancher/k3s/agent/etc/containerd/<REGISTRY>/hosts.toml

In order to check if the change was implemented, I used the following command on the Linux Command Prompt:

sudo cat /var/lib/rancher/k3s/agent/etc/containerd/certs.d/localhost:8443/hosts.toml

With the following output:

# File generated by k3s. DO NOT EDIT.
server = "http://localhost:8443/v2"
capabilities = ["pull", "resolve", "push"]
ca = ["/mnt/mysharedfolder/local-registry.pem"]

[host]

[host."https://localhost:8443/v2"]
  capabilities = ["pull", "resolve"]
  ca = ["/mnt/mysharedfolder/local-registry.pem"]

In order to apply the generated manifest kubernetes.yml to the Kubernetes cluster from the project root, again I used the following commands on the Linux Command Prompt:

cd /mnt/mysharedfolder/kubernetes-quickstart

kubectl apply -f target/kubernetes/kubernetes.yml

Then, in order the check whether the Pod was running successfully (without the need to use the Kubernetes Dashboard for this), again I used the following command on the Linux Command Prompt:

sudo cat /var/lib/rancher/k3s/agent/containerd/containerd.log

With the following output:

…
time="2025-06-09T13:54:47.611052194Z" level=info msg="RunPodSandbox for &PodSandboxMetadata{Name:kubernetes-quickstart-6fcddbfb9c-c5dfl,Uid:934dff08-1a08-4059-acf2-9aeeabf38e66,Namespace:default,Attempt:0,}"

map[string]interface {}{"cniVersion":"1.0.0", "forceAddress":true, "hairpinMode":true, "ipMasq":false, "ipam":map[string]interface {}{"ranges":[][]map[string]interface {}{[]map[string]interface {}{map[string]interface {}{"subnet":"10.42.0.0/24"}}}, "routes":[]types.Route{types.Route{Dst:net.IPNet{IP:net.IP{0xa, 0x2a, 0x0, 0x0}, Mask:net.IPMask{0xff, 0xff, 0x0, 0x0}}, GW:net.IP(nil), MTU:0, AdvMSS:0, Priority:0, Table:(*int)(nil), Scope:(*int)(nil)}}, "type":"host-local"}, "isDefaultGateway":true, "isGateway":true, "mtu":(*uint)(0xc000012da0), "name":"cbr0", "type":"bridge"}
delegateAdd: netconf sent to delegate plugin:
{"cniVersion":"1.0.0","forceAddress":true,"hairpinMode":true,"ipMasq":false,"ipam":{"ranges":[[{"subnet":"10.42.0.0/24"}]],"routes":[{"dst":"10.42.0.0/16"}],"type":"host-local"},"isDefaultGateway":true,"isGateway":true,"mtu":1450,"name":"cbr0","type":"bridge"}time="2025-06-09T13:54:47.739780351Z" level=info msg="connecting to shim 0df27f3da93ae4b2060925f0b46765ea5a640818ed66a253ede79f9ef692937f" address="unix:///run/containerd/s/87b926dcfce9d008a3469935849a46796fbcda338d40806eedff804b19ba1537" namespace=k8s.io protocol=ttrpc version=3
time="2025-06-09T13:54:47.871389232Z" level=info msg="RunPodSandbox for &PodSandboxMetadata{Name:kubernetes-quickstart-6fcddbfb9c-c5dfl,Uid:934dff08-1a08-4059-acf2-9aeeabf38e66,Namespace:default,Attempt:0,} returns sandbox id \"0df27f3da93ae4b2060925f0b46765ea5a640818ed66a253ede79f9ef692937f\""
time="2025-06-09T13:54:47.878573835Z" level=info msg="PullImage \"localhost:8443/quarkus/kubernetes-quickstart:1.0.0-SNAPSHOT\""
time="2025-06-09T13:54:47.920286085Z" level=info msg="host will try HTTPS first since it is configured for HTTP with a TLS configuration, consider changing host to HTTPS or removing unused TLS configuration" host="localhost:8443"
time="2025-06-09T13:54:47.928344728Z" level=info msg="trying next host" error="failed to do request: Head \"https://localhost:8443/v2/quarkus/kubernetes-quickstart/manifests/1.0.0-SNAPSHOT\": tls: failed to verify certificate: x509: certificate relies on legacy Common Name field, use SANs instead" host="localhost:8443"
time="2025-06-09T13:54:47.941959873Z" level=info msg="fetch failed" error="failed to do request: Head \"https://localhost:8443/v2/quarkus/kubernetes-quickstart/manifests/1.0.0-SNAPSHOT\": tls: failed to verify certificate: x509: certificate relies on legacy Common Name field, use SANs instead" host="localhost:8443"
time="2025-06-09T13:54:47.945311013Z" level=error msg="PullImage \"localhost:8443/quarkus/kubernetes-quickstart:1.0.0-SNAPSHOT\" failed" error="rpc error: code = Unknown desc = failed to pull and unpack image \"localhost:8443/quarkus/kubernetes-quickstart:1.0.0-SNAPSHOT\": failed to resolve reference \"localhost:8443/quarkus/kubernetes-quickstart:1.0.0-SNAPSHOT\": failed to do request: Head \"https://localhost:8443/v2/quarkus/kubernetes-quickstart/manifests/1.0.0-SNAPSHOT\": tls: failed to verify certificate: x509: certificate relies on legacy Common Name field, use SANs instead"
time="2025-06-09T13:54:47.945388001Z" level=info msg="stop pulling image localhost:8443/quarkus/kubernetes-quickstart:1.0.0-SNAPSHOT: active requests=0, bytes read=0"
time="2025-06-09T13:55:01.988949930Z" level=info msg="PullImage \"localhost:8443/quarkus/kubernetes-quickstart:1.0.0-SNAPSHOT\""
time="2025-06-09T13:55:01.994710373Z" level=info msg="host will try HTTPS first since it is configured for HTTP with a TLS configuration, consider changing host to HTTPS or removing unused TLS configuration" host="localhost:8443"
time="2025-06-09T13:55:01.998882348Z" level=info msg="trying next host" error="failed to do request: Head \"https://localhost:8443/v2/quarkus/kubernetes-quickstart/manifests/1.0.0-SNAPSHOT\": tls: failed to verify certificate: x509: certificate relies on legacy Common Name field, use SANs instead" host="localhost:8443"
time="2025-06-09T13:55:02.003737260Z" level=info msg="fetch failed" error="failed to do request: Head \"https://localhost:8443/v2/quarkus/kubernetes-quickstart/manifests/1.0.0-SNAPSHOT\": tls: failed to verify certificate: x509: certificate relies on legacy Common Name field, use SANs instead" host="localhost:8443"
time="2025-06-09T13:55:02.007269920Z" level=error msg="PullImage \"localhost:8443/quarkus/kubernetes-quickstart:1.0.0-SNAPSHOT\" failed" error="rpc error: code = Unknown desc = failed to pull and unpack image \"localhost:8443/quarkus/kubernetes-quickstart:1.0.0-SNAPSHOT\": failed to resolve reference \"localhost:8443/quarkus/kubernetes-quickstart:1.0.0-SNAPSHOT\": failed to do request: Head \"https://localhost:8443/v2/quarkus/kubernetes-quickstart/manifests/1.0.0-SNAPSHOT\": tls: failed to verify certificate: x509: certificate relies on legacy Common Name field, use SANs instead"
time="2025-06-09T13:55:02.007369333Z" level=info msg="stop pulling image localhost:8443/quarkus/kubernetes-quickstart:1.0.0-SNAPSHOT: active requests=0, bytes read=0"
…

As you can see in the output above, the following error message is shown:

failed to do request: Head \"https://localhost:8443/v2/quarkus/kubernetes-quickstart/manifests/1.0.0-SNAPSHOT\": tls: failed to verify certificate: x509: certificate relies on legacy Common Name field, use SANs instead" host="localhost:8443

So, instead of using the Common Name (CN) field in the certificate, I had to use the Subject Alternative Name (SAN).

Quarkus - Supersonic Subatomic Java, trying out Quarkus guide “Quarkus - Kubernetes extension” (reinvestigated, part 3), using a local private registry with Quarkus and K3s lameriks 2025 06 57
In SSL/TLS certificates, the Common Name (CN) was traditionally used to identify the domain the certificate was issued for, but it’s now deprecated in favor of the Subject Alternative Name (SAN). SAN allows a certificate to secure multiple domain names, subdomains, or even IP addresses, while the CN can only hold one. Most modern browsers and systems prioritize SAN for validation, making it the primary method for identifying the domains a certificate covers.
[AI overview]
AI responses may include mistakes. Learn more

To create an OpenSSL certificate with Subject Alternative Names (SAN) of the “dnsName” type, you need to modify the OpenSSL configuration file and include the subjectAltName extension. This extension allows you to specify multiple DNS names that the certificate will be valid for, in addition to the common name (CN). 
[AI overview]
AI responses may include mistakes. Learn more

So, unfortunately, I had to change the certificate and also had to repeat a lot of the steps described in this article.

In order to change the certificate in the correct way, I searched on the Internet and found a solution that worked for me.

cd /mnt/mysharedfolder/registry/certs

openssl req -new -x509 -nodes -sha256 -days 365 -key domain.key -out domain.crt -subj "/C=NL/ST=Utrecht/L=Nieuwegein/O=AMIS Conclusion/OU=AMIS Technology Blog/CN=localhost" -addext "subjectAltName = DNS:localhost"

As, you can see in the command above, as I mentioned earlier, this time I pass all the certificate values via the command line.

This for me, was also the right moment to create a new shell script local-registry.sh, because of all the manual steps I had to repeat.

For now however, I will skip telling you about these repeating steps (for readability) and go straight to the outcome. You can read more about the steps I took to further automate setting up my demo environment (including the shell script local-registry.sh) in my next article.

I will however, first show you some of the details of the certificate. For that, I used the following command on the Linux Command Prompt:

cd /mnt/mysharedfolder/registry/certs

openssl x509 -in domain.crt -text -noout

With the following output:

Certificate:
    Data:
        Version: 3 (0x2)
        Serial Number:
            d7:09:83:1d:d1:e5:8c:62:12:8f:9b:82:d8:9a:bd:d6:9a:e2:ed
        Signature Algorithm: sha256WithRSAEncryption
        Issuer: C = NL, ST = Utrecht, L = Nieuwegein, O = AMIS Conclusion, OU = AMIS Technology Blog, CN = localhost
        Validity
            Not Before: Jun 11 14:46:37 2025 GMT
            Not After : Jun 11 14:46:37 2026 GMT
        Subject: C = NL, ST = Utrecht, L = Nieuwegein, O = AMIS Conclusion, OU = AMIS Technology Blog, CN = localhost
        Subject Public Key Info:
            Public Key Algorithm: rsaEncryption
                Public-Key: (2048 bit)
                Modulus:
                    00:b7:…1f:ed
                Exponent: 65537 (0x10001)
        X509v3 extensions:
            X509v3 Subject Key Identifier:
                E0:BC:C4:0E:57:6E:EA:E6:95:9C:19:92:E6:16:AD:3A:06:13:99:94
            X509v3 Authority Key Identifier:
                E0:BC:C4:0E:57:6E:EA:E6:95:9C:19:92:E6:16:AD:3A:06:13:99:94
            X509v3 Basic Constraints: critical
                CA:TRUE
            X509v3 Subject Alternative Name:
                DNS:localhost
    Signature Algorithm: sha256WithRSAEncryption
    Signature Value:
        9a:37:…:b0:1e

In order to apply the generated manifest kubernetes.yml to the Kubernetes cluster from the project root, again I used the following commands on the Linux Command Prompt:

cd /mnt/mysharedfolder/kubernetes-quickstart

kubectl apply -f target/kubernetes/kubernetes.yml

Then, in order the check whether the Pod was running successfully (without the need to use the Kubernetes Dashboard for this), again I used the following command on the Linux Command Prompt:

sudo cat /var/lib/rancher/k3s/agent/containerd/containerd.log

With the following output:

time="2025-06-27T09:43:22.077026056Z" level=info msg="RunPodSandbox for &PodSandboxMetadata{Name:kubernetes-quickstart-576f795bf5-9b8ss,Uid:1dfda00a-8f72-4104-9955-2fe9e48bdef1,Namespace:default,Attempt:0,}"

map[string]interface {}{"cniVersion":"1.0.0", "forceAddress":true, "hairpinMode":true, "ipMasq":false, "ipam":map[string]interface {}{"ranges":[][]map[string]interface {}{[]map[string]interface {}{map[string]interface {}{"subnet":"10.42.0.0/24"}}}, "routes":[]types.Route{types.Route{Dst:net.IPNet{IP:net.IP{0xa, 0x2a, 0x0, 0x0}, Mask:net.IPMask{0xff, 0xff, 0x0, 0x0}}, GW:net.IP(nil), MTU:0, AdvMSS:0, Priority:0, Table:(*int)(nil), Scope:(*int)(nil)}}, "type":"host-local"}, "isDefaultGateway":true, "isGateway":true, "mtu":(*uint)(0xc000012da0), "name":"cbr0", "type":"bridge"}
delegateAdd: netconf sent to delegate plugin:
{"cniVersion":"1.0.0","forceAddress":true,"hairpinMode":true,"ipMasq":false,"ipam":{"ranges":[[{"subnet":"10.42.0.0/24"}]],"routes":[{"dst":"10.42.0.0/16"}],"type":"host-local"},"isDefaultGateway":true,"isGateway":true,"mtu":1450,"name":"cbr0","type":"bridge"}time="2025-06-27T09:43:22.235256044Z" level=info msg="connecting to shim d2fbe3011a395e6c5108817daa8c7ea492d12b31fb1a7fc9d80fb4976b254cf4" address="unix:///run/containerd/s/aefec4338a2eb0270c229c329b2381e6e98623c590b5cc015e2f1aab20cc0c8d" namespace=k8s.io protocol=ttrpc version=3
time="2025-06-27T09:43:22.374980454Z" level=info msg="RunPodSandbox for &PodSandboxMetadata{Name:kubernetes-quickstart-576f795bf5-9b8ss,Uid:1dfda00a-8f72-4104-9955-2fe9e48bdef1,Namespace:default,Attempt:0,} returns sandbox id \"d2fbe3011a395e6c5108817daa8c7ea492d12b31fb1a7fc9d80fb4976b254cf4\""
time="2025-06-27T09:43:22.378298246Z" level=info msg="PullImage \"localhost:8443/quarkus/kubernetes-quickstart:1.0.0-SNAPSHOT\""
time="2025-06-27T09:43:22.390295144Z" level=info msg="host will try HTTPS first since it is configured for HTTP with a TLS configuration, consider changing host to HTTPS or removing unused TLS configuration" host="localhost:8443"
time="2025-06-27T09:43:22.409527551Z" level=info msg="host will try HTTPS first since it is configured for HTTP with a TLS configuration, consider changing host to HTTPS or removing unused TLS configuration" host="localhost:8443"
time="2025-06-27T09:43:22.465835335Z" level=info msg="ImageUpdate event name:\"localhost:8443/quarkus/kubernetes-quickstart:1.0.0-SNAPSHOT\"  labels:{key:\"io.cri-containerd.image\"  value:\"managed\"}"
time="2025-06-27T09:43:22.469616158Z" level=info msg="stop pulling image localhost:8443/quarkus/kubernetes-quickstart:1.0.0-SNAPSHOT: active requests=0, bytes read=0"
time="2025-06-27T09:43:22.471932377Z" level=info msg="Pulled image \"localhost:8443/quarkus/kubernetes-quickstart:1.0.0-SNAPSHOT\" with image id \"sha256:1fba157049f12c5eec3a66377a46d7b555813056a505573ecfa6b66bcdfe44df\", repo tag \"localhost:8443/quarkus/kubernetes-quickstart:1.0.0-SNAPSHOT\", repo digest \"localhost:8443/quarkus/kubernetes-quickstart@sha256:9e834834bdb7eea4be30186b9a5f485bf2424ec00fa1b22346f83a0bf4b03a0e\", size \"150143485\" in 93.597108ms"
time="2025-06-27T09:43:22.472029811Z" level=info msg="PullImage \"localhost:8443/quarkus/kubernetes-quickstart:1.0.0-SNAPSHOT\" returns image reference \"sha256:1fba157049f12c5eec3a66377a46d7b555813056a505573ecfa6b66bcdfe44df\""
time="2025-06-27T09:43:22.479358494Z" level=info msg="CreateContainer within sandbox \"d2fbe3011a395e6c5108817daa8c7ea492d12b31fb1a7fc9d80fb4976b254cf4\" for container &ContainerMetadata{Name:kubernetes-quickstart,Attempt:0,}"
time="2025-06-27T09:43:22.500518353Z" level=info msg="Container d48d9efadd9151f0fcdea7355fd0e1a2ed91fb7fda1fd9f46664e616aa5fc290: CDI devices from CRI Config.CDIDevices: []"
time="2025-06-27T09:43:22.519031563Z" level=info msg="CreateContainer within sandbox \"d2fbe3011a395e6c5108817daa8c7ea492d12b31fb1a7fc9d80fb4976b254cf4\" for &ContainerMetadata{Name:kubernetes-quickstart,Attempt:0,} returns container id \"d48d9efadd9151f0fcdea7355fd0e1a2ed91fb7fda1fd9f46664e616aa5fc290\""
time="2025-06-27T09:43:22.525966901Z" level=info msg="StartContainer for \"d48d9efadd9151f0fcdea7355fd0e1a2ed91fb7fda1fd9f46664e616aa5fc290\""
time="2025-06-27T09:43:22.527535601Z" level=info msg="connecting to shim d48d9efadd9151f0fcdea7355fd0e1a2ed91fb7fda1fd9f46664e616aa5fc290" address="unix:///run/containerd/s/aefec4338a2eb0270c229c329b2381e6e98623c590b5cc015e2f1aab20cc0c8d" protocol=ttrpc version=3
time="2025-06-27T09:43:22.683977329Z" level=info msg="StartContainer for \"d48d9efadd9151f0fcdea7355fd0e1a2ed91fb7fda1fd9f46664e616aa5fc290\" returns successfully"

So, again the Quarkus generated manifest kubernetes.yml, was applied to the Kubernetes cluster and created the following Kubernetes objects:

  • Service
  • Deployment
  • Replica Set
  • Pod

Most of these objects have kubernetes-quickstart as name (defined in kubernetes.yml), but the names of the Replica Set and Pod(s) are determined by Kubernetes.

In order to find out the name of the Replica Set, I used the following command on the Linux Command Prompt:

kubectl get replicasets

With the following output:

NAME                               DESIRED   CURRENT   READY   AGE
kubernetes-quickstart-576f795bf5   1         1         1       5m1s

In order to find out the name of the Pods, I used the following command on the Linux Command Prompt:

kubectl get pods

With the following output:

NAME                                     READY   STATUS    RESTARTS   AGE
kubernetes-quickstart-576f795bf5-9b8ss   1/1     Running   0          5m9s

Quarkus - Supersonic Subatomic Java, trying out Quarkus guide “Quarkus - Kubernetes extension” (reinvestigated, part 3), using a local private registry with Quarkus and K3s lameriks 2025 06 58

So, using the self-signed certificate (local-registry.pem) from Quarkus’s truststore to connect to the secured local private registry (for pushing the image) worked. Also pulling the image by K3s from the secured local private registry now worked 😊.

Kubernetes Dashboard

Next, in order to check the generated objects in Kubernetes, in the Web Browser on my Windows laptop, I started the Kubernetes Dashboard in my demo environment, via:

http://localhost:8001/api/v1/namespaces/kubernetes-dashboard/services/https:kubernetes-dashboard:/proxy/#/login

Quarkus - Supersonic Subatomic Java, trying out Quarkus guide “Quarkus - Kubernetes extension” (reinvestigated, part 3), using a local private registry with Quarkus and K3s lameriks 2025 06 59

I used the token, that was visible in the output of my script, via:

kubectl -n kubernetes-dashboard describe secret $(kubectl -n kubernetes-dashboard get secret | grep admin-user | awk '{print $1}')

The Kubernetes Dashboard was opened with the default namespace selected. Then, I navigated to the Services:

Quarkus - Supersonic Subatomic Java, trying out Quarkus guide “Quarkus - Kubernetes extension” (reinvestigated, part 3), using a local private registry with Quarkus and K3s lameriks 2025 06 60

Next, I navigated to the Deployments:

Quarkus - Supersonic Subatomic Java, trying out Quarkus guide “Quarkus - Kubernetes extension” (reinvestigated, part 3), using a local private registry with Quarkus and K3s lameriks 2025 06 61

Next, I navigated to the Replica Sets:

Quarkus - Supersonic Subatomic Java, trying out Quarkus guide “Quarkus - Kubernetes extension” (reinvestigated, part 3), using a local private registry with Quarkus and K3s lameriks 2025 06 62

Next, I navigated to the Pods:

Quarkus - Supersonic Subatomic Java, trying out Quarkus guide “Quarkus - Kubernetes extension” (reinvestigated, part 3), using a local private registry with Quarkus and K3s lameriks 2025 06 63

Then, I opened the Logs from the Pod:

Quarkus - Supersonic Subatomic Java, trying out Quarkus guide “Quarkus - Kubernetes extension” (reinvestigated, part 3), using a local private registry with Quarkus and K3s lameriks 2025 06 64

Above, you can see, the Quarkus application is listening on: http://0.0.0.0:8080

Then, I closed the Logs and navigated to the Events:

Quarkus - Supersonic Subatomic Java, trying out Quarkus guide “Quarkus - Kubernetes extension” (reinvestigated, part 3), using a local private registry with Quarkus and K3s lameriks 2025 06 65

In order to get the endpoint of the Pod, I navigated to the Services | kubernetes-quickstart:

Quarkus - Supersonic Subatomic Java, trying out Quarkus guide “Quarkus - Kubernetes extension” (reinvestigated, part 3), using a local private registry with Quarkus and K3s lameriks 2025 06 66

For the Pod I quickly checked if it worked.

I used the following command on the Linux Command Prompt:

curl http://10.42.0.48:8080/hello

With the following output:

Hello from Quarkus REST

For now this was enough. I knew my demo environment, including the secured local private registry, was working.

I conclude this article.

Some years ago, I also wrote articles about the Quarkus Kubernetes Extension using the default public Docker image registry. This time, I wanted to try out using a local private registry.

In this article, I shared with you the steps I took, to secure the local private registry I created earlier in my demo environment and using it with Quarkus and K3s (a lightweight certified Kubernetes distribution). I used the registry Docker Official Image, a Distribution implementation for storing and distributing of container images and artifacts.

In a next article, you can read more about the steps I took to further automate setting up my demo environment (including the shell script local-registry.sh).

Feel free, among my other articles on this subject, to read:

Leave a Reply

This site uses Akismet to reduce spam. Learn how your comment data is processed.