Skip to main content

Installation

Complete Prerequisites (cluster access, namespace, registry secret) before installing.

Complete Common setup below, then choose one access mode:

ModeSectionBest for
NodePortNodePort installationSingle host, IP + ports, no DNS
Ingress (HTTP)Ingress (HTTP) installationHostnames on port 80
Ingress (HTTPS)Ingress (HTTPS) installationHostnames on port 443 with TLS

Common setup

Complete these steps once before any access-mode install below.

Extract the package

Extract the deployment package provided by Annolive:

tar -xzf annolive-deployment-package.tar.gz
cd annolive-helm-package

The package contains the chart (Chart.yaml, templates/, values.yaml), example values files, and README.md.

All helm commands below are run from the extracted annolive-helm-package directory.

Configure values

Each access mode copies a different example values file (see NodePort, Ingress HTTP, or Ingress HTTPS). After copying, edit my-values.yaml: set images.annolive.tag and images.modelServer.tag to the version supplied with your release, then replace placeholder licenseKey and app.* fields with your real values. Do not rely on latest in production.

images:
annolive:
repository: annoliveai/annolive
tag: "<annolive-version>" # e.g. 08.07.26 — use the tag supplied with your package
pullPolicy: IfNotPresent
modelServer:
repository: annoliveai/model-server
tag: "<model-server-version>"
pullPolicy: IfNotPresent
postgres:
repository: postgres
tag: "16.1"
pullPolicy: IfNotPresent
rabbitmq:
repository: rabbitmq
tag: 3.9-management
pullPolicy: IfNotPresent
nginx:
repository: nginx
tag: "1.25"
pullPolicy: IfNotPresent

licenseKey: "<your-license-key>"

app:
domainName: "Your Organisation"
adminEmail: "admin@example.com"
adminName: Admin

The NodePort and Ingress example files (values-nodeport.example.yaml, values-ingress.example.yaml) include the same images block—update the Annolive tags there after cp … my-values.yaml.

FieldPurpose
images.annolive.tagAnnolive application image tag (required; supplied with your release)
images.modelServer.tagModel server image tag (required; supplied with your releas)
images.postgres.tag / images.rabbitmq.tag / images.nginx.tagInfrastructure images (defaults in the example files are usually fine)
licenseKeyAnnolive licence supplied by Annolive
app.domainNameOrganisation name created on first install
app.adminEmailPlatform admin email created on first install
app.adminNameDisplay name for the platform admin
caution

The app.* fields are used by the setup Job (setup.py) to bootstrap the application — they create your organisation domain and platform admin account. Verify they are correct before installation; changing them after bootstrap requires manual database changes. Do not re-run setup on upgrades.

Helm pulls images from the registry using the annolive-registry pull secret created in Prerequisites. Set licenseKey in my-values.yaml — do not pass it on the helm install or helm upgrade command line.

Storage

Default PVC sizes are listed in Architecture — Storage (PVCs). Override them in my-values.yaml before the first helm install if you expect heavy media use or a large database:

persistence:
storageClass: local-path # k3s; omit or set "" to use the cluster default
annoliveData:
enabled: true
size: 100Gi
postgres:
size: 10Gi
rabbitmq:
size: 2Gi

Ensure the node has enough disk for the total requested capacity (~112 Gi by default; more if you override).

To grow PVCs after install, see Extend storage later.


NodePort installation

Expose the UI and API on fixed node ports. Suitable for a single node clusters.

Prerequisites

  • Namespace and registry secret created (Prerequisites)
  • Inbound TCP 30558 (UI) and 30559 (API) on the node (firewall or security group)
  • NodePorts must be in the range 30000–32767

Values

First, copy the NodePort example, then perform the Common setup. After that, edit my-values.yaml—in particular and adjust the mode-specific fields as shown below:

cp values-nodeport.example.yaml my-values.yaml
externalUrls:
backendUrl: "http://<node-ip>:30559"
frontendUrl: "http://<node-ip>:30558"

service:
frontend:
type: NodePort
port: 3000
nodePort: 30558
nginx:
type: NodePort
port: 8559
nodePort: 30559

