Deploy Bitwarden on Kubernetes with Helm

Running a password manager that is accessible from the public internet naturally raises an important question: what could go wrong? While solutions like Bitwarden offer convenience and cross-device access, they also introduce additional exposure that must be carefully managed.

For most users, I strongly recommend a locally stored, encrypted alternative such as KeePassXC. When combined with proper backup practices and strong encryption, this approach provides an excellent balance of security and control without increasing your attack surface.

That said, if you still require remote accessibility, it’s critical to implement strict security measures—at a minimum, restricting access to specific IP addresses.

In this article, I will walk through deploying Bitwarden on a Kubernetes cluster using Helm.

I will follow the official documentation, adapting it to my setup.

Step 1: Get Bitwarden Installation Credentials

  • Go to https://bitwarden.com/host in your browser
  • Enter your email address
  • You’ll receive an Installation Id and Installation Key — save both. They look like this:
Installation Id:  xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx
Installation Key: xxxxxxxxxxxx

Step 2: Add the Bitwarden Helm Repository

helm repo add bitwarden https://charts.bitwarden.com/
helm repo update

Step 3: Create the Namespace

kubectl create namespace bitwarden

This creates an isolated namespace. All Bitwarden resources will live here, separate from my other deployments

Step 4: Generate the Default Values File

Dump the entire default configuration of the Bitwarden Helm chart into a file. This file has hundreds of settings. I will edit only the ones relevant to my setup.

helm show values bitwarden/self-host > my-values.yaml

Step 5: Identify StorageClass Name

You need to know the exact name of your StorageClass.

kubectl get storageclass

In my case it is nfs-dynamic. Write it down for later

Step 6: Prepare SMTP Credentials

I use brevo.com with own domain. Working SMTP is a must for bitwarden!

Step 7 : Create Secrets

Bitwarden stores sensitive values in Kubernetes Secrets rather than in the values file. You need to create these before installing the chart. Bitwarden services communicate with each other using an internal identity key. Generate a random key:

openssl rand -base64 64 | tr -d '\n'

Save this output – this is your SA_PASSWORD (service account / internal identity key).
Create custom-secret.yaml and appy it.

apiVersion: v1
kind: Secret
metadata:
  name: custom-secret
  namespace: bitwarden
type: Opaque
stringData:
  globalSettings__installation__id: "PutSomethingHere"
  globalSettings__installation__key: "PutSomethingHere"
  globalSettings__mail__smtp__username: "PutSomethingHere"
  globalSettings__mail__smtp__password: "PutSomethingHere"
  globalSettings__yubico__clientId: "REPLACE"
  globalSettings__yubico__key: "REPLACE"
  SA_PASSWORD: "PutSomethingHere"
  • YOUR-INSTALLATION-ID – from Step 1
  • YOUR-INSTALLATION-KEY – from Step 1
  • SMTP username and password – from Step 6
  • SA_PASSWORD – from the openssl command above
  • Yubico fields – if you don’t use YubiKey, you still need the keys present but can use dummy values like "REPLACE" and "REPLACE"

Step 8: Edit my-values.yaml

I need to change

  • domain (yourdomain.com)
  • ClusterIssuer (kubectl get clusterissuer)
  • mail
  • secretName: “custom-secret”
  • database : true

Default yaml looks like this

# Optional - Override the chart name if desired
fullnameOverride: ""
nameOverride: ""

