Installing CodeScoring.Save in Kubernetes

CodeScoring.Save is installed using the save Helm chart. The same chart supports two deployment scenarios:

  • a standard Kubernetes installation with PostgreSQL, Redis, and S3-compatible object storage;
  • a hosted installation in a single-node k3s cluster with SQLite and filesystem storage.

Before installation, review the system requirements.

Installing with the Helm chart

The vendor provides the Helm repository and container registry URLs and access credentials. The commands below use these placeholders:

  • HELM_REPOSITORY_URL — Helm repository URL;
  • REGISTRY_URL — container registry URL;
  • USERNAME and PASSWORD — access credentials;
  • CHART_VERSION — Helm chart and CodeScoring.Save version.
  1. Create a namespace:

    kubectl create namespace codescoring-save
  2. Create a Secret for accessing the private container registry:

    kubectl create secret docker-registry codescoring-regcred \
      --namespace codescoring-save \
      --docker-server=REGISTRY_URL \
      --docker-username=USERNAME \
      --docker-password=PASSWORD

    The Secret name must match image.pullSecrets and osa-proxy.imagePullSecrets in values.yaml.

  3. Install Helm and add the CodeScoring.Save repository:

    helm repo add codescoring-save HELM_REPOSITORY_URL \
      --username USERNAME \
      --password PASSWORD
    helm repo update
  4. Download and unpack the chart:

    helm pull codescoring-save/save \
      --version CHART_VERSION \
      --untar \
      --untardir codescoring-save-src
    cd codescoring-save-src/save

Configuring Helm chart values

Important

Make the required changes before the first installation. Before upgrading an existing installation, back up PostgreSQL and the object storage.

The complete installation configuration is stored in values.yaml. The examples below include only the fragments to change; preserve the remaining settings from the distributed file.

Container images

The main components use a shared registry, tag, and image pull Secret:

image:
  registry: REGISTRY_URL/save-docker
  tag: CHART_VERSION
  pullSecrets:
    - name: codescoring-regcred

OSA Proxy is distributed as a chart dependency and has separate image settings:

osa-proxy:
  image:
    repository: REGISTRY_URL/save-docker/osa-proxy
    tag: OSA_PROXY_VERSION
  imagePullSecrets:
    - name: codescoring-regcred

Use the image versions specified in the distributed values.yaml; they are tested for compatibility with the selected chart version.

Application secrets

Sensitive backend, worker, scheduler, and auth settings are stored in secrets.save-secrets.data:

secrets:
  save-secrets:
    enabled: true
    data:
      SECRETS_ENCRYPTION_KEY: <STABLE_ENCRYPTION_KEY>
      DATABASE_NAME: save_db
      DATABASE_USER: save_user
      DATABASE_PASSWORD: <DATABASE_PASSWORD>
      REDIS_PASSWORD: ""
      S3_ACCESS_KEY: <S3_ACCESS_KEY>
      S3_SECRET_KEY: <S3_SECRET_KEY>
      AUTH_INTERNAL_SECRET: <AUTH_INTERNAL_SECRET>
      AUTH_ADMIN_PASSWORD: <ADMIN_PASSWORD>

SECRETS_ENCRYPTION_KEY encrypts stored credentials. Generate it once, for example with openssl rand -base64 32, store it securely, and do not change it during upgrades. Previously stored data cannot be decrypted after the key is changed.

Do not keep production passwords and keys in version control. For a production installation, use a protected values file or External Secrets.

PostgreSQL and Redis from the Helm chart

By default, the chart creates PostgreSQL and Redis as StatefulSets. Enable their resources and configure the PersistentVolumeClaims:

statefulSets:
  save-postgresql:
    enabled: true
  save-redis:
    enabled: true

services:
  redis:
    enabled: true

pvcs:
  save-postgresql:
    enabled: true
    accessModes:
      - ReadWriteOnce
    size: 20Gi
    storageClassName: "default"
  save-redis:
    enabled: true
    accessModes:
      - ReadWriteOnce
    size: 2Gi
    storageClassName: "default"

Replace storageClassName and the volume sizes according to the cluster configuration and expected data volume.

The built-in PostgreSQL credentials are configured separately and must match DATABASE_NAME, DATABASE_USER, and DATABASE_PASSWORD in secrets.save-secrets.data:

