Deploy SignServer in Kubernetes

The following provides an example of deploying SignServer to a Kubernetes cluster, integrating it with an external MariaDB database, and worker configuration for a production-ready setup.

Prerequisites

Before you begin, ensure the following are in place. See Prerequisites for full version requirements.

  • Kubernetes v1.32+ with kubectl configured

  • Helm v3+

  • A Certificate Authority, EJBCA or another CA, for infrastructure certificates (TLS, admin client auth) and signer certificates.

  • An external database for any non-ephemeral deployment (MariaDB, MySQL, PostgreSQL, Oracle, or SQL Server)

  • An HSM for production key storage. See HSM Integration for supported options.

To access the Admin Web, you need a client certificate installed in your browser. If you do not have one yet, follow Issue Client Authentication Certificate using EJBCA to get started.


If you want to verify your cluster setup before deploying, follow the Quick Test Deployment guide.

Step 1 - Fetch the Helm Chart

Pull the SignServer Community chart and unpack it locally so you can inspect and customize the default values:

helm pull oci://repo.keyfactor.com/charts/signserver-ce --untar

For SignServer Enterprise, use the Enterprise chart instead:

helm pull oci://repo.keyfactor.com/charts/signserver --untar

Step 2 - Create a Custom Values File

For any deployment beyond the ephemeral test, create a custom values file to configure SignServer for your environment:

helm show values signserver-ce > signserver.yaml

Edit signserver.yaml to add the configuration described in the sections below, then deploy with:

helm install signserver signserver-ce \
    --namespace signserver \
    --create-namespace \
    --values signserver.yaml

Step 3 - Configure an External Database

All production deployments should use an external database. SignServer supports MariaDB/MySQL, PostgreSQL, Oracle, and SQL Server.

Disable the ephemeral H2 database and set the connection details in your values file:

MariaDB Example:

YAML
signserver:
  useEphemeralH2Database: false
  env:
    DATABASE_JDBC_URL: jdbc:mariadb://mariadb-server:3306/signserverdb?characterEncoding=UTF-8
    DATABASE_USER: signserver
    DATABASE_PASSWORD: foo123

PostgreSQL Example using a Kubernetes Secret for Credentials:

YAML
signserver:
  useEphemeralH2Database: false
  env:
    DATABASE_JDBC_URL: jdbc:postgresql://postgresql-server:5432/signserverdb
  envRaw:
    - name: DATABASE_PASSWORD
      valueFrom:
        secretKeyRef:
          name: signserver-db-credentials
          key: database_password
    - name: DATABASE_USER
      valueFrom:
        secretKeyRef:
          name: signserver-db-credentials
          key: database_user

Using Kubernetes secrets for credentials is recommended over plaintext values in production. Bitnami Helm charts can deploy a database into your cluster if needed: PostgreSQL · MariaDB.

For more information, see Connect SignServer to External Database.

Step 4 - Configure a Reverse Proxy (Optional)

Options for using a reverse proxy include:

  • Built-in NGINX Reverse Proxy

  • External Reverse Proxy with AJP

  • External Reverse Proxy with HTTP Back-end Ports

  • TLS Termination

For configuration information, see Configure a Reverse Proxy in SignServer.

This example continues with the built-in NGINX reverse proxy.

Step 5 - Prepare Deployment Parameters

To deploy SignServer, first prepare the deployment parameters by creating a YAML configuration file, and then use the Helm Chart with your values file to install SignServer to a Kubernetes cluster.

Kubernetes has announced the retirement of Ingress NGINX. The ingress parameters remain available for backward compatibility, but are not recommended for new deployments. NGINX Reverse Proxy is the recommended replacement. See Configure a Reverse Proxy in SignServer.

Create an signserver.yaml configuration file with the following content. If using the built-in NGINX reverse proxy, make sure to create the Kubernetes secrets first. See Option A: Built in NGINX Reverse Proxy.