general:
  # Domain name for the service
  domain: "REPLACE"
  # Database provider type, mssql is the default.
  # Use "postgres" when providing your own PostgreSQL database.
  #  - Requires globalSettings__postgreSql__connectionString to exist in the configured secret.
  databaseProvider: "mssql"
  ingress:
    # Set to false if using a custom ingress
    enabled: true
    # Current supported values for ingress type include: nginx
    className: "nginx"
     ## - Annotations to add to the Ingress resource.
    annotations: {}
      # nginx.ingress.kubernetes.io/force-ssl-redirect: "true"
      # nginx.ingress.kubernetes.io/use-regex: "true"
      # nginx.ingress.kubernetes.io/rewrite-target: /$1
      # nginx.ingress.kubernetes.io/proxy-body-size: 500m
      # nginx.ingress.kubernetes.io/proxy-read-timeout: "600"
      # nginx.ingress.kubernetes.io/proxy-send-timeout: "600"
      # nginx.ingress.kubernetes.io/proxy-connect-timeout: "600"
    ## - Labels to add to the Ingress resource
    labels: {}
    # Certificate options
    tls:
      # TLS certificate secret name
      name: ""
      # Cluster cert issuer (ex. Let's Encrypt) name if one exists
      clusterIssuer: ""
    # Ingress path configuration - The samples provided are for Nginx. Examples with other Ingress providers are in the chart Readme on GitHub
    paths:
      web:
        path: /(.*)
        pathType: ImplementationSpecific
      attachments:
        path: /attachments/(.*)
        pathType: ImplementationSpecific
      api:
        path: /api/(.*)
        pathType: ImplementationSpecific
      icons:
        path: /icons/(.*)
        pathType: ImplementationSpecific
      notifications:
        path: /notifications/(.*)
        pathType: ImplementationSpecific
      events:
        path: /events/(.*)
        pathType: ImplementationSpecific
      scim:
        path: /scim/(.*)
        pathType: ImplementationSpecific
      sso:
        path: /(sso/.*)
        pathType: ImplementationSpecific
      identity:
        path: /(identity/.*)
        pathType: ImplementationSpecific
      admin:
        path: /(admin/?.*)
        pathType: ImplementationSpecific

  # Override application container version tags. These will be versioned with the chart and need not be changed in most circumstances.
  # If both are empty, the chart's default version is used
  coreVersionOverride: ""
  webVersionOverride: ""
  # Disable account registration on web vault
  disableUserRegistration: "false"
  # Comma-separated list of email addresses for Admin users
  admins: "false"
  email:
    # Email address used for invitations, typically no-reply@smtp-host
    replyToEmail: "no-reply@bitwarden.localhost"
    # Your SMTP server hostname (recommended) or IP address
    smtpHost: "smtp.gmail.com"
    # The SMTP port used by the SMTP server
    smtpPort: "587"
    # Whether your SMTP server uses an encryption protocol, "true" for SSL, "false" for TLS
    smtpSsl: "false"
    # Specify true to explicitly trust the certificate presented by the SMTP server (not recommended for production).
    smtpTrustServer: "false"
    # Specify true to use SSL (not TLS) on port 25.
    smtpSslOverride: "false"
    # Specify true to force STARTTLS (Opportunistic TLS).
    smtpStartTls: "false"
  sso:
    enforceSsoPolicyForAllUsers: false
  # Custom labels to add throughout the installation
  labels: {}
  # Custom annotations to add throughout the installation
  annotations: {}
  # Additional default pod labels
  podLabels: {}
  # Additional default pod annotations
  podAnnotations: {}
  # nodeSelector and tolerations defined here will apply to all templates in the Helm chart. They can be individually overridden
  nodeSelector: {}
  tolerations: []
  # Specifies the access mode for persistent volume claims.  This should not be changed in most cases, and the allowable
  # values are only ReadWriteMany and ReadWriteOnce.  Please read https://kubernetes.io/docs/concepts/storage/persistent-volumes/#access-modes
  # to better understand these options before changing this value.
  volumeAccessMode: "ReadWriteMany"
  # Set to true to allow communication between your server and our cloud system. Doing so enables billing and license sync.  Please see: https://bitwarden.com/help/self-host-an-organization/#step-4-setup-billing-and-license-sync
  enableCloudCommunication: false
  # Cloud region for sync.  Please see: https://bitwarden.com/help/families-for-enterprise-self-hosted/#step-1-enable-cloud-communication
  cloudRegion: US
  # Uncomment but leave as an empty string if deploying offline or in an air-gap environment
  # pushRelayBaseUri: " "


  # Refresh token variables https://bitwarden.com/help/environment-variables/#refresh-token-variables
  refreshTokens:
    # Set to true to enable refresh tokens.  Recommended for production environments.
    enabled: false
    applyAbsoluteRefreshTokenOnRefreshToken: "true"
    absoluteRefreshTokenLifetimeSeconds: "30"
    slidingRefreshTokenLifetimeSeconds: ""

# Specify the name of the shared storage class
# This storage class requires ReadWriteMany.  You will need to provide your own storage class.  Storage classes with automatic volume previsioners are recommended.
sharedStorageClassName: "shared-storage"

# Secrets are required.  Review the chart README on GitHub for details on creating these secrets
secrets:
  secretName: custom-secret
  # Secrets can either be created via 'kubectl create secrets' or by using a secret provider class.  If using the latter, specify the name here
  secretProviderClass: ""

  # Encryption keys configuration
  # By default, the chart generates encryption keys (internalIdentityKey, oidcIdentityClientKey, duo__aKey).
  # Set generate to false if you want to provide these keys in the secretName provided above.
  secretKeys:
    generate: true

  # Identity certificate configuration
  # By default, the chart generates an identity certificate and password.
  # Set generate to false if you want to provide your own certificate.
  identityCertificate:
    generate: true
    # The name of the secret that contains BOTH the identity.pfx certificate file
    # AND the certificate password (globalSettings__identityServer__certificatePassword).
    secretName: ""