secrets:
  postgresql-secrets:
    enabled: true
    data:
      POSTGRES_DB: save_db
      POSTGRES_USER: save_user
      POSTGRES_PASSWORD: <DATABASE_PASSWORD>

Application connection settings for PostgreSQL and Redis are stored in the save-backend-envs ConfigMap:

configMaps:
  save-backend-envs:
    enabled: true
    data:
      DATABASE_DRIVER: "postgres"
      DATABASE_HOST: "save-postgresql"
      DATABASE_PORT: "5432"
      REDIS_ENABLED: "true"
      REDIS_ADDR: "save-redis:6379"
      REDIS_DB: "0"

The ConfigMap and Secret are already attached to the components through deploymentsGeneral.envConfigmaps and deploymentsGeneral.envSecrets. Preserve these references when editing the complete values.yaml:

deploymentsGeneral:
  envConfigmaps:
    - save-backend-envs
  envSecrets:
    - save-secrets

External PostgreSQL and Redis

For an external PostgreSQL server, disable the built-in StatefulSet and PVC and change the connection settings:

statefulSets:
  save-postgresql:
    enabled: false

pvcs:
  save-postgresql:
    enabled: false

configMaps:
  save-backend-envs:
    data:
      DATABASE_DRIVER: "postgres"
      DATABASE_HOST: "postgresql.example.com"
      DATABASE_PORT: "5432"

secrets:
  save-secrets:
    data:
      DATABASE_NAME: save_db
      DATABASE_USER: save_user
      DATABASE_PASSWORD: <DATABASE_PASSWORD>

Create the database and user before installing CodeScoring.Save.

For an external Redis server, disable the built-in StatefulSet, Service, and PVC:

statefulSets:
  save-redis:
    enabled: false

services:
  redis:
    enabled: false

pvcs:
  save-redis:
    enabled: false

configMaps:
  save-backend-envs:
    data:
      REDIS_ENABLED: "true"
      REDIS_ADDR: "redis.example.com:6379"
      REDIS_DB: "0"

secrets:
  save-secrets:
    data:
      REDIS_PASSWORD: <REDIS_PASSWORD>

S3-compatible object storage

The current chart does not deploy an S3-storage. For a standard Kubernetes installation, create a bucket in an external S3-compatible storage service before installation and specify its connection settings:

configMaps:
  save-backend-envs:
    data:
      STORAGE_TYPE: "s3"
      S3_REGION: "us-east-1"
      S3_BUCKET: "save"
      S3_ENDPOINT: "https://s3.example.com"
      S3_FORCE_PATH_STYLE: "true"

secrets:
  save-secrets:
    data:
      S3_ACCESS_KEY: <S3_ACCESS_KEY>
      S3_SECRET_KEY: <S3_SECRET_KEY>

For storage services that support virtual-hosted-style URLs, set S3_FORCE_PATH_STYLE: "false".

Hosted installation in k3s

The hosted option is intended for a single-server k3s installation and uses SQLite for metadata and the local filesystem for artifacts.

Install k3s and configure cluster access:

curl -sfL https://get.k3s.io | sh -
sudo systemctl status k3s

mkdir -p ~/.kube
sudo cp /etc/rancher/k3s/k3s.yaml ~/.kube/config
sudo chown "$USER:$USER" ~/.kube/config
export KUBECONFIG=~/.kube/config

kubectl get nodes

To switch the standard chart profile to hosted, change three variables in configMaps.save-backend-envs.data:

configMaps:
  save-backend-envs:
    data:
      DATABASE_DRIVER: "sqlite"
      DATABASE_SQLITE_PATH: "./data/repository_manager.db"
      STORAGE_TYPE: "filesystem"

The save-backend-envs ConfigMap is attached to backend, worker, scheduler, and auth, so no additional ConfigMap is required. Preserve the other settings from the distributed values.yaml.

The worker and scheduler components require PostgreSQL and do not support SQLite. Both deployments must be disabled in a hosted installation:

deployments:
  worker:
    enabled: false
  scheduler:
    enabled: false

Persistent data storage

A hosted installation requires three separate persistent volumes:

  • db-data — the backend SQLite database;
  • auth-data — the auth SQLite database;
  • storage — filesystem artifact storage.

