Trust a custom CA for outbound connections on Kubernetes

For the complete documentation index, see llms.txt. For a full content snapshot, see llms-full.txt. Append .md to any kestra.io/docs/* URL for plain Markdown.

Add a self-signed or internal CA certificate to the JVM truststore so Kestra tasks can make outbound calls to services secured by that CA.

This is common in environments where internal services — such as databases, APIs, or artifact registries — use a private or self-signed certificate authority not included in the JVM’s default truststore. Without this configuration, tasks that connect to those services fail with:

javax.net.ssl.SSLHandshakeException: PKIX path building failed: unable to find valid certification path to requested target

Prerequisites

You need kubectl, keytool (bundled with the JDK), and helm.

The commands use the following variables:

VariableDescription
$NAMESPACEKubernetes namespace where Kestra is running
$WEBSERVER_POD_NAMEName of a running Kestra pod (e.g., the webserver or executor)
$ALIAS_FOR_YOUR_DOMAINUnique alias for your certificate (e.g., my-internal-ca)
$RELEASE_NAMEHelm release name for your Kestra installation

Steps

  1. Prepare a working directory:

    mkdir ssl
    cp /path/to/self-signed-certificate.pem ./ssl/
  2. Retrieve the base keystore from the running pod: Pull the default Java truststore (cacerts) from a running pod to preserve all standard public CAs while you add your own.

    kubectl get po -n $NAMESPACE
    WEBSERVER_POD_NAME=<pod-name-from-above>
    kubectl cp $NAMESPACE/$WEBSERVER_POD_NAME:/opt/java/openjdk/lib/security/cacerts ./ssl/cacerts
    ls -l ./ssl
  3. Import and convert the certificate: Import your PEM file into the downloaded truststore:

    keytool -importcert \
    -trustcacerts \
    -file ssl/self-signed-certificate.pem \
    -keystore ssl/cacerts \
    -alias $ALIAS_FOR_YOUR_DOMAIN \
    -storepass changeit \
    -noprompt

    Convert the modified keystore to PKCS12 format:

    keytool -importkeystore \
    -srckeystore ssl/cacerts \
    -destkeystore ssl/truststore.p12 \
    -deststoretype PKCS12 \
    -srcstorepass changeit \
    -deststorepass changeit

    Verify your alias is present in the new truststore:

    keytool -list -keystore ssl/truststore.p12 -storetype PKCS12 -storepass changeit | grep $ALIAS_FOR_YOUR_DOMAIN

    If the command returns a line containing your alias, the import was successful.

  4. Create a Kubernetes Secret:

    kubectl create secret generic kestra-ssl \
    --from-file=truststore.p12=ssl/truststore.p12 \
    -n $NAMESPACE

    The --from-file=key=path syntax sets the key name inside the Secret to truststore.p12, so the mounted filename matches the JVM path configured in step 5.

  5. Configure Helm: Add the following to your values.yaml to mount the secret and point the JVM to it:

    common:
    extraVolumeMounts:
    - name: ssl-secret
    mountPath: "/app/ssl"
    readOnly: true
    extraVolumes:
    - name: ssl-secret
    secret:
    secretName: kestra-ssl
    extraEnv:
    - name: JAVA_OPTS
    value: >-
    -Djavax.net.ssl.trustStore=/app/ssl/truststore.p12
    -Djavax.net.ssl.trustStorePassword=changeit
    -Djavax.net.ssl.trustStoreType=PKCS12

    Then apply the changes:

    helm upgrade --install $RELEASE_NAME kestra/kestra -n $NAMESPACE -f /path/to/values.yaml

Was this page helpful?