Skip to content

Vaultwarden

Self-hosted Bitwarden-compatible password manager. Vaultwarden is a lightweight Rust implementation of the Bitwarden server API, compatible with all official Bitwarden clients (iOS, Android, browser extensions, desktop apps). Supports SQLite, PostgreSQL, and MySQL backends.

Set domain before creating any user accounts

Vaultwarden 1.33+ crashes at startup if domain is empty. Even when the server starts, leaving domain unset breaks WebSocket notifications, attachment URLs, and all email links (invites, 2FA, password reset). Always configure domain with your full public HTTPS URL before first use.

Key Features

  • Bitwarden compatible — works with all official Bitwarden clients and browser extensions
  • Multiple database backends — SQLite (default), PostgreSQL, or MySQL
  • Database auto-detectiondatabase.mode: auto selects backend by configuration precedence
  • Argon2 admin token — hardened admin panel with Argon2 PHC token support
  • Signups disabled by default — new registrations blocked until explicitly enabled
  • SMTP integration — email for invites, 2FA codes, and password reset
  • NetworkPolicy — optional ingress/egress policy for network isolation
  • S3 backup — supports SQLite tar, pg_dump, and mysqldump based on active backend

Installation

HTTPS repository:

helm repo add helmforge https://repo.helmforge.dev
helm repo update
helm install vaultwarden helmforge/vaultwarden -f values.yaml

OCI registry:

helm install vaultwarden oci://ghcr.io/helmforgedev/helm/vaultwarden -f values.yaml

Deployment Examples

# values.yaml — Vaultwarden with SQLite and TLS
domain: 'https://vault.example.com'

vaultwarden:
  signupsAllowed: false # default — enable only temporarily to create first accounts

admin:
  token: '$argon2id$v=19$m=65540,t=3,p=4$...' # use: docker run --rm -it vaultwarden/server /vaultwarden hash

data:
  persistence:
    enabled: true
    size: 10Gi

ingress:
  enabled: true
  ingressClassName: traefik
  annotations:
    cert-manager.io/cluster-issuer: letsencrypt-prod
  hosts:
    - host: vault.example.com
      paths:
        - path: /
          pathType: Prefix
  tls:
    - secretName: vaultwarden-tls
      hosts:
        - vault.example.com
# values.yaml — Vaultwarden with external PostgreSQL
domain: 'https://vault.example.com'

vaultwarden:
  signupsAllowed: false

database:
  mode: auto
  external:
    vendor: postgres
    host: postgresql.database.svc.cluster.local
    port: '5432'
    name: vaultwarden
    username: vaultwarden
    existingSecret: vaultwarden-db-credentials
    existingSecretUrlKey: database-url

admin:
  existingSecret: vaultwarden-admin
  existingSecretTokenKey: admin-token

data:
  persistence:
    enabled: true
    size: 5Gi

ingress:
  enabled: true
  ingressClassName: traefik
  hosts:
    - host: vault.example.com
      paths:
        - path: /
          pathType: Prefix
# values.yaml — Full production Vaultwarden with SMTP, backup, and resource limits
domain: 'https://vault.example.com'

vaultwarden:
  signupsAllowed: false
  invitationsAllowed: true
  sendsAllowed: true
  passwordIterations: 600000

admin:
  existingSecret: vaultwarden-admin
  existingSecretTokenKey: admin-token

smtp:
  enabled: true
  host: smtp.example.com
  port: 587
  from: [email protected]
  fromName: Vaultwarden
  security: starttls
  username: [email protected]
  existingSecret: vaultwarden-smtp
  existingSecretPasswordKey: smtp-password

database:
  mode: auto
  external:
    vendor: postgres
    existingSecret: vaultwarden-db-credentials
    existingSecretUrlKey: database-url

backup:
  enabled: true
  schedule: '0 3 * * *'
  s3:
    endpoint: https://s3.amazonaws.com
    bucket: my-vaultwarden-backups
    existingSecret: vaultwarden-s3-credentials

resources:
  requests:
    memory: 64Mi
    cpu: 50m
  limits:
    memory: 256Mi
    cpu: 200m

data:
  persistence:
    enabled: true
    size: 5Gi

