# Docker and Kubernetes

> Run the HotKV Docker image with a password, a data volume and a license, with Docker Compose, and as a Kubernetes StatefulSet.

URL: https://docs.hotkv.com/operate/docker-and-kubernetes/


HotKV On-Premise is also delivered as a Docker image, with the same server
and `hotkv-cli` as the Linux packages. Your HotKV account team gives you
access to it along with your license. The examples on this page call the
image `hotkv`; use the name and tag you were given.

## What the image does

| Item | Value |
| --- | --- |
| Server | `/usr/local/bin/hotkv`, started as `hotkv /etc/hotkv/hotkv.conf` |
| Client | `/usr/local/bin/hotkv-cli` |
| Configuration | `/etc/hotkv/hotkv.conf`: the packaged file, with `bind 0.0.0.0`, `protected-mode no` and `dir /data` |
| Data | `/data`, a volume: snapshots (`dump.hkv`) and the AOF |
| Port | `6379` |
| User | `hotkv`, not root |
| Health check | `hotkv-cli ping` every 30 seconds |

Arguments after the image name are passed to the server as settings, the
same as `--setting value` on the `hotkv` command line, so most settings
need no configuration file. The image has no environment variables of its
own for settings; the only one it reads is `HOTKVCLI_AUTH`, which gives
the health check (and `hotkv-cli` inside the container) the password.

> **Caution**
> Protected mode is off in the image, as in the official Redis image: the
> ports you publish decide who can connect. Always set a password, and
> publish the port only where clients need it.

## Run a container

```bash
docker volume create hotkv-data

docker run -d --name hotkv \
  -p 127.0.0.1:6379:6379 \
  -v hotkv-data:/data \
  -e HOTKVCLI_AUTH='a-long-random-password' \
  --stop-timeout 120 \
  hotkv --requirepass 'a-long-random-password' --appendonly yes --maxmemory 4gb
```

- `-p 127.0.0.1:6379:6379` publishes the port on the host's loopback
  address only. Publish on another address only for clients that need it.
- The `/data` volume keeps the data when the container is replaced. A
  directory bind-mounted there instead must be writable by the image's
  `hotkv` user.
- `HOTKVCLI_AUTH` must match the password. Without it the health check's
  `PING` gets a `NOAUTH` error and the container is reported unhealthy.
- `docker stop` sends SIGTERM, on which HotKV saves a snapshot when save
  points are configured. Docker kills the container after 10 seconds by
  default; `--stop-timeout` gives a large data set time to save.

Connect from the host, or from inside the container:

```bash
hotkv-cli -a 'a-long-random-password' PING
docker exec -it hotkv hotkv-cli
```

## Use your own configuration file

For enterprise settings (the dotted names such as `replication.enabled`,
which cannot be given on the command line) or a longer configuration,
mount your own `hotkv.conf` over the image's. Keep `bind 0.0.0.0` (or the
container's address) and `dir /data` in it:

```bash
docker run -d --name hotkv \
  -p 127.0.0.1:6379:6379 \
  -v hotkv-data:/data \
  -v "$PWD/hotkv.conf:/etc/hotkv/hotkv.conf:ro" \
  -e HOTKVCLI_AUTH='a-long-random-password' \
  hotkv
```

The image starts the server with `/etc/hotkv/hotkv.conf`, so use the
`.conf` format here.

## Add your license

Mount the license file read-only and point the server at it:

```bash
docker run -d --name hotkv \
  -p 127.0.0.1:6379:6379 \
  -v hotkv-data:/data \
  -v /etc/hotkv/hotkv.license:/etc/hotkv/license/hotkv.license:ro \
  -e HOTKVCLI_AUTH='a-long-random-password' \
  hotkv --requirepass 'a-long-random-password' --license-path /etc/hotkv/license/hotkv.license
```

