Skip to main content

Vaultwarden

A lightweight self-hosted Bitwarden-compatible password manager server.

Vaultwarden is an unofficial Bitwarden server API implementation written in Rust that is compatible with all official Bitwarden clients. It runs with a fraction of the resources required by the official server. Self-hosting ensures your encrypted password vault never leaves your own infrastructure.

Alternatives considered​

Cloud Hosted

ToolOpen SourceFree TierMonthly Cost
BitwardenYesLimitedFrom $10/mo (teams)
1PasswordNoNoFrom $3/user/mo
LastPassNoLimitedFrom $3/user/mo

Self Hosted

ToolOpen SourceFull FeaturesNotes
PassboltYesLimitedTeam-focused; CE is open source

Installation​

Architecture​

  • Deployment: Single vaultwarden deployment in the vaultwarden namespace
  • Image: ghcr.io/dani-garcia/vaultwarden (alpine variant), digest-pinned via Renovate
  • Database: CNPG PostgreSQL cluster with Longhorn-encrypted PVCs
  • Storage: Longhorn PVC (data, k8up.io/backup: "true") for attachments and config
  • Networking: ClusterIP service on port 80, HTTPRoute via public gateway

Security​

  • Runs as runAsUser: 1000, runAsNonRoot: true, readOnlyRootFilesystem: true, allowPrivilegeEscalation: false, capabilities dropped
  • SIGNUPS_ALLOWED controls open registration; admin token controls admin panel access
  • Longhorn PVC and CNPG PVCs encrypted at rest

Updates​

Managed by Renovate. Image is digest-pinned.

Data Management​

  • Database: CNPG PostgreSQL cluster (Longhorn-encrypted PVCs) for vault data
  • PVC: data (Longhorn-encrypted, k8up.io/backup: "true") for attachments
  • Backups: k8up Schedules to Hetzner and Backblaze via restic (workload-talos-vaultwarden-c27b0810) at @daily-random, covering the data PVC and the database dump from the backups-cnpg PreBackupPod.

User Management​

No OIDC (Vaultwarden SSO requires Bitwarden-compatible paid client). Invitations and registration controlled by SIGNUPS_ALLOWED and SMTP-based invite emails.

Configuration Management​

  • Admin token, registration settings, SMTP credentials, and domain from SOPS-encrypted secret
  • Database URL injected from CNPG-generated secret

Usage​

Access the admin panel at /admin with the admin token to manage users and invitations. Install the Bitwarden browser extension or mobile app, point it and log in to access your vault. All vault data is end-to-end encrypted before leaving the client.

Cluster-specific deviations from the above live in the per-cluster README — see k8s/apps/vaultwarden/talos/README.md.

Cluster Deployment​

Vaultwarden — Talos cluster

Kubernetes Metadata
  • Image: ghcr.io/dani-garcia/vaultwarden:1.37.4-alpine@sha256:19ea2b669945d842dadc3d500f2d6a795866a003330f59a47b9634658bae98bb
Rendered manifests (kustomize build)
apiVersion: apps/v1
kind: Deployment
metadata:
annotations:
kustomize.toolkit.fluxcd.io/force: enabled
labels:
app.kubernetes.io/name: vaultwarden
name: vaultwarden
namespace: vaultwarden
spec:
replicas: 1
revisionHistoryLimit: 2
selector:
matchLabels:
app.kubernetes.io/name: vaultwarden
strategy:
type: Recreate
template:
metadata:
labels:
app.kubernetes.io/name: vaultwarden
spec:
automountServiceAccountToken: false
containers:
- env:
- name: ADMIN_TOKEN
valueFrom:
secretKeyRef:
key: vaultwardenAdminSecret
name: vaultwarden
- name: SIGNUPS_ALLOWED
value: 'false'
- name: LOG_LEVEL
value: warn
- name: DOMAIN
value: https://vaultwarden.web.kueber.eu
- name: SMTP_HOST
valueFrom:
secretKeyRef:
key: host
name: allinkl-smtp-credentials
- name: SMTP_FROM
valueFrom:
secretKeyRef:
key: from
name: allinkl-smtp-credentials
- name: SMTP_PORT
valueFrom:
secretKeyRef:
key: portTls
name: allinkl-smtp-credentials
- name: SMTP_SECURITY
value: force_tls
- name: SMTP_USERNAME
valueFrom:
secretKeyRef:
key: username
name: allinkl-smtp-credentials
- name: SMTP_PASSWORD
valueFrom:
secretKeyRef:
key: password
name: allinkl-smtp-credentials
- name: DATABASE_URL
valueFrom:
secretKeyRef:
key: uri
name: cnpg-app
image: ghcr.io/dani-garcia/vaultwarden:1.37.4-alpine@sha256:19ea2b669945d842dadc3d500f2d6a795866a003330f59a47b9634658bae98bb
livenessProbe:
failureThreshold: 10
httpGet:
path: /alive
port: 80
initialDelaySeconds: 5
periodSeconds: 10
successThreshold: 1
timeoutSeconds: 1
name: vaultwarden
ports:
- containerPort: 80
name: web
protocol: TCP
readinessProbe:
failureThreshold: 10
httpGet:
path: /alive
port: 80
initialDelaySeconds: 5
periodSeconds: 10
successThreshold: 1
timeoutSeconds: 1
resources:
limits:
memory: 256Mi
requests:
cpu: 50m
memory: 256Mi
securityContext:
allowPrivilegeEscalation: false
capabilities:
drop:
- ALL
privileged: false
readOnlyRootFilesystem: true
startupProbe:
failureThreshold: 10
httpGet:
path: /alive
port: 80
initialDelaySeconds: 5
periodSeconds: 10
successThreshold: 1
timeoutSeconds: 1
volumeMounts:
- mountPath: /data
name: data
priorityClassName: tier-1
securityContext:
fsGroup: 1000
fsGroupChangePolicy: OnRootMismatch
runAsGroup: 1000
runAsNonRoot: true
runAsUser: 1000
seccompProfile:
type: RuntimeDefault
supplementalGroups:
- 1000
volumes:
- name: data
persistentVolumeClaim:
claimName: data