ingress:
  enabled: true
  ingressClassName: traefik
  annotations:
    cert-manager.io/cluster-issuer: letsencrypt-prod
  hosts:
    - host: vault.example.com
      paths:
        - path: /
          pathType: Prefix
  tls:
    - secretName: vaultwarden-tls
      hosts:
        - vault.example.com
# values.yaml — Vaultwarden with NetworkPolicy for strict cluster isolation
domain: 'https://vault.example.com'

vaultwarden:
  signupsAllowed: false

admin:
  existingSecret: vaultwarden-admin

data:
  persistence:
    enabled: true
    size: 5Gi

networkPolicy:
  enabled: true
  ingress:
    allowSameNamespace: true
    additionalFrom:
      # Allow only from Traefik ingress namespace
      - namespaceSelector:
          matchLabels:
            kubernetes.io/metadata.name: ingress-system
        podSelector:
          matchLabels:
            app.kubernetes.io/name: traefik
  egress:
    enabled: true
    allowDns: true
    additionalTo:
      # Allow egress only to the database namespace
      - namespaceSelector:
          matchLabels:
            kubernetes.io/metadata.name: database

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

Admin Token (Argon2)

Use an Argon2 PHC hash for the admin token in production

Setting admin.token to a plain text string works but exposes the token via bcrypt-equivalent checks. Argon2 is significantly more secure. Generate one using the container itself:

docker run --rm -it vaultwarden/server:1.35.4 /vaultwarden hash
# or with argon2 CLI:
echo -n 'your-password' | argon2 "$(openssl rand -base64 32)" -e -id -k 65540 -t 3 -p 4

Store the resulting $argon2id$... string in admin.token or in a Kubernetes Secret via admin.existingSecret.

Configuration Reference

Core

ParameterTypeDefaultDescription
nameOverridestring""Override the chart name.
fullnameOverridestring""Override the full release name.
commonLabelsobject{}Extra labels added to all resources.
clusterDomainstringcluster.localKubernetes cluster domain.
domainstring""Required. Public HTTPS URL for Vaultwarden.

Image

ParameterTypeDefaultDescription
image.repositorystringdocker.io/vaultwarden/serverVaultwarden container image.
image.tagstring"1.35.4"Image tag.
image.pullPolicystringIfNotPresentImage pull policy.
imagePullSecretsarray[]Pull secrets for private registries.

Vaultwarden Settings

ParameterTypeDefaultDescription
vaultwarden.signupsAllowedbooleanfalseAllow new user self-registration. Disabled by default.
vaultwarden.signupsVerifybooleanfalseRequire email verification for new signups.
vaultwarden.signupsDomainsWhitelistarray[]Restrict signups to specific email domains when signupsAllowed: false.
vaultwarden.invitationsAllowedbooleantrueAllow admin to invite users by email.
vaultwarden.invitationOrgNamestringVaultwardenOrganization name shown in invitation emails.
vaultwarden.invitationExpirationHoursinteger120Hours before invite and verification tokens expire.
vaultwarden.sendsAllowedbooleantrueAllow Bitwarden Send file and text sharing.
vaultwarden.emergencyAccessAllowedbooleantrueAllow emergency access features.
vaultwarden.emailChangeAllowedbooleantrueAllow users to change their email addresses.
vaultwarden.orgEventsEnabledbooleanfalseEnable organization event logging.
vaultwarden.orgCreationUsersstring""Who may create organizations: "" (all), "none", or comma-separated emails.
vaultwarden.passwordIterationsinteger600000Server-side PBKDF2 iterations for password hashing.
vaultwarden.passwordHintsAllowedbooleantrueAllow users to set password hints.
vaultwarden.showPasswordHintbooleanfalseShow hints on the login page (avoid if SMTP is configured).
vaultwarden.websocket.enabledbooleantrueEnable WebSocket notifications for real-time vault sync.
vaultwarden.logLevelstringinfoLog verbosity.
vaultwarden.proxy.ipHeaderstringX-Real-IPHeader used to identify the real client IP behind a reverse proxy.
signupsAllowed defaults to false

New user self-registration is disabled by default. To create the first accounts, either temporarily set signupsAllowed: true (then revert), use invitationsAllowed: true and invite via the admin panel, or restrict to specific email domains with signupsDomainsWhitelist.

Admin Panel