ingress:
enabled: false

setupJob:
enabled: true

initJob:
enabled: true

The nginx container listens on 8559 inside the cluster; 30559 is the NodePort.

Install

helm install annolive . \
-n annolive \
-f my-values.yaml

Access and verify

  • UI: http://<node-ip>:30558
  • API: http://<node-ip>:30559

Log in with app.adminEmail and temporary password obtained in post installation notes. See Bootstrap and first login.


Ingress (HTTP) installation

Serve the UI and API on HTTP hostnames (for example app.example.com and api.example.com).

Prerequisites

  • Namespace and registry secret created (Prerequisites)
  • An Ingress controller (k3s ships Traefik; set ingress.className: traefik)
  • DNS A records for both hostnames pointing at the node IP
  • Inbound TCP 80 on the node (firewall or security group)

Example DNS (domain example.com):

TypeNameValue
Aapp<node-ip>
Aapi<node-ip>

Values

First, copy the Ingress example, then perform the Common setup. After that, edit my-values.yaml—in particular and adjust the mode-specific fields as shown below:

cp values-ingress.example.yaml my-values.yaml
externalUrls:
backendUrl: "http://api.example.com"
frontendUrl: "http://app.example.com"

service:
frontend:
type: ClusterIP
port: 3000
nginx:
type: ClusterIP
port: 8559

ingress:
enabled: true
className: traefik
tls:
enabled: false
hosts:
frontend: app.example.com
api: api.example.com

setupJob:
enabled: true

initJob:
enabled: true

Install

helm install annolive . \
-n annolive \
-f my-values.yaml

Access and verify

Open the UI using the same scheme and host as frontendUrl (not the raw IP). Log in with app.adminEmail and temporary password. See Bootstrap and first login.


Ingress (HTTPS) installation

Same as HTTP Ingress, but terminate HTTPS on the Ingress and use https:// in externalUrls.

Prerequisites

  • Namespace and registry secret created (Prerequisites)
  • An Ingress controller (k3s ships Traefik; set ingress.className: traefik)
  • DNS A records for both hostnames pointing at the node IP
  • Inbound TCP 443 (and usually 80 for redirects or certificate validation) on the node
  • TLS certificate covering both hostnames (uploaded Secret or cert-manager)

Example DNS (domain example.com):

TypeNameValue
Aapp<node-ip>
Aapi<node-ip>

TLS certificate

The Ingress expects a TLS Secret named annolive-tls (or the name you set in ingress.tls.secretName). Create the Secret before or after helm install; the certificate must cover both Ingress hostnames, or use a matching wildcard (for example *.example.com).