# Settings for individual service containers
component:
  # The Admin component
  admin:
    # Additional deployment labels
    labels: {}
    # Additional deployment annotations
    annotations: {}
    # Additional pod labels
    podLabels: {}
    # Additional pod annotations
    podAnnotations: {}
    # Optionally place deployment on specifc nodes
    nodeSelector: {}
    # Optionally add taint tolerations
    tolerations: []
    # Image repository, tag, and pull policy
    image:
      repository: ghcr.io/bitwarden/admin
      tag: ""
    # The pod resource requirements.  You can adjust these up and down for your environment
    resources:
      requests:
        memory: "256Mi"
        cpu: "125m"
      limits:
        memory: "384Mi"
        cpu: "250m"
    # Liveness probe configuration
    livenessProbe:
      httpGet:
        path: /alive
        port: 5000
      initialDelaySeconds: 300
    # Readiness probe configuration
    readinessProbe:
      httpGet:
        path: /alive
        port: 5000
      initialDelaySeconds: 5
      periodSeconds: 5
      failureThreshold: 3
    # Specifies the strategy used to replace old Pods by new ones. The value can be "Recreate" or "RollingUpdate". "RollingUpdate" is the default value.
    deploymentStrategy: RollingUpdate
    # This will set the Kubernetes container security context
    securityContext: {}
    # This will set the Kubernetes pod security context
    podSecurityContext: {}
    # Run the pod under a service account you create.  This is especially useful for OpenShift deployments
    podServiceAccount: ""
    # Extra environment variables to add to the container
    extraEnv: []
    # Extra environment variables from secrets or configmaps to add to the container
    extraEnvFrom: []
    # Extra volume mounts to add to the container
    extraVolumeMounts: []
    # Extra volumes to add to the pod
    extraVolumes: []
  api:
    # Additional deployment labels
    labels: {}
    # Additional deployment annotations
    annotations: {}
    # Additional pod labels
    podLabels: {}
    # Additional pod annotations
    podAnnotations: {}
    # Optionally place deployment on specifc nodes
    nodeSelector: {}
    # Optionally add taint tolerations
    tolerations: []
    # Image repository, tag, and pull policy
    image:
      repository: ghcr.io/bitwarden/api
      tag: ""
    # The pod resource requirements.  You can adjust these up and down for your environment
    resources:
      requests:
        memory: "128Mi"
        cpu: "100m"
      limits:
        memory: "256Mi"
        cpu: "200m"
    # Liveness probe configuration
    livenessProbe:
      httpGet:
        path: /alive
        port: 5000
      initialDelaySeconds: 120
      periodSeconds: 120
      failureThreshold: 5
      timeoutSeconds: 180
    # Readiness probe configuration
    readinessProbe:
      httpGet:
        path: /alive
        port: 5000
      initialDelaySeconds: 120
      periodSeconds: 120
      failureThreshold: 5
      timeoutSeconds: 180
    # Specifies the strategy used to replace old Pods by new ones. The value can be "Recreate" or "RollingUpdate". "RollingUpdate" is the default value.
    deploymentStrategy: RollingUpdate
    # This will set the Kubernetes container security context
    securityContext: {}
    # This will set the Kubernetes pod security context
    podSecurityContext: {}
    # Run the pod under a service account you create.  This is especially useful for OpenShift deployments
    podServiceAccount: ""
    # Extra environment variables to add to the container
    extraEnv: []
    # Extra environment variables from secrets or configmaps to add to the container
    extraEnvFrom: []
    # Extra volume mounts to add to the container
    extraVolumeMounts: []
    # Extra volumes to add to the pod
    extraVolumes: []
  attachments:
    # Additional deployment labels
    labels: {}
    # Additional deployment annotations
    annotations: {}
    # Additional pod labels
    podLabels: {}
    # Additional pod annotations
    podAnnotations: {}
    # Optionally place deployment on specifc nodes
    nodeSelector: {}
    # Optionally add taint tolerations
    tolerations: []
    # Image repository, tag, and pull policy
    image:
      repository: ghcr.io/bitwarden/attachments
      tag: ""
    # The pod resource requirements.  You can adjust these up and down for your environment
    resources:
      requests:
        memory: "64Mi"
        cpu: "50m"
      limits:
        memory: "128Mi"
        cpu: "100m"
    # Liveness probe configuration
    livenessProbe:
      httpGet:
        path: /alive
        port: 5000
      initialDelaySeconds: 30
    # Readiness probe configuration
    readinessProbe:
      httpGet:
        path: /alive
        port: 5000
      initialDelaySeconds: 5
      periodSeconds: 5
      failureThreshold: 3
    # Specifies the strategy used to replace old Pods by new ones. The value can be "Recreate" or "RollingUpdate". "RollingUpdate" is the default value.
    deploymentStrategy: RollingUpdate
    # This will set the Kubernetes container security context
    securityContext: {}
    # This will set the Kubernetes pod security context
    podSecurityContext: {}
    # Run the pod under a service account you create.  This is especially useful for OpenShift deployments
    podServiceAccount: ""
    # Extra environment variables to add to the container
    extraEnv: []
    # Extra environment variables from secrets or configmaps to add to the container
    extraEnvFrom: []
    # Extra volume mounts to add to the container
    extraVolumeMounts: []
    # Extra volumes to add to the pod
    extraVolumes: []
  events:
    # Additional deployment labels
    labels: {}
    # Additional deployment annotations
    annotations: {}
    # Additional pod labels
    podLabels: {}
    # Additional pod annotations
    podAnnotations: {}
    # Optionally place deployment on specifc nodes
    nodeSelector: {}
    # Optionally add taint tolerations
    tolerations: []
    # Image repository, tag, and pull policy
    image:
      repository: ghcr.io/bitwarden/events
      # Leave tag empty to use general.coreVersionOverride or chart default. Set a specific tag to override.
      tag: ""
    # The pod resource requirements.  You can adjust these up and down for your environment
    resources:
      requests:
        memory: "64Mi"
        cpu: "50m"
      limits:
        memory: "128Mi"
        cpu: "100m"
    # Liveness probe configuration
    livenessProbe:
      httpGet:
        path: /alive
        port: 5000
      initialDelaySeconds: 30
    # Readiness probe configuration
    readinessProbe:
      httpGet:
        path: /alive
        port: 5000
      initialDelaySeconds: 5
      periodSeconds: 5
      failureThreshold: 3
    # Specifies the strategy used to replace old Pods by new ones. The value can be "Recreate" or "RollingUpdate". "RollingUpdate" is the default value.
    deploymentStrategy: RollingUpdate
    # This will set the Kubernetes container security context
    securityContext: {}
    # This will set the Kubernetes pod security context
    podSecurityContext: {}
    # Run the pod under a service account you create.  This is especially useful for OpenShift deployments
    podServiceAccount: ""
    # Extra environment variables to add to the container
    extraEnv: []
    # Extra environment variables from secrets or configmaps to add to the container
    extraEnvFrom: []
    # Extra volume mounts to add to the container
    extraVolumeMounts: []
    # Extra volumes to add to the pod
    extraVolumes: []
  icons:
    # Additional deployment labels
    labels: {}
    # Additional deployment annotations
    annotations: {}
    # Additional pod labels
    podLabels: {}
    # Additional pod annotations
    podAnnotations: {}
    # Optionally place deployment on specifc nodes
    nodeSelector: {}
    # Optionally add taint tolerations
    tolerations: []
    # Image repository, tag, and pull policy
    image:
      repository: ghcr.io/bitwarden/icons
      tag: ""
    # The pod resource requirements.  You can adjust these up and down for your environment
    resources:
      requests:
        memory: "64Mi"
        cpu: "50m"
      limits:
        memory: "128Mi"
        cpu: "100m"
    # Liveness probe configuration
    livenessProbe:
      httpGet:
        path: /alive
        port: 5000
      initialDelaySeconds: 30
    # Readiness probe configuration
    readinessProbe:
      httpGet:
        path: /alive
        port: 5000
      initialDelaySeconds: 5
      periodSeconds: 5
      failureThreshold: 3
    # Specifies the strategy used to replace old Pods by new ones. The value can be "Recreate" or "RollingUpdate". "RollingUpdate" is the default value.
    deploymentStrategy: RollingUpdate
    # This will set the Kubernetes container security context
    securityContext: {}
    # This will set the Kubernetes pod security context
    podSecurityContext: {}
    # Run the pod under a service account you create.  This is especially useful for OpenShift deployments
    podServiceAccount: ""
    # Extra environment variables to add to the container
    extraEnv: []
    # Extra environment variables from secrets or configmaps to add to the container
    extraEnvFrom: []
    # Extra volume mounts to add to the container
    extraVolumeMounts: []
    # Extra volumes to add to the pod
    extraVolumes: []
  identity:
    # Additional deployment labels
    labels: {}
    # Additional deployment annotations
    annotations: {}
    # Additional pod labels
    podLabels: {}
    # Additional pod annotations
    podAnnotations: {}
    # Optionally place deployment on specifc nodes
    nodeSelector: {}
    # Optionally add taint tolerations
    tolerations: []
    # Image repository, tag, and pull policy
    image:
      repository: ghcr.io/bitwarden/identity
      tag: ""
    # The pod resource requirements.  You can adjust these up and down for your environment
    resources:
      requests:
        memory: "128Mi"
        cpu: "100m"
      limits:
        memory: "256Mi"
        cpu: "200m"
    # Liveness probe configuration
    livenessProbe:
      httpGet:
        path: /alive
        port: 5000
      initialDelaySeconds: 120
      periodSeconds: 60
      failureThreshold: 3
      timeoutSeconds: 30
    # Readiness probe configuration
    readinessProbe:
      httpGet:
        path: /alive
        port: 5000
      initialDelaySeconds: 60
      periodSeconds: 120
      failureThreshold: 3
      timeoutSeconds: 60
    # Specifies the strategy used to replace old Pods by new ones. The value can be "Recreate" or "RollingUpdate". "RollingUpdate" is the default value.
    deploymentStrategy: RollingUpdate
    # This will set the Kubernetes container security context
    securityContext: {}
    # This will set the Kubernetes pod security context
    podSecurityContext: {}
    # Run the pod under a service account you create.  This is especially useful for OpenShift deployments
    podServiceAccount: ""
    # Extra environment variables to add to the container
    extraEnv: []
    # Extra environment variables from secrets or configmaps to add to the container
    extraEnvFrom: []
    # Extra volume mounts to add to the container
    extraVolumeMounts: []
    # Extra volumes to add to the pod
    extraVolumes: []
  notifications:
    # Additional deployment labels
    labels: {}
    # Additional deployment annotations
    annotations: {}
    # Additional pod labels
    podLabels: {}
    # Additional pod annotations
    podAnnotations: {}
    # Optionally place deployment on specifc nodes
    nodeSelector: {}
    # Optionally add taint tolerations
    tolerations: []
    # Image repository, tag, and pull policy
    image:
      repository: ghcr.io/bitwarden/notifications
      tag: ""
    # The pod resource requirements.  You can adjust these up and down for your environment
    resources:
      requests:
        memory: "64Mi"
        cpu: "50m"
      limits:
        memory: "128Mi"
        cpu: "100m"
    # Liveness probe configuration
    livenessProbe:
      httpGet:
        path: /alive
        port: 5000
      initialDelaySeconds: 30
    # Readiness probe configuration
    readinessProbe:
      httpGet:
        path: /alive
        port: 5000
      initialDelaySeconds: 5
      periodSeconds: 5
      failureThreshold: 3
    # Specifies the strategy used to replace old Pods by new ones. The value can be "Recreate" or "RollingUpdate". "RollingUpdate" is the default value.
    deploymentStrategy: RollingUpdate
    # This will set the Kubernetes container security context
    securityContext: {}
    # This will set the Kubernetes pod security context
    podSecurityContext: {}
    # Run the pod under a service account you create.  This is especially useful for OpenShift deployments
    podServiceAccount: ""
    # Extra environment variables to add to the container
    extraEnv: []
    # Extra environment variables from secrets or configmaps to add to the container
    extraEnvFrom: []
    # Extra volume mounts to add to the container
    extraVolumeMounts: []
    # Extra volumes to add to the pod
    extraVolumes: []
  scim:
    # SCIM is disabled by default.  To use this service, enable it below and set an appropriate Ingress path
    enabled: false
    # Additional deployment labels
    labels: {}
    # Additional deployment annotations
    annotations: {}
    # Additional pod labels
    podLabels: {}
    # Additional pod annotations
    podAnnotations: {}
    # Optionally place deployment on specifc nodes
    nodeSelector: {}
    # Optionally add taint tolerations
    tolerations: []
    # Image repository, tag, and pull policy
    image:
      repository: ghcr.io/bitwarden/scim
      tag: ""
    # The pod resource requirements.  You can adjust these up and down for your environment
    resources:
      requests:
        memory: "64Mi"
        cpu: "50m"
      limits:
        memory: "128Mi"
        cpu: "100m"
    # Specifies the strategy used to replace old Pods by new ones. The value can be "Recreate" or "RollingUpdate". "RollingUpdate" is the default value.
    deploymentStrategy: RollingUpdate
    # This will set the Kubernetes container security context
    securityContext: {}
    # This will set the Kubernetes pod security context
    podSecurityContext: {}
    # Run the pod under a service account you create.  This is especially useful for OpenShift deployments
    podServiceAccount: ""
    # Extra environment variables to add to the container
    extraEnv: []
    # Extra environment variables from secrets or configmaps to add to the container
    extraEnvFrom: []
    # Extra volume mounts to add to the container
    extraVolumeMounts: []
    # Extra volumes to add to the pod
    extraVolumes: []
  sso:
    # Additional deployment labels
    labels: {}
    # Additional deployment annotations
    annotations: {}
    # Additional pod labels
    podLabels: {}
    # Additional pod annotations
    podAnnotations: {}
    # Optionally place deployment on specifc nodes
    nodeSelector: {}
    # Optionally add taint tolerations
    tolerations: []
    # Image repository, tag, and pull policy
    image:
      repository: ghcr.io/bitwarden/sso
      tag: ""
    # The pod resource requirements.  You can adjust these up and down for your environment
    resources:
      requests:
        memory: "64Mi"
        cpu: "50m"
      limits:
        memory: "128Mi"
        cpu: "100m"
    # Liveness probe configuration
    livenessProbe:
      httpGet:
        path: /alive
        port: 5000
      initialDelaySeconds: 90
      periodSeconds: 60
      failureThreshold: 5
      timeoutSeconds: 60
    # Readiness probe configuration
    readinessProbe:
      httpGet:
        path: /alive
        port: 5000
      initialDelaySeconds: 5
      periodSeconds: 5
      failureThreshold: 3
    # Specifies the strategy used to replace old Pods by new ones. The value can be "Recreate" or "RollingUpdate". "RollingUpdate" is the default value.
    deploymentStrategy: RollingUpdate
    # This will set the Kubernetes container security context
    securityContext: {}
    # This will set the Kubernetes pod security context
    podSecurityContext: {}
    # Run the pod under a service account you create.  This is especially useful for OpenShift deployments
    podServiceAccount: ""
    # Extra environment variables to add to the container
    extraEnv: []
    # Extra environment variables from secrets or configmaps to add to the container
    extraEnvFrom: []
    # Extra volume mounts to add to the container
    extraVolumeMounts: []
    # Extra volumes to add to the pod
    extraVolumes: []
  web:
    # Additional deployment labels
    labels: {}
    # Additional deployment annotations
    annotations: {}
    # Additional pod labels
    podLabels: {}
    # Additional pod annotations
    podAnnotations: {}
    # Optionally place deployment on specifc nodes
    nodeSelector: {}
    # Optionally add taint tolerations
    tolerations: []
    # Image repository, tag, and pull policy
    image:
      repository: ghcr.io/bitwarden/web
      # Leave tag empty to use general.webVersionOverride or chart default. Set a specific tag to override.
      tag: ""
    # The pod resource requirements.  You can adjust these up and down for your environment
    resources:
      requests:
        memory: "64Mi"
        cpu: "50m"
      limits:
        memory: "128Mi"
        cpu: "100m"
    # Liveness probe configuration
    livenessProbe:
      httpGet:
        path: /
        port: 5000
      initialDelaySeconds: 30
    # Readiness probe configuration
    readinessProbe:
      httpGet:
        path: /
        port: 5000
      initialDelaySeconds: 5
      periodSeconds: 5
      failureThreshold: 3
    # Specifies the strategy used to replace old Pods by new ones. The value can be "Recreate" or "RollingUpdate". "RollingUpdate" is the default value.
    deploymentStrategy: RollingUpdate
    # This will set the Kubernetes container security context
    securityContext: {}
    # This will set the Kubernetes pod security context
    podSecurityContext: {}
    # Run the pod under a service account you create.  This is especially useful for OpenShift deployments
    podServiceAccount: ""
    # Extra environment variables to add to the container
    extraEnv: []
    # Extra environment variables from secrets or configmaps to add to the container
    extraEnvFrom: []
    # Extra volume mounts to add to the container
    extraVolumeMounts: []
    # Extra volumes to add to the pod
    extraVolumes: []
