> For the complete documentation index, see [llms.txt](https://docs.wproofreader.com/llms.txt). Markdown versions of documentation pages are available by appending `.md` to page URLs; this page is available as [Markdown](https://docs.wproofreader.com/v6.18.1/deployment/installation/kubernetes/install-wproofreader-server-only.md).

# Install WProofreader Server only

Install only WProofreader Server on Kubernetes with Helm, without Admin-panel and MySQL.

Use this guide to install WProofreader Server (AppServer) on Kubernetes without Admin-panel or a database. Applications send spell check and grammar check requests directly to WProofreader Server.

You manage the server with configuration files and APIs. See [Application server](/v6.18.1/deployment/configuration/application-server.md) and the [API reference](/v6.18.1/api-reference/overview.md). To manage the server through Admin-panel, use the [full stack guide](/v6.18.1/deployment/installation/kubernetes.md).

WProofreader Server can process requests without a database. The [wproofreader-helm](https://github.com/WebSpellChecker/wproofreader-helm) chart disables the database connection and db-manager by default. This guide keeps both settings disabled.

{% hint style="info" %}
Text in angle brackets is a placeholder, for example `<host>`. Replace the complete placeholder, including the brackets.
{% endhint %}

### What you need

* A Kubernetes cluster, version 1.27 or later.
* Permission to create a namespace and manage Deployments, Services, ServiceAccounts, Secrets, and temporary Pods in it.
* For persistent storage, permission to manage PersistentVolumeClaims. For external access, permission to manage Ingresses or HTTPRoutes.
* A workstation with `kubectl`, Helm 3.15 or later, and `git`.
* A WProofreader license ticket ID.
* Outbound internet access from the cluster. WProofreader Server uses it to activate the license. See [Internet connectivity requirements](/v6.18.1/deployment/installation-requirements/internet-connectivity-requirements.md).
* Access from the cluster to Docker Hub or to your container registry. By default, the chart uses the public `webspellchecker/wproofreader` image, which is for evaluation only. For production, use your own image. See [Use your own WProofreader Server image](#use-your-own-wproofreader-server-image).
* Enough CPU and memory for WProofreader Server. See [Hardware requirements](/v6.18.1/deployment/installation-requirements/hardware-requirements.md).

### Install WProofreader Server

{% stepper %}
{% step %}

#### Check the tools

```bash
kubectl version --client
helm version --short
git --version
```

Each command shows a version. Helm must show version 3.15 or later.
{% endstep %}

{% step %}

#### Select the cluster

```bash
kubectl config current-context
```

Make sure that the command shows the cluster where you want to install WProofreader Server.
{% endstep %}

{% step %}

#### Create the namespace

```bash
kubectl create namespace wsc
```

If the namespace exists, the command shows `AlreadyExists`. You can continue.
{% endstep %}

{% step %}

#### Download the Helm chart

The next steps use files in this directory. Run all commands from it.

```bash
mkdir -p ~/wproofreader-k8s
cd ~/wproofreader-k8s
git clone https://github.com/WebSpellChecker/wproofreader-helm.git
```

The `git clone` command creates the `wproofreader-helm` directory from the `main` branch.
{% endstep %}

{% step %}

#### Create the license Secret

Replace `<license_ticket_id>` with your license ticket ID:

```bash
kubectl -n wsc create secret generic wproofreader-license \
  --from-literal=license='<license_ticket_id>'
```

The command shows `secret/wproofreader-license created`.

{% hint style="info" %}
For production, create this Secret with your secret-management system. Use the Secret name `wproofreader-license` and the key `license`.
{% endhint %}
{% endstep %}

{% step %}

#### Create the values file

This command creates `wproofreader-values.yaml` with the license Secret name and both database settings disabled:

```bash
cat > wproofreader-values.yaml <<'EOF'
licenseExistingSecret: wproofreader-license
database:
  enabled: false
databaseProvisioning:
  enabled: false
EOF
```

Use this file for each install and upgrade of this release.
{% endstep %}

{% step %}

#### Use your own WProofreader Server image

By default, the chart uses the public `webspellchecker/wproofreader` image from Docker Hub. This image is for evaluation only. It has four English languages (`en_US`, `en_GB`, `en_CA`, and `en_AU`), and you cannot add languages or AI models to it. See [Run the image from Docker Hub](/v6.18.1/deployment/installation/docker/run-the-image-from-docker-hub.md).

For production, build your own WProofreader Server image and push it to your container registry. To build the image, see [Build using Dockerfile](/v6.18.1/deployment/installation/docker/build-using-dockerfile.md) or [Build using prebuilt configuration](/v6.18.1/deployment/installation/docker/build-using-prebuilt-configuration.md).

If your registry requires authentication, create a Secret with the registry credentials. Replace `<registry_host>`, `<user>`, and `<password>`:

```bash
kubectl -n wsc create secret docker-registry wproofreader-registry \
  --docker-server='<registry_host>' \
  --docker-username='<user>' \
  --docker-password='<password>'
```

The command shows `secret/wproofreader-registry created`.

Add the image settings to the values file. Replace `<registry_host>/<repository>` with the image name without the tag, and `<tag>` with the image tag:

```bash
cat >> wproofreader-values.yaml <<'EOF'
image:
  repository: <registry_host>/<repository>
  tag: "<tag>"
imagePullSecrets:
  - name: wproofreader-registry
EOF
```

If your registry does not require authentication, remove the two `imagePullSecrets` lines.

The `image.tag` setting pins the image used by this release. To change the server version later, follow [Upgrade WProofreader Server](#upgrade-wproofreader-server).

{% hint style="info" %}
For an evaluation, you can skip this step and use the image from Docker Hub.
{% endhint %}
{% endstep %}

{% step %}

#### Optional: keep dictionaries and style guides

To keep custom dictionaries and style guides when Kubernetes replaces the Pod, add persistent storage for `/dictionaries`. Replace `<storage_class>` with the name of a storage class that supports `ReadWriteOnce`:

```bash
cat >> wproofreader-values.yaml <<'EOF'
replicaCount: 1
dictionaries:
  enabled: true
  storageClassName: <storage_class>
  accessMode: ReadWriteOnce
  volumeSize: 20Gi
strategy:
  type: Recreate
  rollingUpdate: null
EOF
```

This configuration runs one replica. During an update, `Recreate` stops the old Pod before it starts the new Pod. The service is unavailable during that time. This prevents the old and new Pods from trying to use the volume on different nodes.

For multiple replicas on different nodes, use a storage class that supports `ReadWriteMany` and set `accessMode: ReadWriteMany`. See the [chart storage settings](https://github.com/WebSpellChecker/wproofreader-helm/blob/main/wproofreader/values.yaml) for other options, including `dictionaries.existingClaim`.

{% hint style="warning" %}
Without persistent storage, custom dictionaries and style guides in `/dictionaries` are lost when Kubernetes replaces the Pod.
{% endhint %}
{% endstep %}

{% step %}

#### Install the chart

```bash
helm upgrade --install wproofreader-app ./wproofreader-helm/wproofreader \
  --namespace wsc \
  --values wproofreader-values.yaml \
  --timeout 15m
kubectl -n wsc rollout status deployment/wproofreader-app --timeout=15m
```

The Helm command shows `STATUS: deployed`. It does not run a database provisioning Job. The rollout command shows `successfully rolled out`.

{% hint style="info" %}
The first start can take some minutes. The cluster must download the WProofreader Server image, and WProofreader Server must activate the license.
{% endhint %}
{% endstep %}

{% step %}

#### Check the version and license

```bash
kubectl -n wsc run wproofreader-version-check \
  --rm --attach --restart=Never --image=curlimages/curl:8.22.0 \
  -- curl --fail --show-error --silent \
  'http://wproofreader-app.wsc.svc.cluster.local/wscservice/api?cmd=ver'

kubectl -n wsc run wproofreader-license-check \
  --rm --attach --restart=Never --image=curlimages/curl:8.22.0 \
  -- curl --fail --show-error --silent \
  'http://wproofreader-app.wsc.svc.cluster.local/wscservice/api?cmd=license_status'
```

The first response contains `"ProgramVersion"` and the version installed in your WProofreader Server image. The second response contains `"valid":true`.

`kubectl` can show Pod messages before each response. You can ignore these messages.

Do not continue until the license is valid. If the license is not valid, see [License status](/v6.18.1/api-reference/auxiliary-api-commands/license-status.md) and [License verification](/v6.18.1/deployment/licensing/license-verification.md) for the possible causes.
{% endstep %}
{% endstepper %}

Applications in the cluster can now send requests to `http://wproofreader-app.wsc.svc.cluster.local/wscservice/api`.

### Recommended: protect the management APIs

Set an access key before you make the route public. Without an access key, anyone who can reach `/wscservice/api` can change your custom dictionaries and style guides.

The key protects the [Custom dictionary API](/v6.18.1/api-reference/custom-dictionary-api.md) and the [Style guide API](/v6.18.1/api-reference/style-guide-api.md). It does not protect all API commands. See [Access key](/v6.18.1/deployment/configuration/application-server/access-key.md).

{% stepper %}
{% step %}

#### Create the access-key Secret

Create a unique access key with your password manager. Use at least 20 characters, with upper and lower case letters and digits.

Save only the key, without a final newline, in a local file named `wproofreader-access-key.txt`. Do not add this file to Git.

```bash
chmod 600 wproofreader-access-key.txt
kubectl -n wsc create secret generic wproofreader-access-key \
  --from-file=access-key=./wproofreader-access-key.txt
```

The command shows `secret/wproofreader-access-key created`. It does not print the key. Remove the local file after you create the Secret. Keep the key in your password manager.

{% hint style="info" %}
For production, create this Secret with your secret-management system. Use the Secret name `wproofreader-access-key` and the key `access-key`.
{% endhint %}
{% endstep %}

{% step %}

#### Add the Secret reference

Add this configuration to `wproofreader-values.yaml`. If the file has an `extraEnv` list, add the entry to that list instead of creating a second list:

```bash
cat >> wproofreader-values.yaml <<'EOF'
extraEnv:
  - name: WPR_ACCESS_KEY
    valueFrom:
      secretKeyRef:
        name: wproofreader-access-key
        key: access-key
EOF
```

The values file contains only the Secret reference, not the key.
{% endstep %}

{% step %}

#### Apply the access-key configuration

```bash
helm upgrade wproofreader-app ./wproofreader-helm/wproofreader \
  --namespace wsc \
  --values wproofreader-values.yaml \
  --timeout 15m
kubectl -n wsc rollout status deployment/wproofreader-app --timeout=15m
```

The Helm command shows `STATUS: deployed`. The rollout command shows `successfully rolled out`.
{% endstep %}
{% endstepper %}

Send the key as the `access_key` parameter when you call the Custom dictionary or Style guide API from a trusted server-side client. Do not put the key in browser code.

If you later change the key in the Secret, restart the Deployment with `kubectl -n wsc rollout restart deployment/wproofreader-app`. The containers read the new key when they start.

### Set up external access

Use these steps if browsers or applications outside the cluster need to reach WProofreader Server. The route provides HTTPS on a public host name and sends requests to the Service on port `80`. For example, if the host name is `wproofreader.example.com`, clients send their requests to `https://wproofreader.example.com/wscservice/api`.

This setup does not provide a built-in list of allowed domains. Limit access at the Ingress or Gateway controller, or at the network level.

You need:

* An Ingress controller or a Gateway API controller in the cluster. The chart does not install a controller, and it does not create an Ingress or a route.
* A DNS name that points to the controller, for example `wproofreader.example.com`.
* A TLS certificate for this name. You can get it from [cert-manager](https://cert-manager.io/) or use a certificate that you already have.

{% stepper %}
{% step %}

#### Set the host name

Replace `wproofreader.example.com` with your DNS name:

```bash
export PUBLIC_HOST='wproofreader.example.com'
```

{% endstep %}

{% step %}

#### Create the route

Select the tab for your controller.

{% tabs %}
{% tab title="Ingress" %}
Replace `<ingress_class>` with the IngressClass name, for example `nginx` or `traefik`. Replace `<cluster_issuer>` with the name of your cert-manager ClusterIssuer.

```bash
cat > wproofreader-route.yaml <<EOF
apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
  name: wproofreader
  namespace: wsc
  annotations:
    cert-manager.io/cluster-issuer: <cluster_issuer>
spec:
  ingressClassName: <ingress_class>
  tls:
    - secretName: wproofreader-tls
      hosts:
        - ${PUBLIC_HOST}
  rules:
    - host: ${PUBLIC_HOST}
      http:
        paths:
          - path: /wscservice
            pathType: Prefix
            backend:
              service:
                name: wproofreader-app
                port:
                  number: 80
EOF
```

{% hint style="info" %}
If you do not use cert-manager, delete the two `annotations` lines from the file. Then create the `wproofreader-tls` Secret.

Replace the two example file names before you run this command:

```bash
kubectl -n wsc create secret tls wproofreader-tls \
  --cert=certificate.pem --key=certificate-key.pem
```

{% endhint %}
{% endtab %}

{% tab title="Gateway API" %}
Use this tab if the cluster has a Gateway with an HTTPS listener for your host name. The Gateway owner manages the TLS certificate.

Replace `<gateway_name>` with the Gateway name. Replace `<gateway_namespace>` with its namespace.

```bash
cat > wproofreader-route.yaml <<EOF
apiVersion: gateway.networking.k8s.io/v1
kind: HTTPRoute
metadata:
  name: wproofreader
  namespace: wsc
spec:
  parentRefs:
    - name: <gateway_name>
      namespace: <gateway_namespace>
  hostnames:
    - ${PUBLIC_HOST}
  rules:
    - matches:
        - path:
            type: PathPrefix
            value: /wscservice
      backendRefs:
        - name: wproofreader-app
          port: 80
EOF
```

{% hint style="info" %}
The Gateway must allow routes from the `wsc` namespace. For the settings, see the documentation of your Gateway API controller.
{% endhint %}
{% endtab %}
{% endtabs %}

Apply the route:

```bash
kubectl apply -f wproofreader-route.yaml
```

The command shows `ingress.networking.k8s.io/wproofreader created` or `httproute.gateway.networking.k8s.io/wproofreader created`.
{% endstep %}

{% step %}

#### Check the access

```bash
curl --fail --show-error --silent \
  "https://${PUBLIC_HOST}/wscservice/api?cmd=license_status"
```

The command shows JSON that contains `"valid":true`.
{% endstep %}

{% step %}

#### Connect the WProofreader products

Replace `<host>` with your DNS name. Load the script from `https://<host>/wscservice/wscbundle/wscbundle.js`, and set these options in the WProofreader configuration:

```js
serviceProtocol: 'https',
serviceHost: '<host>',
servicePort: '443',
servicePath: 'wscservice/api'
```

For an NPM SDK integration, also set `srcUrl` to the script URL. See [Service connection](/v6.18.1/integrations/initialization/configuration-options/service-connection.md).
{% endstep %}
{% endstepper %}

### Common problems

| Problem                                      | What to do                                                                                                                                                                            |
| -------------------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| The rollout does not finish.                 | Read the log: `kubectl -n wsc logs deployment/wproofreader-app`. Make sure that the license Secret has the `license` key.                                                             |
| A Pod stays `Pending` after you add storage. | Run `kubectl -n wsc describe pod <pod_name>`. Check that the storage class exists and supports the access mode in the values file.                                                    |
| The license is not valid.                    | Make sure that the cluster can reach the license server. See [License verification](/v6.18.1/deployment/licensing/license-verification.md).                                           |
| `curl` cannot resolve the host name.         | Make sure that the DNS name points to the address of the controller.                                                                                                                  |
| `curl` shows a certificate error.            | Check that the certificate matches the host name. For cert-manager, check its status with `kubectl -n wsc get certificate`. For a Gateway, ask its owner to check the HTTPS listener. |
| `curl` shows `404`.                          | Make sure that the route uses the path `/wscservice` and the Service `wproofreader-app` on port `80`.                                                                                 |

### Upgrade WProofreader Server

Read the release notes and back up your custom dictionaries and style guides before you upgrade. Keep the license, storage, and other settings in `wproofreader-values.yaml`.

If you use your own image, build it for the new WProofreader Server version and push it to your registry with a new tag. See [Build using Dockerfile](/v6.18.1/deployment/installation/docker/build-using-dockerfile.md) or [Build using prebuilt configuration](/v6.18.1/deployment/installation/docker/build-using-prebuilt-configuration.md).

Update `image.tag` in the existing `image` block in `wproofreader-values.yaml` to the new tag. Update `image.repository` too if the image location changes. Pulling a newer chart does not change an explicitly configured `image.tag`.

If you use the default Docker Hub evaluation image, leave `image.tag` unset to use the new chart's `appVersion`.

Then get the new chart and apply the updated values:

```bash
cd ~/wproofreader-k8s
git -C wproofreader-helm pull
helm upgrade wproofreader-app ./wproofreader-helm/wproofreader \
  --namespace wsc \
  --values wproofreader-values.yaml \
  --timeout 15m
kubectl -n wsc rollout status deployment/wproofreader-app --timeout=15m
```

The Helm command shows `STATUS: deployed`. The rollout command shows `successfully rolled out`.

A new WProofreader Server version activates the license again. Repeat [Check the version and license](#check-the-version-and-license) after the upgrade and confirm that the server reports the version you selected. See [Upgrade application version](/v6.18.1/deployment/installation/upgrade-application-version.md).

### Add Admin-panel later

You can add Admin-panel and MySQL to this installation. Do the steps in [Kubernetes](/v6.18.1/deployment/installation/kubernetes.md) from [Prerequisites](/v6.18.1/deployment/installation/kubernetes/prerequisites.md), in the same `~/wproofreader-k8s` directory, with these changes:

* In [Prerequisites](/v6.18.1/deployment/installation/kubernetes/prerequisites.md), the `wproofreader-helm` directory exists. Run `git -C wproofreader-helm pull` instead of its `git clone` command.
* In [Install WProofreader Server](/v6.18.1/deployment/installation/kubernetes/install-wproofreader-server.md), skip the step *Create the license Secret*. The `wproofreader-license` Secret exists.
* The step *Create the values file* on that page replaces `wproofreader-values.yaml`. Keep your storage, image, replica, update strategy, and `extraEnv` settings, including the access-key Secret reference, in the new file.
* If you set up external access on this page, delete the route before you do [Set up external access](/v6.18.1/deployment/installation/kubernetes/set-up-external-access.md) for the stack. The Admin-panel routing also sends `/wscservice` to WProofreader Server:

  ```bash
  kubectl delete -f wproofreader-route.yaml
  ```

The `helm upgrade --install` command on the [Install WProofreader Server](/v6.18.1/deployment/installation/kubernetes/install-wproofreader-server.md) page adds the database settings to the existing release.

### Uninstall WProofreader Server

{% hint style="danger" %}
Helm deletes a dictionary PersistentVolumeClaim that the chart created when you uninstall the release. This can also delete the stored data. Back up your custom dictionaries and style guides before you run `helm uninstall`.
{% endhint %}

If you set up external access, delete the route first:

```bash
cd ~/wproofreader-k8s
kubectl delete -f wproofreader-route.yaml
```

Remove the Helm release:

```bash
helm uninstall wproofreader-app --namespace wsc
```

The command shows `release "wproofreader-app" uninstalled`.

If no other workloads use the `wsc` namespace, delete it:

```bash
kubectl delete namespace wsc
```

This deletes all remaining Secrets and PersistentVolumeClaims in the namespace, including an existing dictionary claim. Delete the namespace only if you no longer need those resources.