ParameterTypeDefaultDescription
admin.tokenstring""Admin panel token. Use Argon2 PHC hash for production.
admin.existingSecretstring""Existing secret containing the admin token.
admin.existingSecretTokenKeystringadmin-tokenKey inside the existing secret.

SMTP

ParameterTypeDefaultDescription
smtp.enabledbooleanfalseEnable SMTP for email notifications.
smtp.hoststring""SMTP server hostname.
smtp.portinteger587SMTP port.
smtp.fromstring""Sender email address.
smtp.fromNamestringVaultwardenSender display name.
smtp.securitystringstarttlsConnection security: starttls, force_tls, or off.
smtp.usernamestring""SMTP authentication username.
smtp.passwordstring""SMTP password (prefer existingSecret).
smtp.existingSecretstring""Existing secret containing the SMTP password.
smtp.existingSecretPasswordKeystringsmtp-passwordKey inside the existing secret.
smtp.timeoutinteger15SMTP connection timeout in seconds.
smtp.debugbooleanfalseEnable verbose SMTP troubleshooting logs.
smtp.acceptInvalidCertsbooleanfalseAccept invalid TLS certificates. Not recommended.
smtp.acceptInvalidHostnamesbooleanfalseAccept invalid TLS hostnames. Not recommended.

Database

Auto-detection precedence when database.mode: auto:

  1. database.external.host or database.external.existingSecret → external DB
  2. postgresql.enabled: true → PostgreSQL subchart
  3. mysql.enabled: true → MySQL subchart
  4. SQLite fallback
ParameterTypeDefaultDescription
database.modestringautoDatabase mode: auto, sqlite, external, postgresql, or mysql.
database.sqlite.enableWalbooleantrueEnable SQLite WAL mode for better read concurrency.
database.connection.retriesinteger15Startup retries for database connection. 0 = infinite.
database.connection.timeoutinteger30Timeout in seconds when acquiring a connection.
database.connection.idleTimeoutinteger600Seconds before idle connections are closed.
database.connection.minConnectionsinteger2Minimum pool size.
database.connection.maxConnectionsinteger10Maximum pool size.
database.external.vendorstringpostgresExternal database vendor: postgres or mysql.
database.external.hoststring""External database hostname.
database.external.portstring""External database port.
database.external.namestringvaultwardenDatabase name.
database.external.usernamestringvaultwardenDatabase username.
database.external.passwordstring""Database password (prefer existingSecret).
database.external.existingSecretstring""Existing secret with a complete DATABASE_URL value.
database.external.existingSecretUrlKeystringdatabase-urlKey inside the existing secret.
database.external.parametersstring""Optional query string appended to the generated DATABASE_URL.

Persistence

ParameterTypeDefaultDescription
data.persistence.enabledbooleantrueEnable PVC for /data (SQLite DB, attachments, sends, icons).
data.persistence.sizestring5GiPVC size.
data.persistence.storageClassstring""StorageClass for the PVC.
data.persistence.accessModestringReadWriteOncePVC access mode.
data.persistence.existingClaimstring""Use an existing PVC.
data.persistence.selectorLabelsobject{}Label selectors for pre-bound PV matching (useful for DR restores).

Service

ParameterTypeDefaultDescription
service.typestringClusterIPKubernetes service type.
service.portinteger80Service port.
service.targetPortinteger80Container port.
service.annotationsobject{}Annotations for the Service.

Ingress

ParameterTypeDefaultDescription
ingress.enabledbooleanfalseEnable an Ingress resource.
ingress.ingressClassNamestringtraefikIngress class name.
ingress.annotationsobject{}Annotations for the Ingress (e.g. cert-manager).
ingress.hostsarray[]Ingress host and path rules.
ingress.tlsarray[]TLS configuration.

NetworkPolicy

ParameterTypeDefaultDescription
networkPolicy.enabledbooleanfalseCreate a NetworkPolicy for Vaultwarden.
networkPolicy.ingress.allowSameNamespacebooleantrueAllow traffic from pods in the same namespace.
networkPolicy.ingress.additionalFromarray[]Extra ingress peer selectors.
networkPolicy.egress.enabledbooleanfalseEnable egress policy (blocks all egress when enabled, add rules).
networkPolicy.egress.allowDnsbooleantrueAllow DNS egress (port 53) when egress is restricted.
networkPolicy.egress.additionalToarray[]Extra egress peer selectors.