# Images used for jobs and init containers
supportComponents:
  dbMigrator:
    image:
      # @schema
      # deprecated: true
      # @schema
      name: ""
      repository: ghcr.io/bitwarden/mssqlmigratorutility
      # @schema
      # type: [string, number]
      # @schema
      tag: ""
  certGenerator:
    image:
      # @schema
      # deprecated: true
      # @schema
      name: ""
      repository: alpine/openssl
      # @schema
      # type: [string, number]
      # @schema
      tag: 3.5.2
  kubectl:
    image:
      # @schema
      # deprecated: true
      # @schema
      name: ""
      repository: ghcr.io/bitwarden/helm-charts/kubectl
      # @schema
      # type: [string, number]
      # @schema
      tag: 1.34

# Data volume sizes for shared PVCs
volume:
  dataprotection:
    # Use an existing PVC by specifying the name.
    # existingClaim: claimName
    # Override the accessMode specified in general.volumeAccessMode
    # accessMode: ReadWriteOnce
    # Override the storageClass specified in sharedStorageClassName
    # storageClass: "shared-storage"
    size: "1Gi"
    labels: {}
  attachments:
    # Use an existing PVC by specifying the name.
    # existingClaim: claimName
    # Override the accessMode specified in general.volumeAccessMode
    # accessMode: ReadWriteOnce
    # Override the storageClass specified in sharedStorageClassName
    # storageClass: "shared-storage"
    size: 1Gi
    labels: {}
  licenses:
    # Use an existing PVC by specifying the name.
    # existingClaim: claimName
    # Override the accessMode specified in general.volumeAccessMode
    # accessMode: ReadWriteOnce
    # Override the storageClass specified in sharedStorageClassName
    # storageClass: "shared-storage"
    size: 1Gi
    labels: {}
  logs:
    # Logs are disabled by default
    enabled: false
    # Use an existing PVC by specifying the name.
    # existingClaim: claimName
    # Override the accessMode specified in general.volumeAccessMode
    # accessMode: ReadWriteOnce
    # Override the storageClass specified in sharedStorageClassName
    # storageClass: "shared-storage"
    size: 1Gi
    labels: {}
  caCertificates:
    # Extra root certificates are disabled by default
    enabled: false
    # Load custom Certificates from the following ConfigMap
    configMapName: cacert