Attach the PVCs to the deployments through volumes and volumeMounts. For auth, also set an empty initContainers value to remove the inherited wait-for-postgres init container:

deployments:
  save:
    containers:
      save:
        volumeMounts:
          - name: db-data
            mountPath: /app/data
          - name: storage
            mountPath: /app/storage
    volumes:
      - name: db-data
        type: pvc
      - name: storage
        type: pvc

  auth:
    initContainers:
    containers:
      auth:
        volumeMounts:
          - name: auth-data
            mountPath: /app/data
    volumes:
      - name: auth-data
        type: pvc

Backend and auth use the same DATABASE_SQLITE_PATH value but separate PVCs, so each service stores its own database in its /app/data directory. The default STORAGE_ROOT_PATH is ./storage, which corresponds to the /app/storage mount point when the working directory is /app.

For Dynamic Volume Provisioning, specify a StorageClass for each PVC. The corresponding PVs will be created automatically:

pvcs:
  db-data:
    accessModes:
      - ReadWriteOnce
    storageClassName: "default"
    size: 5Gi
  auth-data:
    accessModes:
      - ReadWriteOnce
    storageClassName: "default"
    size: 1Gi
  storage:
    accessModes:
      - ReadWriteOnce
    storageClassName: "default"
    size: 10Gi

Choose the volume sizes and storageClassName according to the workload and cluster configuration.

You can also use pre-created PVs of any Kubernetes-supported type. In this case, create the PVs separately or describe them under pvs, and set an empty storageClassName and the corresponding volumeName in each PVC, for example:

pvs:
  db-data:
    accessModes:
      - ReadWriteMany
    size: 5Gi
    storageClassName: ""
    mountOptions:
      - nfsvers=4.2
    nfs:
      server: nfs.example.com
      path: /exports/codescoring-save/db-data

pvcs:
  db-data:
    accessModes:
      - ReadWriteMany
    storageClassName: ""
    volumeName: codescoring-save-db-data
    size: 5Gi

In this example, for the codescoring-save Helm release, the chart creates the codescoring-save-db-data PV from pvs.db-data, and the PVC binds to it explicitly through volumeName. Replace the NFS server address and export path with values from your infrastructure.

Create and bind the auth-data and storage volumes in the same way. The volume source can be NFS, CSI, a local disk, or another storage system; its parameters depend on the cluster infrastructure.

Optional Redis

Redis is optional for a hosted installation. To run Save without Redis, disable it in the application configuration and disable the related StatefulSet, Service, and PVC:

configMaps:
  save-backend-envs:
    data:
      REDIS_ENABLED: "false"

statefulSets:
  save-redis:
    enabled: false

services:
  redis:
    enabled: false

pvcs:
  save-redis:
    enabled: false

If Redis is used, keep REDIS_ENABLED: "true", and leave its StatefulSet, Service, and PVC enabled.

SQLite supports a single replica. Do not increase deploymentsGeneral.replicas, and disable autoscaling for backend and worker:

deploymentsGeneral:
  replicas: 1

hpas:
  save:
    enabled: false
  worker:
    enabled: false

The application no longer uses PostgreSQL after this switch. You can also disable its unused StatefulSet and PVC:

statefulSets:
  save-postgresql:
    enabled: false

pvcs:
  save-postgresql:
    enabled: false
Important

SQLite and filesystem storage are suitable only for a single-server installation. Keep the SQLite database and artifact directories on persistent k3s storage and back them up regularly. Do not use this profile for a multi-replica deployment.

Ingress

Ingress is disabled by default. Enable it and replace the domain name to expose the web interface and API:

ingresses:
  ingress:
    enabled: true
    ingressClassName: nginx
    hosts:
      - hostname: save.example.com
        paths:
          - path: /
            serviceName: frontend
            servicePort: 8081
    tls:
      - hosts:
          - save.example.com
        secretName: save-tls

configMaps:
  save-frontend-envs:
    data:
      NGINX_HOST: "save.example.com"
      NGINX_PORT: "8081"

If TLS is terminated by an external load balancer, configure tls and the Ingress annotations according to the cluster infrastructure.

HTTPRoute (Gateway API)

