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.
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/]
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:
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=quarkusquarkus.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"]}
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).
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
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:
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:
Next, I navigated to the Deployments:
Next, I navigated to the Replica Sets:
Next, I navigated to the Pods:
Then, I opened the Logs from the Pod:
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:
In order to get the endpoint of the Pod, I navigated to the Services | kubernetes-quickstart:
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:
- “Quarkus – Supersonic Subatomic Java, trying out Quarkus guide “Quarkus – Kubernetes extension” (part 1)”, September 27, 2020
In this article, you can read more about the steps I took, trying out the Quarkus code guide “Quarkus – Kubernetes extension”, and more specific the steps related to using custom labels and annotations. - “Quarkus – Supersonic Subatomic Java, trying out some Quarkus code guides (part2)”, October 3, 2020
In this article, you can read more about the steps I took, trying out the Quarkus code guide “Quarkus – Kubernetes extension”, and more specific the steps related to customizing the namespace, the service account name, the number of replicas and the type of service. - “Quarkus – Supersonic Subatomic Java, trying out Quarkus guide “Quarkus – Kubernetes extension (part 3)”, October 10, 2020
In this article, you can read more about the steps I took, trying out the Quarkus code guide “Quarkus – Kubernetes extension”, and more specific the steps related the automatic deployment of the generated resources to a target platform, in my case K3s (lightweight certified Kubernetes distribution).