jobs:
  #
  # Set security contexts for pre- and post-install hooks
  #
  securityContext: {}

  podSecurityContext: {}

  db:
    preInstallMigrator:
      # Additional job labels
      labels: {}
      # Additional job annotations
      annotations: {}
      # Additional pod labels
      podLabels: {}
      # Additional pod annotations
      podAnnotations: {}
      # Resource limits for the db migrator container
      resources:
        requests:
          memory: "32Mi"
        limits:
          memory: "64Mi"
    postInstallMigrator:
      # Additional job labels
      labels: {}
      # Additional job annotations
      annotations: {}
      # Additional pod labels
      podLabels: {}
      # Additional pod annotations
      podAnnotations: {}
      # Resource limits for the db migrator container
      resources:
        requests:
          memory: "32Mi"
        limits:
          memory: "64Mi"

  setup:
    # Additional job labels
    labels: {}
    # Additional job annotations
    annotations: {}
    # Additional pod labels
    podLabels: {}
    # Additional pod annotations
    podAnnotations: {}
    # Resource limits for the kubectl container
    resources:
      requests:
        memory: "32Mi"
      limits:
        memory: "64Mi"
  cleanup:
    # Enabled by default, disable to retain resources after uninstall
    enabled: true
    # Additional job labels
    labels: {}
    # Additional job annotations
    annotations: {}
    # Additional pod labels
    podLabels: {}
    # Additional pod annotations
    podAnnotations: {}
    # Resource limits for the kubectl container
    resources:
      requests:
        memory: "32Mi"
      limits:
        memory: "64Mi"
  secret:
    # Additional job labels
    labels: {}
    # Additional job annotations
    annotations: {}
    # Additional pod labels
    podLabels: {}
    # Additional pod annotations
    podAnnotations: {}
    # Resource limits for the kubectl container
    resources:
      requests:
        memory: "32Mi"
      limits:
        memory: "64Mi"

