Kubernetes
LinkShelf comes with a Helm chart that deploys a Deployment, a Service and an optional Ingress. It needs a Postgres or MySQL database, either your own or the bundled Postgres.
The chart version follows the app version: chart 1.4.0 deploys the image of release 1.4.0, unless you set
image.tag. Every option is documented in
values.yaml, and
values.schema.json rejects wrong values on install.
Install
The chart never creates a Secret. Create them yourself, e.g. with kubectl, External Secrets or Sealed Secrets:
helm repo add linkshelf https://m-mattia-m.github.io/LinkShelf
helm repo update
kubectl create secret generic linkshelf-secrets \
--from-literal=jwt-secret="$(openssl rand -hex 32)"
kubectl create secret generic linkshelf-db \
--from-literal=host=postgres.example.svc \
--from-literal=port=5432 \
--from-literal=username=linkshelf \
--from-literal=password=change-me \
--from-literal=database=linkshelf
A secret is an env entry with valueFrom instead of value, plain Kubernetes syntax:
# values.yaml
env:
- name: AUTHENTICATION_JWTSECRET
valueFrom:
secretKeyRef:
name: linkshelf-secrets
key: jwt-secret
- name: DATABASE_HOST
valueFrom:
secretKeyRef:
name: linkshelf-db
key: host
- name: DATABASE_PORT
valueFrom:
secretKeyRef:
name: linkshelf-db
key: port
- name: DATABASE_USERNAME
valueFrom:
secretKeyRef:
name: linkshelf-db
key: username
- name: DATABASE_PASSWORD
valueFrom:
secretKeyRef:
name: linkshelf-db
key: password
- name: DATABASE_NAME
valueFrom:
secretKeyRef:
name: linkshelf-db
key: database
ingress:
enabled: true
className: nginx
frontend:
host: links.example.com
backend:
host: api.example.com
tls:
- secretName: linkshelf-tls
hosts:
- links.example.com
- api.example.com
helm install linkshelf linkshelf/linkshelf -f values.yaml
Without an ingress, open it with kubectl port-forward svc/linkshelf 3000:3000 8085:8085.
Settings
Every key of the backend config can be set as an environment variable in env, e.g. smtp.host becomes SMTP_HOST.
See Configuration for the keys. Email verification is on by default, so set up SMTP
or turn it off, otherwise new accounts can't log in:
env:
- name: SMTP_HOST
value: smtp.example.com
- name: SMTP_FROM
value: no-reply@example.com
# - name: AUTHENTICATION_EMAILVERIFICATION_ENABLED
# value: "false"
Secrets
AUTHENTICATION_JWTSECRET is required: a random value of at least 32 bytes, like the openssl rand -hex 32 above. There
is no bootstrap admin by default; to get an admin, add AUTHENTICATION_BOOTSTRAPADMIN_EMAIL and
AUTHENTICATION_BOOTSTRAPADMIN_PASSWORD to env. Its username is admin, change it with
AUTHENTICATION_BOOTSTRAPADMIN_USERNAME. That account needs no email verification, so it works before SMTP is set up.
It is only created when it doesn't exist yet, later restarts never change it. Add SMTP_PASSWORD and AUTHENTICATION_OIDC_CLIENTSECRET the same way if you use them.
The optional service token works the same way: set
AUTHENTICATION_SERVICETOKEN_ENABLED to "true" and AUTHENTICATION_SERVICETOKEN_TOKEN from a Secret. The chart refuses
to render with it enabled and no token.
Database
Set DATABASE_HOST, DATABASE_PORT, DATABASE_USERNAME, DATABASE_PASSWORD and DATABASE_NAME in env (each can
come from a different Secret). The non-secret ones can go in configFile instead. The chart doesn't
check them; the backend fails at startup if the database is unreachable. Set database.engine to MYSQL for MySQL and
database.params for extra connection parameters such as sslmode=require.
Ingress
The browser talks to the backend directly, so the frontend and the backend need their own host. The chart derives
frontendUrl, apiUrl and oidcRedirectUrl from these hosts and uses https when a host is listed in ingress.tls.
Set the three values yourself if you expose LinkShelf another way.
Custom domains
Shelves can be served on a domain of their own, see Custom domains. List those
domains in ingress.extraHosts (a wildcard works too) and add a certificate for them to ingress.tls:
ingress:
enabled: true
frontend:
host: links.example.com
backend:
host: api.example.com
extraHosts:
- profile.example.com
- "*.shelves.example.com"
tls:
- secretName: linkshelf-tls
hosts:
- links.example.com
- api.example.com
- profile.example.com
The ingress controller has to pass the original Host header (or X-Forwarded-Host) on, and no annotation or
load balancer in front of it may overwrite it. Otherwise shelves only work through their path.
Strict origins
With the ingress enabled, strictOrigins (default true) sets app.strictOrigins: the API only answers on the
apiUrl host and only to browsers from the frontend or a shelf's domain. See
Strict origins. Set strictOrigins: false to keep the API open.
Without an ingress it has no effect.
To make the instance itself, not a shelf, reachable on another domain too, add it to ingress.extraHosts and
ingress.tls, and to APP_ADDITIONALORIGINS (comma-separated for more than one). See
Reachable on more than one domain.
ingress:
extraHosts:
- links.example.org
tls:
- secretName: linkshelf-tls
hosts:
- links.example.com
- api.example.com
- links.example.org
env:
- name: APP_ADDITIONALORIGINS
value: "https://links.example.org"
Website and SEO
Set siteUrl to the public URL of your instance (NUXT_PUBLIC_SITE_URL) to enable the sitemap, canonical URLs and
server-rendered marketing pages, see Website and SEO. It is empty
by default.
Analytics
Plausible is off by default. The chart's plausible.enabled, plausible.domain,
plausible.apiHost and plausible.proxy values set the matching NUXT_PUBLIC_PLAUSIBLE_* variables.
plausible:
enabled: true
domain: links.example.org
apiHost: https://plausible.example.org
Themes and assets
Mount the directories with extraVolumes and extraVolumeMounts and point THEMES_DIRECTORY and ASSETS_DIRECTORY
at them. See Themes and assets.
Config file
Some settings are awkward as a single variable, e.g. server.trustedProxies (a list) or a full oidc block.
configFile takes the same nested YAML as the backend config and is rendered into a ConfigMap. When it's non-empty the
chart mounts it and sets CONFIGURATION_FILE_PATH. env still wins over the same key. Never put secrets in it.
configFile:
server:
trustedProxies:
- 10.0.0.0/8
logging:
level: debug
Resources
The default resources (100m CPU / 192Mi memory requested, 512Mi limit) leave plenty of headroom. A small instance
with little traffic runs at around 15m CPU and 40MB memory.
Additional resources
additionalResources takes a list of manifests, as maps or as strings. Both are rendered with tpl, so
{{ .Release.Name }} works.
additionalResources:
- apiVersion: networking.k8s.io/v1
kind: NetworkPolicy
metadata:
name: "{{ .Release.Name }}-default-deny"
spec:
podSelector: {}
policyTypes: [Ingress]
Postgres for evaluation
postgresql.enabled: true deploys a single Postgres next to LinkShelf, using the
groundhog2k/postgres chart. Everything under
postgresql is passed on to it.
It needs two Secrets. settings.existingSecret holds the superuser password under POSTGRES_PASSWORD.
userDatabase.existingSecret holds POSTGRES_DB, USERDB_USER and USERDB_PASSWORD, and LinkShelf reads the same
Secret. The key names can be changed with the secretKey fields of that chart.
postgresql:
enabled: true
settings:
existingSecret: linkshelf-postgres-admin
userDatabase:
existingSecret: linkshelf-postgres
There is no backup, replication or upgrade handling, so use your own database for anything you care about. It can't be
combined with setting the DATABASE_* variables in env.
OpenShift
The default security context sets no user or group ID, so the restricted SCC can assign its own. The image runs with an arbitrary user ID and a read-only root filesystem.
- The bundled Postgres does not run on OpenShift. Bring your own database.
- OpenShift converts an Ingress to a Route. For a native
Route, add it withadditionalResources.