If the cluster uses Kubernetes Gateway API, configure a route to the frontend through the httpRoutes section. The referenced Gateway resource must exist before CodeScoring.Save is installed.

httpRoutesGeneral: {}
httpRoutes:
  save:
    gatewayName: codescoring-gateway
    gatewayNamespace: gateway-system
    gatewaySectionName: https
    gatewayKind: Gateway
    gatewayGroup: gateway.networking.k8s.io
    hostnames:
      - "save.example.com"
    rules:
      - matches:
          - path:
              type: PathPrefix
              value: /
        backendRefs:
          - name: codescoring-save-frontend
            port: 8081

configMaps:
  save-frontend-envs:
    data:
      NGINX_HOST: "save.example.com"
      NGINX_PORT: "8081"

Set gatewayName, gatewayNamespace, gatewaySectionName, and hostnames to match the Gateway and DNS name in your cluster.

Important

The backendRefs.name value must be formed as {helm-release-name}-frontend. For example, with helm install codescoring-save ..., specify name: codescoring-save-frontend.

OSA Proxy

OSA Proxy is disabled by default. To enable it set the osa-proxy.enabled field to true. In the existing osa-proxy.config.content block, replace codescoring.url with the CodeScoring installation URL. Pass the token through the osa-proxy Secret:

osa-proxy:
  enabled: true
  secret:
    create: true
    stringData:
      CODESCORING_TOKEN: <CODESCORING_TOKEN>

If component checks through OSA Proxy are not required, disable the dependency:

osa-proxy:
  enabled: false

osa-proxy.config.content contains the complete service configuration. Preserve the remaining sections from the distributed values.yaml when modifying it.

Resource limits

The distributed values.yaml does not limit the main component containers. For a production installation, set requests and limits for every component based on load testing:

deployments:
  save:
    containers:
      save:
        resources:
          requests:
            cpu: 500m
            memory: 1Gi
          limits:
            cpu: "2"
            memory: 4Gi
  worker:
    containers:
      worker:
        resources:
          requests:
            cpu: 500m
            memory: 1Gi
          limits:
            cpu: "2"
            memory: 4Gi

Configure deployments.scheduler, deployments.auth, deployments.save-frontend, statefulSets.save-postgresql, and statefulSets.save-redis in the same way. CPU requests must be defined for HPA to work correctly with scalable containers.

External Secrets

The chart can obtain secrets from an external store through External Secrets Operator. Install the operator and create a SecretStore or ClusterSecretStore in the cluster first.

Enable the required resource under vaults:

vaults:
  save-secrets-external:
    apiVersion: external-secrets.io/v1
    enabled: true
    store:
      name: vault-backend
      kind: ClusterSecretStore
    path: save-secrets

Then attach the generated Secret to the deployments:

deploymentsGeneral:
  envConfigmaps:
    - save-backend-envs
  envSecrets:
    - save-secrets-external

For the built-in PostgreSQL, configure vaults.postgresql-secrets-external in the same way and attach the Secret under statefulSets.save-postgresql.envSecrets.

Installation

From the unpacked chart directory, run:

helm install codescoring-save . \
  --namespace codescoring-save \
  --values values.yaml \
  --atomic

To update an existing installation, use the same configured values.yaml version:

helm upgrade codescoring-save . \
  --namespace codescoring-save \
  --values values.yaml \
  --atomic

Verifying the installation

Check the resource status:

kubectl get pods -n codescoring-save
kubectl get statefulset,pvc -n codescoring-save
kubectl get service,ingress -n codescoring-save

All Pods must reach the Running state and their containers must be ready. Use component logs for diagnostics:

kubectl logs -n codescoring-save -l app=backend --tail=100
kubectl logs -n codescoring-save -l app=worker --tail=100
kubectl logs -n codescoring-save -l app=scheduler --tail=100
kubectl logs -n codescoring-save -l app=auth --tail=100
kubectl logs -n codescoring-save -l app=frontend --tail=100

After installation, open the Ingress URL and sign in with the password specified in AUTH_ADMIN_PASSWORD.

Next steps

After a successful installation:

  1. Create the first repository.
  2. Configure cleanup policies.
  3. Create users and assign roles.
Was this page helpful?