#
# Configure service account for pre- and post-install hooks
#
serviceAccount:
  name: service-account
  # Certain instances will require the creation of a pre-deployed service account. For instance, AWS IAM enabled service accounts need to be created outside
  # of the chart to allow for setting of permissions on other AWS services like Secrets Manager
  deployRolesOnly: false

#
# Configure MSSQL Database Deployment
# This section is only used when general.databaseProvider is "mssql"
# For PostgreSQL deployments, users must bring their own
# PostgreSQL database and provide the connection string via secrets.secretName or one of the other configuration providers.
#
database:
  # deploy the MSSQL database pod?  if false, a connection string to a SQL Server will need to be provided through one of the configuration providers.
  enabled: true
  # Labels to add to the MSSQL deployment
  labels: {}
  # Annotations to add to the MSSQL deployment
  annotations: {}
  # Additional labels to the MSSQL pod
  podLabels: {}
  # Additional annotations to the MSSQL pod
  podAnnotations: {}
  # Optionally place deployment on specifc nodes
  nodeSelector: {}
  # Optionally add taint tolerations
  tolerations: []
  # Image repository, tag, and pull policy
  image:
    # @schema
    # deprecated: true
    # @schema
    name: ""
    repository: mcr.microsoft.com/mssql/server
    tag: 2022-CU22-ubuntu-22.04
  # The container is limited to the resources below.  Adjust for your environment.
  resources:
    requests:
      memory: "2G"
      cpu: "100m"
    limits:
      memory: "2G"
      cpu: "500m"

  # The MSSQL volumes for the PVCs
  volume:
    backups:
      # Use an existing PVC by specifying the name.
      # existingClaim: claimName
      # Override the accessMode specified in general.volumeAccessMode
      # accessMode: ReadWriteOnce
      # Override the storageClass specified in sharedStorageClassName
      # storageClass: "shared-storage"
      size: 1Gi
      labels: {}
      annotations: {}
    data:
      # Use an existing PVC by specifying the name.
      # existingClaim: claimName
      # Override the accessMode specified in general.volumeAccessMode
      # accessMode: ReadWriteOnce
      # Override the storageClass specified in sharedStorageClassName
      # storageClass: "shared-storage"
      size: 10Gi
      labels: {}
      annotations: {}
    log:
      # Use an existing PVC by specifying the name.
      # existingClaim: claimName
      # Override the accessMode specified in general.volumeAccessMode
      # accessMode: ReadWriteOnce
      # Override the storageClass specified in sharedStorageClassName
      # storageClass: "shared-storage"
      size: 10Gi
      labels: {}
      annotations: {}
  # Specifies the strategy used to replace old Pods by new ones. The value can be "OnDelete" or "RollingUpdate". "OnDelete" is the default value.
  # When set to OnDelete, the SQL pod will not be recreated automatically on update.  It must be manually deleted to update.
  updateStrategy: OnDelete
  # This will set the Kubernetes container security context
  securityContext: {}
  # This will set the Kubernetes pod security context (including fsGroup)
  podSecurityContext: {}
  # Run the pod under a service account you create.  This is especially useful for OpenShift deployments
  podServiceAccount: ""

