IvyLabs Wiki
Language: English

Deploy on Kubernetes with Helm

The BostonIvy Helm chart runs the server (API, MCP, CalDAV and background jobs), BostonIvy Web in front of it on the same address, and, for a quick start, a bundled PostgreSQL. A new chart version is published with every server release, at oci://ghcr.io/ivylabsapp/charts/bostonivy.

Before you start

Install

  1. Create a namespace and a pull secret for the private images:

    kubectl create namespace bostonivy
    kubectl -n bostonivy create secret docker-registry ghcr \
      --docker-server=ghcr.io --docker-username=<your GitHub username> --docker-password=<token with read:packages>
    
  2. Sign Helm in to the registry, so it can fetch the chart:

    echo "$GITHUB_TOKEN" | helm registry login ghcr.io -u <your GitHub username> --password-stdin
    
  3. Write your settings to a file, say values.yaml. url is required: links in mail, share links, OAuth and OIDC callbacks all use it.

    url: https://tasks.example.com
    imagePullSecrets:
      - name: ghcr
    ingress:
      enabled: true
      className: nginx
      annotations:
        # live updates are long-lived WebSockets on /api/v1/realtime; uploads go up to 64 MiB
        nginx.ingress.kubernetes.io/proxy-read-timeout: "3600"
        nginx.ingress.kubernetes.io/proxy-send-timeout: "3600"
        nginx.ingress.kubernetes.io/proxy-body-size: 64m
      tls:
        - secretName: bostonivy-tls
          hosts: [tasks.example.com]
    

    bostonivy-tls is the certificate for your domain: a TLS Secret you create, or one that cert-manager makes for you.

  4. Install:

    helm install ivy oci://ghcr.io/ivylabsapp/charts/bostonivy --version 0.7.0 -n bostonivy -f values.yaml
    

    The examples on this page call the release ivy, so its resources are named ivy-bostonivy-…. With a checkout of bostonivy-os you can also install from deploy/helm/bostonivy.

Create the first administrator

After installing, Helm prints these steps too. The password is read without showing it:

printf 'Password: '; read -rs PW; echo
printf '%s\n' "$PW" | kubectl -n bostonivy exec -i deploy/ivy-bostonivy-server -- bostonivy admin create-user <username> <email> --admin
unset PW

Back up the server secret

The server secret and the bundled database password are generated on install, kept on every upgrade, and stored in the Secret ivy-bostonivy-secrets, which Helm leaves in place even when you uninstall. Every signing and encryption key is derived from the server secret: if you lose it, everyone has to sign in again and the stored TOTP and webhook secrets can no longer be read. Copy it somewhere safe now:

kubectl -n bostonivy get secret ivy-bostonivy-secrets -o jsonpath='{.data.auth-secret}' | base64 -d

To bring your own instead, set auth.secret, or put it under the key auth-secret of a Secret you manage and set auth.existingSecret.

Try it without an Ingress

Leave ingress.enabled off and set auth.cookieSecure=false, then forward the web service to your machine:

kubectl -n bostonivy port-forward svc/ivy-bostonivy-web 8080:80

and open http://localhost:8080. Sign-in cookies need auth.cookieSecure=true again as soon as BostonIvy is served over HTTPS.

Values

Value Default What it does
url required Where people reach BostonIvy; links, OAuth and OIDC callbacks use it
imagePullSecrets [] Pull secrets for the private images
auth.secret, auth.existingSecret generated The server secret, given directly or from your own Secret (key auth-secret)
auth.cookieSecure true Keep it on behind HTTPS; off only to try BostonIvy on plain http
database.url, database.existingSecret bundled An external PostgreSQL 16 or later; empty uses the bundled one
postgresql.enabled true One PostgreSQL in the cluster, for trying BostonIvy or small setups
mail.smtpUrl, mail.from empty For example smtp://user:password@host:587, or log; empty sends no mail
config empty Extra server settings as TOML, such as [[auth.oidc]] sign-in providers
server.env, server.envFrom [] Any other BOSTONIVY_<SECTION>__<KEY> setting
server.replicas 1 More than one needs S3 storage and worker.separate
worker.separate, worker.replicas false, 1 Run background jobs (mail, webhooks, reminders, maintenance) in their own pods
persistence.* 10Gi, ReadWriteOnce Attachments and avatars while storage is local
web.image.tag 0.2.0 The BostonIvy Web release to serve
ingress.* off The host defaults to the one in url; allow long WebSocket reads for live updates

Database

The bundled PostgreSQL is a single pod, good for trying BostonIvy and small setups. For production, use a managed database or an operator, and point BostonIvy at it:

postgresql:
  enabled: false
database:
  url: postgres://bostonivy:<password>@db.example.com:5432/bostonivy

or keep the address in a Secret of your own and set database.existingSecret. BostonIvy needs PostgreSQL 16 or later.

Mail, sign-in and other settings

Scale out

The server keeps no state of its own apart from locally stored files. For more than one replica:

server:
  replicas: 3
  env:
    - name: BOSTONIVY_STORAGE__KIND
      value: s3
    # plus the bucket settings from [storage] in bostonivy.example.toml
worker:
  separate: true
persistence:
  enabled: false

Upgrade and back up

Upgrade with the chart of the new release:

helm upgrade ivy oci://ghcr.io/ivylabsapp/charts/bostonivy --version <new version> -n bostonivy -f values.yaml

The server pods roll over, and the new version migrates the database when it starts. Migrations only add, never remove, so together with a backup you can go back.

Back up three things: