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
| Tool | Open Source | Free Tier | Monthly Cost |
|---|---|---|---|
| Bitwarden | Yes | Limited | From $10/mo (teams) |
| 1Password | No | No | From $3/user/mo |
| LastPass | No | Limited | From $3/user/mo |
Self Hosted
| Tool | Open Source | Full Features | Notes |
|---|---|---|---|
| Passbolt | Yes | Limited | Team-focused; CE is open source |
Installation
Architecture
- Deployment: Single
vaultwardendeployment in thevaultwardennamespace - 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_ALLOWEDcontrols 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 thebackups-cnpgPreBackupPod.
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
- 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