Backup

The backup CronJob automatically selects the correct dump tool based on the active database:

  • SQLitetar + cp via Alpine (archives /data)
  • PostgreSQLpg_dump
  • MySQLmysqldump
ParameterTypeDefaultDescription
backup.enabledbooleanfalseEnable scheduled S3 backup CronJob.
backup.schedulestring"0 0 * * *"Cron schedule for backups.
backup.suspendbooleanfalseSuspend the CronJob without deleting it.
backup.successfulJobsHistoryLimitinteger3Successful job records to keep.
backup.failedJobsHistoryLimitinteger3Failed job records to keep.
backup.resourcesobject{}Resources for backup containers.
backup.s3.endpointstring""S3-compatible endpoint URL.
backup.s3.bucketstring""Target bucket name.
backup.s3.prefixstringvaultwardenKey prefix within the bucket.
backup.s3.createBucketIfNotExistsbooleantrueCreate the bucket if it does not exist.
backup.s3.existingSecretstring""Existing secret with S3 access and secret keys.
backup.s3.existingSecretAccessKeyKeystringaccess-keyKey for the S3 access key.
backup.s3.existingSecretSecretKeyKeystringsecret-keyKey for the S3 secret key.
backup.database.postgresDumpArgsstring--clean --if-existsExtra arguments passed to pg_dump.
backup.database.mysqlDumpArgsstring--single-transaction --quick --skip-lock-tables --no-tablespacesExtra args for mysqldump.

Probes

ParameterTypeDefaultDescription
probes.startup.enabledbooleantrueEnable startup probe on /alive.
probes.startup.initialDelaySecondsinteger10Startup probe initial delay.
probes.startup.periodSecondsinteger10Startup probe period.
probes.startup.failureThresholdinteger30Startup probe failure threshold.
probes.liveness.enabledbooleantrueEnable liveness probe on /alive.
probes.liveness.initialDelaySecondsinteger30Liveness probe initial delay.
probes.liveness.periodSecondsinteger20Liveness probe period.
probes.liveness.failureThresholdinteger6Liveness probe failure threshold.
probes.readiness.enabledbooleantrueEnable readiness probe on /alive.
probes.readiness.initialDelaySecondsinteger10Readiness probe initial delay.
probes.readiness.periodSecondsinteger10Readiness probe period.
probes.readiness.failureThresholdinteger6Readiness probe failure threshold.

Resources and Security

Vaultwarden runs fully hardened: non-root (UID 1000), all capabilities dropped, seccomp RuntimeDefault.

ParameterTypeDefaultDescription
resourcesobject{}CPU and memory requests and limits.
podSecurityContext.runAsUserinteger1000UID for the pod.
podSecurityContext.runAsGroupinteger1000GID for the pod.
podSecurityContext.runAsNonRootbooleantrueEnforce non-root execution.
podSecurityContext.fsGroupinteger1000Filesystem group for volume ownership.
podSecurityContext.seccompProfile.typestringRuntimeDefaultSeccomp profile type.
securityContext.allowPrivilegeEscalationbooleanfalseDisallow privilege escalation.
securityContext.capabilities.droparray["ALL"]Drop all Linux capabilities.

Scheduling

ParameterTypeDefaultDescription
nodeSelectorobject{}Node selector for scheduling.
tolerationsarray[]Tolerations for scheduling.
affinityobject{}Affinity rules.
topologySpreadConstraintsarray[]Topology spread constraints.
priorityClassNamestring""PriorityClass for the pod.
terminationGracePeriodSecondsinteger60Termination grace period.
podLabelsobject{}Extra labels for the pod.
podAnnotationsobject{}Extra annotations for the pod.

Subcharts

ParameterTypeDefaultDescription
postgresql.enabledbooleanfalseDeploy the bundled PostgreSQL subchart.
postgresql.auth.passwordstring""Required for deterministic DATABASE_URL generation.
mysql.enabledbooleanfalseDeploy the bundled MySQL subchart.
mysql.auth.passwordstring""Required for deterministic DATABASE_URL generation.

Extra

ParameterTypeDefaultDescription
extraEnvarray[]Extra environment variables for the container.
extraVolumesarray[]Extra volumes to attach to the pod.
extraVolumeMountsarray[]Extra volume mounts for the container.

More Information