# You can specify raw Kubernetes manifests that will be applied before or after the base Helm install.
# Please see the chart README in GitHub for more information and examples.
rawManifests:
  preInstall: []
  # E.g. add a custom certificate to your bitwarden installation.
  # - apiVersion: v1
  #   kind: ConfigMap
  #   metadata:
  #     name: cacert
  #   data:
  #     rootca.crt: |
  #       -----BEGIN CERTIFICATE-----
  #       xxxx
  #       -----END CERTIFICATE-----
  postInstall: []

What I changed to make it work:

  • Row 7 : set my domain
  • Row 18-25 : remove comments #
  • Row 31 : name: “bitwarden-tls”
  • Row 33 : letsencrypt-prod (find yours with kubectl get clusterissuer)
  • Row 72 : let it false for now. I will change it later to disable random people from internet to register
  • Row 74 : e-mails using for administration
  • Row 77 and 79 : SMTP setting
  • Row 89 : true
  • Row 125 : nfs-dynamic (find in with kubectl get clusterissuer)
  • Row 876 : bitwarden

What Helm would generate:

Useful to troubleshoot helm charts. Check every generated yaml file

helm template bitwarden bitwarden/self-host -n bitwarden -f my-values.yaml > rendered.yaml

Step 9: Install Bitwarden

