Skip to content

Memos

Deploy Memos on Kubernetes as a lightweight self-hosted note-taking service.

Overview

The HelmForge Memos chart uses the official docker.io/neosmemo/memos:0.29.1 image and deploys a StatefulSet. Memos listens on port 5230 and stores durable data under /var/opt/memos.

The default topology is one persistent pod with SQLite. PostgreSQL and MySQL are supported through MEMOS_DRIVER and MEMOS_DSN, but the data PVC remains required because Memos can still store local assets and instance data on disk.

Configuration Reference

Core runtime:

  • image.repository, image.tag, image.pullPolicy: official pinned Memos image and pull behavior.
  • imagePullSecrets: optional registry pull secrets.
  • replicaCount: Memos pod count. Keep 1 with SQLite; scale external database mode only with shared MEMOS_DATA storage.
  • app.port: HTTP port.
  • app.command, app.args: optional command and argument overrides.
  • app.env, app.envFrom, app.extraEnv: additional runtime environment and Secret or ConfigMap imports.
  • commonLabels, nameOverride, fullnameOverride: naming and common metadata controls.

Memos application:

  • memos.addr: bind address passed to MEMOS_ADDR.
  • memos.instanceUrl: public URL for Ingress, Gateway API, or another reverse proxy.
  • memos.demo: upstream demo mode. Keep disabled for real instances.
  • memos.allowPrivateWebhooks: private webhook target control.
  • memos.logLevel: debug, info, warn, or error.

Database and storage:

  • database.driver: sqlite, mysql, or postgres.
  • database.dsn: inline lab DSN for MySQL or PostgreSQL.
  • database.existingSecret, database.existingSecretKey: production DSN Secret.
  • persistence.enabled, persistence.size, persistence.storageClass: Memos data directory storage.
  • persistence.accessModes, persistence.existingClaim, persistence.mountPath.
  • The chart rejects SQLite scaling, external database without DSN, external database without a data volume, and scaled MySQL/PostgreSQL without persistence.existingClaim.

Exposure and operations:

  • serviceAccount.create, serviceAccount.name, serviceAccount.annotations, serviceAccount.automountServiceAccountToken.
  • service.type, service.port, service.annotations, service.ipFamilyPolicy, service.ipFamilies.
  • ingress.enabled, ingress.ingressClassName, ingress.annotations, ingress.hosts, ingress.tls.
  • gateway.enabled, gateway.parentRefs, gateway.hostnames, gateway.path, gateway.pathType.
  • pdb.enabled, pdb.minAvailable.
  • networkPolicy.enabled, networkPolicy.ingressFrom.
  • probes.startup, probes.liveness, probes.readiness: enable flags and timing values.
  • resources, podSecurityContext, securityContext, nodeSelector, tolerations, affinity.
  • topologySpreadConstraints, priorityClassName, terminationGracePeriodSeconds.
  • podLabels, podAnnotations, extraVolumes, extraVolumeMounts, extraManifests.

Installation

helm repo add helmforge https://repo.helmforge.dev
helm repo update
helm install memos helmforge/memos

OCI install:

helm install memos oci://ghcr.io/helmforgedev/helm/memos

Production Example

memos:
  instanceUrl: https://memos.example.com

persistence:
  enabled: true
  size: 20Gi

ingress:
  enabled: true
  ingressClassName: traefik
  hosts:
    - host: memos.example.com
      paths:
        - path: /
          pathType: Prefix
  tls:
    - secretName: memos-tls
      hosts:
        - memos.example.com

networkPolicy:
  enabled: true

Set memos.instanceUrl when Memos is behind Ingress, Gateway API, or another reverse proxy.

External Database

database:
  driver: postgres
  existingSecret: memos-postgres
  existingSecretKey: dsn
persistence:
  existingClaim: memos-shared-data

The Secret should contain a Memos-compatible DSN:

apiVersion: v1
kind: Secret
metadata:
  name: memos-postgres
type: Opaque
stringData:
  dsn: postgres://memos:password@postgresql:5432/memos?sslmode=disable

The chart blocks unsafe configurations:

  • multiple replicas with SQLite
  • multiple replicas with MySQL/PostgreSQL and no shared existing data claim
  • MySQL/PostgreSQL without a DSN Secret or inline DSN
  • external database with no data volume

Security

The chart runs the upstream non-root UID/GID 10001, disables ServiceAccount token automount by default, drops Linux capabilities, and keeps memos.allowPrivateWebhooks=false.

Only enable private webhooks when the targets are trusted internal services and cluster egress is controlled.

Backup

Back up the PVC in every mode. With SQLite, it contains the database and assets. With PostgreSQL or MySQL, it can still contain local assets and instance data, so database backup alone is not enough.

Additional Resources