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
- A Kubernetes cluster,
kubectl, and Helm 3.8 or later, which can install charts from an OCI registry. - An ingress controller (ingress-nginx in the examples) and a domain name pointing at it. To try BostonIvy without one, see Try it without an Ingress.
- A GitHub token with the
read:packagesscope. The images and the chart are private packages on GitHub Container Registry.
Install
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>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-stdinWrite your settings to a file, say
values.yaml.urlis 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-tlsis the certificate for your domain: a TLS Secret you create, or one that cert-manager makes for you.Install:
helm install ivy oci://ghcr.io/ivylabsapp/charts/bostonivy --version 0.7.0 -n bostonivy -f values.yamlThe examples on this page call the release
ivy, so its resources are namedivy-bostonivy-…. With a checkout of bostonivy-os you can also install fromdeploy/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
Mail:
mail.smtpUrlandmail.from.logwrites mail to the server log, which is handy for a trial.Server settings in TOML:
configtakes a TOML file, for example sign-in providers:config: | [[auth.oidc]] id = "google" # the other fields are in bostonivy.example.tomlAnything else:
server.envtakesBOSTONIVY_<SECTION>__<KEY>variables;bostonivy.example.tomlin bostonivy-os lists every setting.
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
- Files go to S3-compatible storage instead of the volume, which only one pod can mount.
worker.separatemoves background jobs into their own pods, so they run once and not in every server replica.- Set
BOSTONIVY_REDIS__URLinserver.envif the replicas should share rate-limit counters. - Live updates, session revocation and plugins sync between replicas through PostgreSQL; nothing else is needed.
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:
The database. With the bundled PostgreSQL:
kubectl -n bostonivy exec ivy-bostonivy-postgresql-0 -- pg_dump -U bostonivy bostonivy > backup.sqlAttachments and avatars: the files volume, or your bucket.
The server secret:
auth-secret(see Back up the server secret).