helm install bitwarden bitwarden/self-host -n bitwarden -f my-values.yaml

Waiting for end result

NAME                                    READY   STATUS    RESTARTS   AGE
bitwarden-mssql-0                       1/1     Running   0          2m
bitwarden-web-xxxxx-xxxxx               1/1     Running   0          2m
bitwarden-api-xxxxx-xxxxx               1/1     Running   0          2m
bitwarden-admin-xxxxx-xxxxx             1/1     Running   0          2m
bitwarden-identity-xxxxx-xxxxx          1/1     Running   0          2m
bitwarden-notifications-xxxxx-xxxxx     1/1     Running   0          2m
bitwarden-events-xxxxx-xxxxx            1/1     Running   0          2m
bitwarden-icons-xxxxx-xxxxx             1/1     Running   0          2m
bitwarden-attachments-xxxxx-xxxxx       1/1     Running   0          2m
bitwarden-sso-xxxxx-xxxxx               1/1     Running   0          2m
bitwarden-scim-xxxxx-xxxxx              1/1     Running   0          2m

Visit your domain and register a user. After that I will stop free user registration. To do that, edit my-values.yaml. Make it look like this:

disableUserRegistration: "true"

Apply with (btw to upgrade to latest version, the command is the same help upgrade)

helm upgrade bitwarden bitwarden/self-host -n bitwarden -f my-values.yaml

Reload all pods. If not reloaded UserRegistration will remain possible. This will reload envs inside containers

kubectl rollout restart deployment -n bitwarden

Useful commands

# Check status of the Helm release
helm status bitwarden -n bitwarden

# List all Helm releases
helm list -n bitwarden

# Upgrade after changing my-values.yaml
helm upgrade bitwarden bitwarden/self-host -n bitwarden -f my-values.yaml

# Rollback to previous version if something breaks
helm rollback bitwarden -n bitwarden

# Completely uninstall Bitwarden
helm uninstall bitwarden -n bitwarden

Leave a Reply

Your email address will not be published. Required fields are marked *