Option A — Certbot (Let's Encrypt)

Use Certbot on the Kubernetes node when you prefer to manage certificates on the host rather than in-cluster.

Prerequisites

  • DNS A records for both hostnames point at the node IP.
  • Inbound TCP 80 is open on the node (firewall or security group; required for HTTP-01 validation).
  • Port 80 must reach Certbot on the node, not Traefik. On k3s, the Traefik LoadBalancer service installs an svclb handler that redirects host port 80 via iptables even when Traefik pods are scaled to zero. Patch the service to ClusterIP before running Certbot:
kubectl patch svc traefik -n kube-system -p '{"spec":{"type":"ClusterIP"}}'
kubectl scale deployment traefik -n kube-system --replicas=0

Confirm nothing is listening on port 80 except during the Certbot run:

ss -tlnp | grep ':80 ' || echo "port 80 free"

Install Certbot if needed:

sudo apt update
sudo apt install -y certbot

Request a certificate for both hostnames (replace the email address and domains):

sudo certbot certonly --standalone \
-d app.example.com \
-d api.example.com \
--non-interactive \
--agree-tos \
-m admin@example.com

Certbot stores multi-hostname certificates under /etc/letsencrypt/live/<first-domain>/ (the first -d value). Create the Kubernetes TLS Secret from those files. Certbot files are root-readable only — use sudo and point at your kubeconfig:

sudo KUBECONFIG=$HOME/.kube/config kubectl create secret tls annolive-tls \
--cert=/etc/letsencrypt/live/app.example.com/fullchain.pem \
--key=/etc/letsencrypt/live/app.example.com/privkey.pem \
-n annolive

If you listed the API hostname first in the certbot command, use that name in the live/ path instead (for example /etc/letsencrypt/live/api.example.com/).

Restore Traefik:

kubectl scale deployment traefik -n kube-system --replicas=1
kubectl patch svc traefik -n kube-system -p '{"spec":{"type":"LoadBalancer"}}'

Renewal

Let's Encrypt certificates expire every 90 days. To renew:

kubectl patch svc traefik -n kube-system -p '{"spec":{"type":"ClusterIP"}}'
kubectl scale deployment traefik -n kube-system --replicas=0
sudo certbot renew
kubectl delete secret annolive-tls -n annolive
sudo KUBECONFIG=$HOME/.kube/config kubectl create secret tls annolive-tls \
--cert=/etc/letsencrypt/live/app.example.com/fullchain.pem \
--key=/etc/letsencrypt/live/app.example.com/privkey.pem \
-n annolive
kubectl scale deployment traefik -n kube-system --replicas=1
kubectl patch svc traefik -n kube-system -p '{"spec":{"type":"LoadBalancer"}}'

Traefik picks up the updated Secret automatically; no pod restart is required for the Ingress controller.

Option B — Upload an existing certificate

If you already have tls.crt and tls.key files (from your CA or another tool), create the Secret directly:

kubectl create secret tls annolive-tls \
--cert=tls.crt \
--key=tls.key \
-n annolive

Option C — cert-manager (in-cluster)

Alternatively, use cert-manager with a ClusterIssuer for automatic issuance and renewal. Example manifests are included in the package under manifests/ (cluster-issuer.yaml, certificate.yaml). Edit the email address and dnsNames, then apply:

kubectl apply -f manifests/cluster-issuer.yaml
kubectl apply -f manifests/certificate.yaml

Ensure ingress.className matches the solver in cluster-issuer.yaml (for example traefik on k3s).

Values

First, copy the Ingress example, then perform the Common setup. After that, edit my-values.yaml—in particular and adjust the mode-specific fields as shown below:

cp values-ingress.example.yaml my-values.yaml
externalUrls:
backendUrl: "https://api.example.com"
frontendUrl: "https://app.example.com"

service:
frontend:
type: ClusterIP
port: 3000
nginx:
type: ClusterIP
port: 8559

ingress:
enabled: true
className: traefik
tls:
enabled: true
secretName: annolive-tls
hosts:
frontend: app.example.com
api: api.example.com

setupJob:
enabled: true

initJob:
enabled: true
caution

Django CSRF_TRUSTED_ORIGINS is derived from externalUrls.backendUrl. If users open https://api... but values still say http://api..., login and POST requests fail with CSRF errors. Keep scheme and host aligned, then restart backend pods after changes.

Install

helm install annolive . \
-n annolive \
-f my-values.yaml

Access and verify

Open https://app.example.com in a browser and log in with app.adminEmail and temporary password. See Bootstrap and first login.


Bootstrap and first login

Set app.domainName, app.adminEmail, and app.adminName in Common setup — Configure values before the first install — the setup Job uses them to create your organisation and admin account.

On first install, the setup Job runs setup.py when setupJob.enabled: true. It creates one organisation domain and one platform admin with a temporary password.

Check setup Job logs:

kubectl logs -n annolive job/annolive-setup

setup.py is fresh-install only. It fails if domains or admin users already exist. Set setupJob.enabled: false when importing existing data or migrating from Docker Compose.

On upgrades, the init Job runs init.py (collectstatic + migrate). Do not re-run setup.


Upgrade

cd annolive-helm-package
helm upgrade annolive . \
-n annolive \
-f my-values.yaml

After changing URLs or secrets:

kubectl rollout restart deploy/annolive-backend deploy/annolive-frontend deploy/annolive-worker -n annolive

Key Helm values

ValueDescription
images.annolive.tagAnnolive application image tag (set to the version supplied with your package)
images.modelServer.tagModel server image tag
externalUrls.backendUrlAPI URL users open in the browser (CORS, CSRF, ALLOWED_HOSTS)
externalUrls.frontendUrlUI URL users open in the browser (CORS)
licenseKeyAnnolive licence (set in my-values.yaml before install)
app.domainNameOrganisation name for bootstrap (setup.py; verify before first install)
app.adminEmailAdmin email for bootstrap (setup.py; verify before first install)
app.adminNameDisplay name for the platform admin
setupJob.enabledPost-install Job running setup.py
initJob.enabledPost-upgrade Job running init.py
service.frontend.nodePortNodePort UI (default 30558)
service.nginx.nodePortNodePort API (default 30559)
ingress.enabledCreate Ingress resource
ingress.classNameIngressClass (traefik on k3s, or nginx)
ingress.tls.enabledAttach TLS to Ingress
ingress.hosts.frontend / apiHostnames for UI and API
persistence.storageClassStorageClass for all PVCs (for example local-path on k3s)
persistence.annoliveData.sizeApp data PVC size (default 100Gi)
persistence.postgres.sizePostgreSQL PVC size (default 10Gi)
persistence.rabbitmq.sizeRabbitMQ PVC size (default 2Gi)

Migrate from Docker Compose

Moving from Docker Compose to Helm reuses your existing database, media, and configuration. The path depends on which Compose package version you are running.

Compose versionGuide
After 08.06.26 (latest stack)Migrate — newer Compose versions
08.06.26 or earlierMigrate — Compose ≤ 08.06.26
caution

Do not run the Helm setup or init Jobs until backup data is restored into the PVCs. Install with setupJob.enabled: false and initJob.enabled: false in my-values.yaml. After restoring from ~/annolive_backup_data.tgz, set initJob.enabled: true in my-values.yaml and run helm upgrade so init.py applies collectstatic and migrations. See Backup and Restore.

Migrate from Docker Compose (newer versions)

Use this path when your Compose installation already uses the latest stack (UI served by nginx on port 8558, API on 8559, and FRONTEND_URL / BACKEND_URL set in .env). If you are still on an older layout, use Migrate — Compose ≤ 08.06.26 instead (direct to Helm; no Compose upgrade required).

All later restore steps use the backup archive (~/annolive_backup_data.tgz), not live paths under /opt/annolive/.

1. Stop Compose and create a backup archive

Stop the stack so PostgreSQL and RabbitMQ flush cleanly, then archive everything under /opt/annolive/ into a single tarball (same layout as the Compose backup guide):

docker compose -f /opt/annolive/docker-compose.yml down

cd /opt/annolive/
sudo tar czvf ~/annolive_backup_data.tgz .

Keep ~/annolive_backup_data.tgz until Helm migration is verified. Also keep /opt/annolive/ until everything is successfully migrated.

2. Install Helm with bootstrap jobs disabled

Complete Common setup and choose an access mode. Read organisation and URL settings from the backup .env:

tar -xzOf ~/annolive_backup_data.tgz ./.env | grep -E 'DOMAIN_NAME|ADMIN_EMAIL|ADMIN_NAME|LICENSE_KEY|FRONTEND_URL|BACKEND_URL'

Map those values into my-values.yaml exactly as in .env (do not change organisation or admin values — they must match the restored database):

.envmy-values.yaml
DOMAIN_NAMEapp.domainName
ADMIN_EMAILapp.adminEmail
ADMIN_NAMEapp.adminName
LICENSE_KEYlicenseKey
FRONTEND_URLexternalUrls.frontendUrl
BACKEND_URLexternalUrls.backendUrl

externalUrls scheme and host must also match what users open in the browser.

Disable both bootstrap Jobs in my-values.yaml — the setup Job would seed a fresh admin (conflicting with your existing database), and the init Job must not run until after PVC data is restored:

setupJob:
enabled: false

initJob:
enabled: false

Install the chart (example):

helm install annolive . -n annolive -f my-values.yaml

Wait until the PostgreSQL and RabbitMQ pods exist, then proceed to restore. Scale them down in step 3.2 before writing to their PVCs.

3. Restore backup data into PVCs

Extract storage, postgresql, and rabbitmq from the backup archive into a staging directory, then copy those trees into the Helm PVCs.

Path in ~/annolive_backup_data.tgzHelm PVC (release annolive)Mount path in pod
./storage/annolive-data/opt/annolive/storage/
./postgresql/postgres-data-annolive-postgres-0/var/lib/postgresql/data/
./rabbitmq/rabbitmq-data-annolive-rabbitmq-0/var/lib/rabbitmq/

3.1 Unpack restore staging from the tarball

RESTORE_DIR=~/annolive-migrate-restore
rm -rf "$RESTORE_DIR"
mkdir -p "$RESTORE_DIR"
sudo tar -xzf ~/annolive_backup_data.tgz -C "$RESTORE_DIR" ./storage ./postgresql ./rabbitmq
sudo ls "$RESTORE_DIR"
# expect: storage postgresql rabbitmq

3.2 Confirm PVC names

If your Helm release name is not annolive, PVC names differ. List them:

kubectl get pvc -n annolive

Use the names shown in the commands below.

3.3 Stop workloads that use the volumes

Scale down StatefulSets (and optionally Deployments) so files are not written while you restore data:

kubectl scale statefulset annolive-postgres annolive-rabbitmq -n annolive --replicas=0
kubectl scale deployment annolive-backend annolive-worker annolive-nginx -n annolive --replicas=0

Wait until those pods terminate:

kubectl get pods -n annolive

3.4 Restore app data (media and static files)

Start a temporary pod with the app-data PVC mounted at /opt/annolive:

kubectl run restore-app --restart=Never -n annolive \
--image=busybox \
--overrides='{"spec":{"containers":[{"name":"restore","image":"busybox","command":["sleep","3600"],"volumeMounts":[{"name":"data","mountPath":"/opt/annolive"}]}],"volumes":[{"name":"data","persistentVolumeClaim":{"claimName":"annolive-data"}}]}}'

kubectl wait --for=condition=ready pod/restore-app -n annolive --timeout=120s

Copy the contents of staged storage into the PVC so files land at /opt/annolive/storage/media and /opt/annolive/storage/static :

sudo tar -C "$RESTORE_DIR/storage" -cf - . | kubectl exec -i restore-app -n annolive -- tar xf - -C /opt/annolive/storage

Confirm layout, then remove the helper pod:

kubectl exec -n annolive restore-app -- ls /opt/annolive/storage
# expect: media static (not a nested "storage" directory)
kubectl delete pod restore-app -n annolive

3.5 Restore PostgreSQL data

kubectl run restore-postgres --restart=Never -n annolive \
--image=busybox \
--overrides='{"spec":{"containers":[{"name":"restore","image":"busybox","command":["sleep","3600"],"volumeMounts":[{"name":"data","mountPath":"/var/lib/postgresql/data"}]}],"volumes":[{"name":"data","persistentVolumeClaim":{"claimName":"postgres-data-annolive-postgres-0"}}]}}'

kubectl wait --for=condition=ready pod/restore-postgres -n annolive --timeout=120s

sudo tar -C "$RESTORE_DIR/postgresql" -cf - . | kubectl exec -i restore-postgres -n annolive -- tar xf - -C /var/lib/postgresql/data

kubectl delete pod restore-postgres -n annolive

3.6 Restore RabbitMQ data

kubectl run restore-rabbitmq --restart=Never -n annolive \
--image=busybox \
--overrides='{"spec":{"containers":[{"name":"restore","image":"busybox","command":["sleep","3600"],"volumeMounts":[{"name":"data","mountPath":"/var/lib/rabbitmq"}]}],"volumes":[{"name":"data","persistentVolumeClaim":{"claimName":"rabbitmq-data-annolive-rabbitmq-0"}}]}}'

kubectl wait --for=condition=ready pod/restore-rabbitmq -n annolive --timeout=120s

sudo tar -C "$RESTORE_DIR/rabbitmq" -cf - . | kubectl exec -i restore-rabbitmq -n annolive -- tar xf - -C /var/lib/rabbitmq

kubectl delete pod restore-rabbitmq -n annolive

3.7 Start workloads again

kubectl scale statefulset annolive-postgres annolive-rabbitmq -n annolive --replicas=1
kubectl scale deployment annolive-backend annolive-worker annolive-nginx annolive-frontend annolive-model-server -n annolive --replicas=1

Wait for PostgreSQL and RabbitMQ to become ready before continuing:

kubectl wait --for=condition=ready pod -l app.kubernetes.io/component=postgres -n annolive --timeout=300s
kubectl wait --for=condition=ready pod -l app.kubernetes.io/component=rabbitmq -n annolive --timeout=300s
caution

Restore data only while the database and message-queue pods are stopped. Do not re-run the Helm setup Job after restoring — your existing database already contains organisations and users.

Optional: remove the staging directory after a successful restore (rm -rf ~/annolive-migrate-restore). Keep ~/annolive_backup_data.tgz until you have verified the Helm install. See also Backup and Restore — Restore data.

4. Run migrations

In my-values.yaml, enable the init Job (keep setup disabled — your database is already initialised from Compose):

setupJob:
enabled: false

initJob:
enabled: true

Upgrade so init.py runs collectstatic and migrations against the restored database:

helm upgrade annolive . -n annolive -f my-values.yaml
kubectl logs -n annolive job/annolive-init

5. Verify

  • Log in with your existing admin credentials (not a freshly seeded account).
  • Confirm organisations, projects, and media are present.
  • Check pod health: kubectl get pods -n annolive

Migrate from Docker Compose (≤ 08.06.26)

Installations on version 08.06.26 or earlier use an older Compose layout. You can migrate directly to Helm — you do not need to upgrade Compose to the latest package first. Persistent data (storage/, postgresql/, rabbitmq/) uses the same paths as newer Compose, so the backup → PVC restore flow is identical.

Characteristic≤ 08.06.26 (Compose)After Helm migration
Frontend host port8558:3000 on frontendHelm frontend Service / Ingress (or NodePort 30558)
Nginx host ports8559 only (API)Helm nginx gateway / Ingress (or NodePort 30559)
.env URLsFRONTEND_URL / BACKEND_URL may be missingSet externalUrls in my-values.yaml for the Helm access mode

Old Compose nginx.conf and host-port layout do not apply under Helm — the chart provides its own nginx ConfigMap and Services.

1. Stop Compose and create a backup archive

Stop the stack so PostgreSQL and RabbitMQ flush cleanly, then archive everything under /opt/annolive/ into a single tarball (same layout as the Compose backup guide):

docker compose -f /opt/annolive/docker-compose.yml down

cd /opt/annolive/
sudo tar czvf ~/annolive_backup_data.tgz .

Keep ~/annolive_backup_data.tgz until Helm migration is verified. Also keep /opt/annolive/ until everything is successfully migrated.

2. Map values from the old .env (URLs may be missing)

Read organisation and licence fields from the backup and copy them exactly into my-values.yaml:

tar -xzOf ~/annolive_backup_data.tgz ./.env | grep -E 'DOMAIN_NAME|ADMIN_EMAIL|ADMIN_NAME|LICENSE_KEY|FRONTEND_URL|BACKEND_URL'
.envmy-values.yaml
DOMAIN_NAMEapp.domainName
ADMIN_EMAILapp.adminEmail
ADMIN_NAMEapp.adminName
LICENSE_KEYlicenseKey
FRONTEND_URL (if present)externalUrls.frontendUrl
BACKEND_URL (if present)externalUrls.backendUrl

If FRONTEND_URL / BACKEND_URL are absent (common on ≤ 08.06.26), set externalUrls to the URLs users will open on Helm — not the old Compose ports unless you deliberately keep the same hostnames.

NodePort example (UI 30558, API 30559):

externalUrls:
frontendUrl: "http://<node-ip>:30558"
backendUrl: "http://<node-ip>:30559"

app:
domainName: "Annolive" # same as DOMAIN_NAME
adminEmail: "admin@annolive.com" # same as ADMIN_EMAIL
adminName: Admin # same as ADMIN_NAME

licenseKey: "<same as LICENSE_KEY>"

Ingress example (replace with your hostnames and scheme):

externalUrls:
frontendUrl: "https://app.example.com"
backendUrl: "https://api.example.com"

Keep app.* and licenseKey identical to .env even when URLs change for the new access mode.

3. Follow the newer migration path for backup and restore

Complete following steps from Migrate from Docker Compose (newer versions) till verify:

  1. Install Helm with setupJob.enabled: false and initJob.enabled: false
  2. Restore ./storage, ./postgresql, and ./rabbitmq from the tarball into the PVCs
  3. Enable initJob in my-values.yaml and run helm upgrade
  4. Verify login, data, and media
note

To stay on Docker Compose and only upgrade the Compose package (without Helm), use Migrate Docker Compose stack instead.


Uninstall

Helm uninstall removes Deployments, StatefulSets, Services, ConfigMaps, and Secrets. PVCs are retained by default.

Keep data:

helm uninstall annolive -n annolive
kubectl get pvc -n annolive

Remove all data:

helm uninstall annolive -n annolive
kubectl delete job annolive-setup -n annolive # if setup job still exists
kubectl delete pvc --all -n annolive

Complete namespace removal:

helm uninstall annolive -n annolive
kubectl delete namespace annolive

Recreate the annolive-registry pull secret before reinstalling.


Extend storage later

PVCs can be enlarged after install only if your StorageClass supports volume expansion (allowVolumeExpansion: true). Many cloud CSI drivers (EBS, Azure Disk, etc.) support this; k3s local-path does not.

  1. Check your StorageClass:

    kubectl get storageclass
    kubectl describe storageclass <name> | grep -i allowVolumeExpansion
  2. If expansion is supported, increase the PVC request (example for app data):

    kubectl patch pvc annolive-data -n annolive \
    -p '{"spec":{"resources":{"requests":{"storage":"100Gi"}}}}'

    Repeat for the PostgreSQL and RabbitMQ PVCs if needed. StatefulSet PVCs use names like <release>-postgres-postgres-data-0.

  3. Restart the pod using the volume so the filesystem resize completes (depends on the CSI driver):

    kubectl rollout restart statefulset/annolive-postgres -n annolive
caution

Changing persistence.*.size in Helm values and running helm upgrade does not resize PVCs that already exist. Either patch the live PVC (when expansion is supported) or migrate data to new, larger PVCs.

Increasing size with local-path (k3s)

k3s local-path does not support in-place PVC expansion. To grow a volume you must back up, replace the PVC with a larger one, and restore.

To grow an existing volume (example: app data PVC annolive-data):

  1. Back up the PVC — see Backup and Restore. Also save Helm values:

    helm get values annolive -n annolive > annolive-values-backup.yaml
  2. Stop workloads using the volume:

    kubectl scale deployment -n annolive \
    annolive-backend annolive-frontend annolive-nginx annolive-worker \
    --replicas=0

    For PostgreSQL or RabbitMQ, scale the StatefulSet to zero first:

    kubectl scale statefulset annolive-postgres -n annolive --replicas=0
    kubectl scale statefulset annolive-rabbitmq -n annolive --replicas=0
  3. Delete the old PVC (and its local-path PV):

    kubectl delete pvc annolive-data -n annolive

    For StatefulSet volumes, delete the named PVC (for example annolive-postgres-postgres-data-0) after the pod is gone.

  4. Recreate at the new size — update your values file with the larger size, then upgrade so the chart creates a fresh PVC:

    persistence:
    annoliveData:
    size: 200Gi # was 100Gi
    helm upgrade annolive . -n annolive -f my-values.yaml
  5. Restore data into the new PVC using a temporary pod (reverse of the backup steps in Backup and Restore).

  6. Bring workloads back:

    kubectl scale deployment -n annolive \
    annolive-backend annolive-frontend annolive-nginx annolive-worker \
    --replicas=1
    kubectl scale statefulset annolive-postgres annolive-rabbitmq -n annolive --replicas=1

Repeat steps 2–5 for each PVC you need to enlarge (annolive-data, PostgreSQL, RabbitMQ).

tip

On k3s, size PVCs appropriately at install time to avoid this procedure. For production, consider a StorageClass that supports expansion (cloud CSI).