YAML
signserver:
  useEphemeralH2Database: false
  env:
    DATABASE_JDBC_URL: "jdbc:mariadb://mariadb:3306/signserver?characterEncoding=utf8"
    DATABASE_USER: signserver
  envRaw:
    - name: DATABASE_PASSWORD
      valueFrom:
        secretKeyRef:
          name: mariadb-passwords
          key: mariadb-password
services:
  proxyHttp:
    enabled: true
    type: ClusterIP
    bindIP: 0.0.0.0
    httpPort: 8081
    httpsPort: 8082
    
nginx:
  image: nginx:1.27.1
  enabled: true
  initializeWithSelfSignedTls: false
  host: "signserverhost"
  externalConfiguration:
  # Server TLS credential for cluster internal communication
  mountInternalNginxCert: true
  secretInternalNginxCert: "managementca-secret"
  service:
    type: LoadBalancer
    bindIP: 0.0.0.0
    httpPort: 80
    httpsPort: 443
  # only relevant if multiple replicas
  loadBalancerAccess:
    enableStickySessionClientIp: false
    # create a load balancer service for each Pod with separate IP address
    enableReplicaSpecificAccess: false
  additionalHosts:
 
#imagePullSecrets:
#  - name: keyfactor-registry

For a description of the supported parameters, see SignServer Helm Deployment Parameters.

Customize Default Deploy Properties (Optional)

To customize the default deploy properties in your SignServer deployment, override the default properties by mounting in your own signserver_deploy.properties file version from a ConfigMap.

Note: Before version 7.8.0 the container bundles its own signserver_deploy.properties file containing some mandatory properties. To set one or more properties one would need to make sure to also include those mandatory properties. Since 7.8.0 it is enough to mount in a properties file with only the wanted properties.

  1. (Required only for SignServer version before 7.8.0:)
    Obtain the default properties file from /opt/keyfactor/signserver-custom/conf/signserver_deploy.properties within the container and modify as needed.

  2. Create a ConfigMap with your custom properties file.

    kubectl create configmap custom-signserver-deploy-properties --from-file=/path/to/signserver_deploy.properties
    
  3. Update your values.yaml to mount the ConfigMap.

    Add the following configuration to mount the custom deploy properties file into the SignServer container:

    YAML
    signserver:
      volumes:
        - name: signserver-deploy-properties
          configMap:
            name: custom-signserver-deploy-properties
      volumeMounts:
        - name: signserver-deploy-properties
          mountPath: /opt/keyfactor/signserver-custom/conf/signserver_deploy.properties
          subPath: signserver_deploy.properties
    

Configure OIDC (Optional)

OpenID Connect (OIDC) authentication for SignServer can be configured in two ways: by mounting an oidc.properties file from a ConfigMap, or by setting OIDC environment variables through Helm values. The two approaches can be combined. Environment variables take precedence over values in the mapped oidc.properties file and overwrite them when both are present.

For OIDC parameters, see SignServer Helm Deployment Parameters.

Option A - Configure OIDC Using a Properties File

  1. Create or modify the oidc.properties file with the desired OIDC settings.

  2. Create a ConfigMap with the properties file.

    kubectl create configmap signserver-oidc-properties --from-file=oidc.properties=/path/to/oidc.properties
    
  3. Update your values.yaml to mount the ConfigMap.

    Add the following configuration to mount the OIDC properties file into the SignServer container:

    YAML
    signserver:
      volumes:
        - name: signserver-oidc-properties
          configMap:
            name: signserver-oidc-properties
      volumeMounts:
        - name: signserver-oidc-properties
          mountPath: /opt/keyfactor/signserver/conf/oidc.properties
          subPath: oidc.properties
    

Notes:

  • The file is mounted as oidc.properties in the container.

  • You can add this mount alongside other configuration mounts.

  • Keep sensitive OIDC values in Kubernetes secrets where possible, and reference them from the properties file or environment variables as appropriate.

Option B - Configure OIDC Using Environment Variables

OIDC environment variables can be used to configure OpenID Connect authentication for SignServer through Helm values. These variables can be set in signserver.env, while sensitive values such as the client secret should preferably be provided through envRaw using a Kubernetes secret.