Ask your account team for a license suited to container deployments; see
[Which license type fits](https://docs.hotkv.com/quickstart-onpremise/index.md#which-license-type-fits).
The core data store runs without a license; only enterprise and AI
commands need one.

## Docker Compose

```yaml
services:
  hotkv:
    image: hotkv
    command:
      - --requirepass
      - ${HOTKV_PASSWORD}
      - --appendonly
      - "yes"
      - --license-path
      - /etc/hotkv/license/hotkv.license
    environment:
      HOTKVCLI_AUTH: ${HOTKV_PASSWORD}
    ports:
      - "127.0.0.1:6379:6379"
    volumes:
      - hotkv-data:/data
      - ./hotkv.license:/etc/hotkv/license/hotkv.license:ro
    stop_grace_period: 2m
    restart: unless-stopped

volumes:
  hotkv-data:
```

Put `HOTKV_PASSWORD` in an `.env` file next to `compose.yaml` and keep that
file out of version control. Other services in the same Compose project
reach the server at `hotkv:6379`; leave out `ports` if only they need it.

This runs a single node. For a cluster of three primaries and three replicas
in one Compose file, with a password, volumes, an init service and a failover
test, see [Cluster in Docker Compose](https://docs.hotkv.com/clustering/docker-compose/index.md).

## Kubernetes

There is no HotKV operator or Helm chart. The image runs under Kubernetes
like any other stateful service. The manifest below is an **example** to
adapt, not a supported deployment: a single node with a persistent volume,
its password in a Secret, and your license.

```yaml
apiVersion: v1
kind: Secret
metadata:
  name: hotkv
type: Opaque
stringData:
  password: a-long-random-password
---
apiVersion: v1
kind: Service
metadata:
  name: hotkv
spec:
  selector:
    app: hotkv
  ports:
    - name: resp
      port: 6379
      targetPort: 6379
---
apiVersion: apps/v1
kind: StatefulSet
metadata:
  name: hotkv
spec:
  serviceName: hotkv
  replicas: 1
  selector:
    matchLabels:
      app: hotkv
  template:
    metadata:
      labels:
        app: hotkv
    spec:
      terminationGracePeriodSeconds: 120
      containers:
        - name: hotkv
          image: hotkv   # the image and tag your account team gave you
          args:
            - --requirepass
            - $(HOTKV_PASSWORD)
            - --appendonly
            - "yes"
            - --maxmemory
            - 3gb
            - --license-path
            - /etc/hotkv/license/hotkv.license
          env:
            - name: HOTKV_PASSWORD
              valueFrom:
                secretKeyRef:
                  name: hotkv
                  key: password
            - name: HOTKVCLI_AUTH
              valueFrom:
                secretKeyRef:
                  name: hotkv
                  key: password
          ports:
            - name: resp
              containerPort: 6379
          readinessProbe:
            exec:
              command: ["hotkv-cli", "ping"]
            periodSeconds: 10
          startupProbe:
            tcpSocket:
              port: 6379
            periodSeconds: 10
            failureThreshold: 60   # up to 10 minutes to load the data
          livenessProbe:
            tcpSocket:
              port: 6379
            periodSeconds: 20
          resources:
            requests:
              memory: 4Gi
            limits:
              memory: 4Gi
          volumeMounts:
            - name: data
              mountPath: /data
            - name: license
              mountPath: /etc/hotkv/license
              readOnly: true
      volumes:
        - name: license
          secret:
            secretName: hotkv-license   # created from your hotkv.license
  volumeClaimTemplates:
    - metadata:
        name: data
      spec:
        accessModes: ["ReadWriteOnce"]
        resources:
          requests:
            storage: 20Gi
```

Create the license Secret with
`kubectl create secret generic hotkv-license --from-file=hotkv.license`.

Things to get right in any Kubernetes setup:

- **Memory.** Set `maxmemory` below the container's memory limit, leaving
  room for client buffers and other overhead, so the kernel does not kill
  the server. See [Memory and eviction](https://docs.hotkv.com/operate/memory-and-eviction/index.md).
- **Shutdown time.** `terminationGracePeriodSeconds` must cover saving the
  data set; otherwise the pod is killed before the snapshot is written.
- **Readiness.** `hotkv-cli ping` fails while the data is still loading and
  when writes are refused because a save failed, so traffic goes to the pod
  only when it can serve.
- **Startup.** A large data set takes a while to load. The server opens its port only once the data is
  loaded, so give the startup probe enough time for your data set; the
  liveness probe starts after it succeeds.
- **Clusters.** For [cluster mode](https://docs.hotkv.com/clustering/index.md), every node must announce
  an address the other nodes and the clients can reach. Give each pod a
  stable name through the StatefulSet's headless Service, announce that name
  with `cluster-announce-ip` (not the pod's IP address, which changes), and
  open the cluster bus port (client port + 10000) between pods.
  [Cluster on Kubernetes](https://docs.hotkv.com/clustering/kubernetes/index.md) has a manifest for
  six pods, with the Job that creates the cluster and a rolling image update.
- **Exposure.** Keep the Service internal (the default `ClusterIP`) unless
  clients outside the cluster need it, and add
  [TLS](https://docs.hotkv.com/operate/tls/index.md) when they do.

## Upgrade a container

Pull or load the new image and recreate the container with the same volume
and settings; the data in `/data` is loaded at startup. In Kubernetes,
change the image tag and let the StatefulSet roll the pods. See
[Upgrading](https://docs.hotkv.com/operate/upgrading/index.md) for version notes.

## Next steps

- [Install](https://docs.hotkv.com/get-started/install/index.md): the Linux packages.
- [Security](https://docs.hotkv.com/security/index.md): passwords, ACL users and protected mode.
- [Persistence](https://docs.hotkv.com/persistence/index.md): snapshots and the AOF.