OIDC environment variables can be used alongside a mapped oidc.properties file. Values defined through Helm environment variables take precedence over values in the mapped oidc.properties file and overwrite them when both are present.

Step 6 - Deploy

Once your signserver.yaml is configured, use the Helm Chart with your values file to install SignServer to a Kubernetes cluster.

Community Edition:

Bash
helm install signserver-ce -f signserver.yaml \
    oci://repo.keyfactor.com/charts/signserver-ce --version x.y.z

Enterprise Edition:

helm install signserver -f signserver.yaml \
    oci://repo.keyfactor.com/charts/signserver --version x.y.z

Wait for the installation to finish. You can use the following command to monitor the pod creation process:

kubectl get pods -w -l app.kubernetes.io/instance=signserver

Proceed once all pods are ready (1/1).

NAME       READY   STATUS              RESTARTS   AGE
signserver...   0/1     Pending             0          0s
signserver...   0/1     ContainerCreating   0          0s
signserver...   0/1     Running             0          20s
signserver...   1/1     Running             0          40s

Step 7 - Access the Admin Web

Finalize the installation by importing the administrator certificate into your browser before navigating to the Admin Web.

The process of importing a certificate may vary depending on the web browser you are using. The following example outlines how to import a certificate into Mozilla Firefox.

To import the downloaded super administrator P12 keystore in Firefox:

  1. Open Settings → Privacy & Security → View Certificates.

  2. On the Your Certificates tab, click Import.

  3. Browse to your .p12 file, click OK, and enter the certificate password.

Navigate to https://signserver.example.com/signserver/adminweb/ and select the administrator certificate when prompted.

Step 8 - Configure Workers

Before configuring the SignServer Workers, import the keystore files containing signer keys and certificates the Workers will use.

Import Signer Keystores

Keystore files can be mounted into the SignServer container from a Kubernetes secret:

kubectl create secret generic signer-keystores-secret \
    --from-file=signer_keystore.p12=signer_keystore.p12

Configure the chart to mount keystore files from the secret. The keystoresMountPath is the location to place the files in the container:

YAML
signserver:
  importKeystores: true
  keystoresSecret: signer-keystores-secret
  keystoresMountPath: /mnt/external

Configure Workers Using Properties Files

Workers can be fully configured from a properties file at startup, without manual Admin Web configuration. This is the recommended approach for repeatable, automated deployments.

The following example configures a Crypto Worker backed by a PKCS#12 keystore and a Plain Signer that uses the key signKey0001 from this keystore:

WORKER1.NAME=SignerCryptoToken
WORKER1.TYPE=CRYPTO_WORKER
WORKER1.IMPLEMENTATION_CLASS=org.signserver.server.signers.CryptoWorker
WORKER1.CRYPTOTOKEN_IMPLEMENTATION_CLASS=org.signserver.server.cryptotokens.KeystoreCryptoToken
WORKER1.KEYSTORETYPE=PKCS12
WORKER1.KEYSTOREPATH=/mnt/external/signer_keystore.p12
WORKER1.KEYSTOREPASSWORD=foo123
WORKER1.DEFAULTKEY=testKey

WORKER2.NAME=PlainSigner
WORKER2.TYPE=PROCESSABLE
WORKER2.IMPLEMENTATION_CLASS=org.signserver.module.cmssigner.PlainSigner
WORKER2.CRYPTOTOKEN=SignerCryptoToken
WORKER2.DEFAULTKEY=signKey0001
WORKER2.DISABLEKEYUSAGECOUNTER=true
WORKER2.AUTHTYPE=NOAUTH

Sample properties files for different Worker types are available in the SignServer GitHub repository.

Use explicit Worker IDs. The sample properties files use the prefix WORKERGENID1 to always create a new Worker. In order to handle container restarts, exact Worker ID should be used like in the example above, to avoid overwriting the properties of an existing Worker.

Create a secret from one or more text files with Worker properties:

kubectl create secret generic workers-secret \
    --from-file=workers.properties=workers.properties

Configure the chart to import Worker properties at start-up:

YAML
signserver:
  importWorkerProperties: true
  workerPropertiesSecret: workers-secret

Next Steps