1. Architecture Overview
Vaultwarden is an unofficial, lightweight implementation of the Bitwarden server API written in Rust. It replaces the official multi-container .NET Bitwarden server with a single binary that implements the complete Bitwarden client API (268 endpoints), enabling compatibility with all official Bitwarden clients. The project is licensed under AGPL-3.0-only.
The server is built on the Rocket web framework with Diesel ORM for database access and supports SQLite, PostgreSQL, and MySQL/MariaDB backends. All authentication uses RS256 JWT tokens with an auto-generated RSA-2048 key pair. Configuration is driven entirely through environment variables processed by a custom make_config! macro that generates a type-safe configuration system with validation, admin panel integration, and runtime updates.
Architecture Diagram
┌─────────────────────────────────────────────────────────┐
│ Bitwarden Clients │
│ (Browser, Desktop, Mobile, CLI) │
└──────────────────────┬──────────────────────────────────┘
│ HTTPS (via reverse proxy)
┌──────────────────────▼──────────────────────────────────┐
│ Vaultwarden Binary │
│ ┌─────────────┐ ┌──────────┐ ┌────────────────────┐ │
│ │ Rocket HTTP │ │ WebSocket│ │ Admin Panel (HBS) │ │
│ │ Server │ │ Hub │ │ │ │
│ └──────┬──────┘ └────┬─────┘ └────────┬───────────┘ │
│ │ │ │ │
│ ┌──────▼──────────────▼──────────────────▼───────────┐ │
│ │ API Layer │ │
│ │ /api/* (core) /identity/* (auth) /admin/* │ │
│ │ /icons/* /notifications/* /events/* │ │
│ └──────────────────────┬─────────────────────────────┘ │
│ │ │ │ │
│ ┌──────▼───┐ ┌───────▼────┐ ┌───────▼────────────┐ │
│ │ Auth/JWT │ │ Diesel ORM │ │ Background Jobs │ │
│ │ (RS256) │ │ │ │ (job_scheduler_ng) │ │
│ └──────────┘ └──────┬─────┘ └────────────────────┘ │
│ │ │
└────────────────────────┼─────────────────────────────────┘
│
┌──────────▼──────────┐
│ Database Backend │
│ SQLite / PostgreSQL │
│ / MySQL-MariaDB │
└─────────────────────┘
Note
Vaultwarden compiles database support via Cargo feature flags. The official Docker images include all three backends. Source builds must choose one:
--features sqlite,--features postgresql, or--features mysql.
Component Inventory
| Component | Path | Role |
|---|---|---|
src/main.rs |
Entry point | CLI parsing, startup, signal handling, job scheduling |
src/config.rs |
Configuration | make_config! macro, env var processing, validation |
src/auth.rs |
Authentication | JWT (RS256), RSA key management, request guards |
src/crypto.rs |
Cryptography | PBKDF2, HMAC, random generation |
src/api/ |
API handlers | All HTTP endpoint implementations |
src/api/core/ |
Core API | Ciphers, folders, collections, organizations, sends |
src/api/core/two_factor/ |
2FA | TOTP, WebAuthn, Duo, YubiKey, Email implementations |
src/api/identity/ |
Identity | Login, token refresh, SSO flows |
src/api/admin.rs |
Admin panel | Web-based admin interface |
src/api/icons.rs |
Icons | Favicon proxy/cache service |
src/api/notifications.rs |
Notifications | WebSocket hub for real-time sync |
src/db/ |
Database layer | Diesel models, migrations, connection pool |
src/db/models/ |
Data models | All entity types (User, Cipher, Organization, etc.) |
src/mail.rs |
SMTP/sendmail integration, Handlebars templates | |
src/sso.rs / src/sso_client.rs |
SSO | OpenID Connect client implementation |
src/http_client.rs |
HTTP client | Outbound requests with IP blocking |
src/ratelimit.rs |
Rate limiting | Per-IP rate limiting for auth endpoints |
docker/ |
Docker | Dockerfiles, build scripts, healthcheck |
Technology Stack
| Layer | Technology | Version/Notes |
|---|---|---|
| Language | Rust | 1.92.0+ (1.94.1 in Docker builds) |
| Web Framework | Rocket | Async, custom config |
| ORM | Diesel | With r2d2 connection pool |
| Database | SQLite / PostgreSQL / MySQL | Compile-time feature selection |
| JWT | jsonwebtoken | RS256 with 2048-bit RSA |
| 2FA | webauthn-rs, yubico, oath | WebAuthn, YubiKey OTP, TOTP |
| SSO | openidconnect | Full OIDC flow with PKCE |
| lettre | SMTP + sendmail support | |
| Templates | Handlebars | Email + admin panel rendering |
| Object Storage | OpenDAL | S3 via AWS credential chain |
| Allocator | mimalloc (optional) | enable_mimalloc feature flag |
| Container | Docker | Debian trixie / Alpine 3.23, multi-arch |
2. System Requirements
Hardware
| Deployment | CPU | RAM | Disk |
|---|---|---|---|
| Personal (1-5 users) | 1 core | 64 MB | 500 MB |
| Small team (5-50 users) | 1-2 cores | 256 MB | 2 GB |
| Organization (50+ users) | 2+ cores | 512 MB+ | 10 GB+ |
Software
| Dependency | Requirement |
|---|---|
| Docker | 20.10+ (for container deployment) |
| Rust | 1.92.0+ (for source builds) |
| OpenSSL | Required for TLS and crypto operations |
| Web Vault | v2026.2.0 (must match server version) |
Supported Platforms
| Architecture | Debian | Alpine |
|---|---|---|
| linux/amd64 | Yes | Yes |
| linux/arm64 | Yes | Yes |
| linux/arm/v7 | Yes | Yes |
| linux/arm/v6 | Yes | Yes |
3. Configuration Reference
Vaultwarden uses a custom make_config! macro that generates a type-safe configuration system. All settings are environment variables (uppercased). Settings can also be modified at runtime through the admin panel (editable settings only) and are persisted to config.json in the data folder.
Important
Environment variables set at startup take precedence initially, but admin panel changes are persisted to
config.jsonwhich overrides environment variables on subsequent startups. If a setting appears in both, theconfig.jsonvalue wins and a warning is logged listing the overridden variables.
Folders
| Variable | Type | Default | Editable | Description |
|---|---|---|---|---|
DATA_FOLDER |
String | data |
No | Main data directory |
DATABASE_URL |
String | {DATA_FOLDER}/db.sqlite3 |
No | Database connection string |
ICON_CACHE_FOLDER |
String | {DATA_FOLDER}/icon_cache |
No | Icon cache directory |
ATTACHMENTS_FOLDER |
String | {DATA_FOLDER}/attachments |
No | File attachments directory |
SENDS_FOLDER |
String | {DATA_FOLDER}/sends |
No | Send files directory |
TMP_FOLDER |
String | {DATA_FOLDER}/tmp |
No | Temporary upload directory |
TEMPLATES_FOLDER |
String | {DATA_FOLDER}/templates |
No | Custom template overrides |
RSA_KEY_FILENAME |
String | {DATA_FOLDER}/rsa_key |
No | RSA key file path (without .pem) |
WEB_VAULT_FOLDER |
String | web-vault/ |
No | Web vault static files |
WebSocket
| Variable | Type | Default | Editable | Description |
|---|---|---|---|---|
ENABLE_WEBSOCKET |
bool | true |
No | Enable WebSocket notifications |
Push Notifications
| Variable | Type | Default | Editable | Description |
|---|---|---|---|---|
PUSH_ENABLED |
bool | false |
No | Enable push notifications |
PUSH_RELAY_URI |
String | https://push.bitwarden.com |
No | Push relay server URL |
PUSH_IDENTITY_URI |
String | https://identity.bitwarden.com |
No | Push identity server URL |
PUSH_INSTALLATION_ID |
Password | (empty) | No | Installation ID from bitwarden.com/host |
PUSH_INSTALLATION_KEY |
Password | (empty) | No | Installation key from bitwarden.com/host |
Important
Push relay and identity URIs must start with
https://. All three push settings (enabled, ID, key) must be configured together.
Scheduled Jobs
All cron schedules use 6-field cron syntax (sec min hour day month weekday). Set a schedule to empty string to disable that job. Set JOB_POLL_INTERVAL_MS=0 to disable all scheduled jobs.
| Variable | Type | Default | Description |
|---|---|---|---|
JOB_POLL_INTERVAL_MS |
u64 | 30000 |
How often the scheduler checks for jobs (ms). 0 = disable all. |
SEND_PURGE_SCHEDULE |
String | 0 5 * * * * |
Purge expired Sends (hourly) |
TRASH_PURGE_SCHEDULE |
String | 0 5 0 * * * |
Delete trashed items past retention (daily) |
INCOMPLETE_2FA_SCHEDULE |
String | 30 * * * * * |
Check for incomplete 2FA logins (every minute) |
EMERGENCY_NOTIFICATION_REMINDER_SCHEDULE |
String | 0 3 * * * * |
Send emergency access reminders (hourly) |
EMERGENCY_REQUEST_TIMEOUT_SCHEDULE |
String | 0 7 * * * * |
Grant timed-out emergency requests (hourly) |
EVENT_CLEANUP_SCHEDULE |
String | 0 10 0 * * * |
Clean old events (daily) |
AUTH_REQUEST_PURGE_SCHEDULE |
String | 30 * * * * * |
Clean old auth requests (every minute) |
DUO_CONTEXT_PURGE_SCHEDULE |
String | 30 * * * * * |
Clean expired Duo contexts (every minute) |
PURGE_INCOMPLETE_SSO_AUTH |
String | 0 20 0 * * * |
Clean incomplete SSO auth records (daily) |
General Settings
| Variable | Type | Default | Editable | Description |
|---|---|---|---|---|
DOMAIN |
String | http://localhost |
Yes | Full server URL including protocol |
WEB_VAULT_ENABLED |
bool | true |
No | Enable the web vault UI |
SENDS_ALLOWED |
bool | true |
Yes | Allow users to create Sends |
HIBP_API_KEY |
Password | (unset) | Yes | HaveIBeenPwned API key for breach checking |
USER_ATTACHMENT_LIMIT |
i64 | (unset) | Yes | Per-user attachment storage limit (KB) |
ORG_ATTACHMENT_LIMIT |
i64 | (unset) | Yes | Per-org attachment storage limit (KB) |
USER_SEND_LIMIT |
i64 | (unset) | Yes | Per-user Send storage limit (KB) |
TRASH_AUTO_DELETE_DAYS |
i64 | (unset) | Yes | Days before trashed items are auto-deleted |
INCOMPLETE_2FA_TIME_LIMIT |
i64 | 3 |
Yes | Minutes before incomplete 2FA triggers notification (0 = disable) |
DISABLE_ICON_DOWNLOAD |
bool | false |
Yes | Disable icon fetching (serves cached only) |
SIGNUPS_ALLOWED |
bool | true |
Yes | Allow new user self-registration |
SIGNUPS_VERIFY |
bool | false |
Yes | Require email verification for signups |
SIGNUPS_VERIFY_RESEND_TIME |
u64 | 3600 |
Yes | Seconds between auto-resent verification emails |
SIGNUPS_VERIFY_RESEND_LIMIT |
u32 | 6 |
Yes | Max auto-resent verification emails (0 = no limit) |
SIGNUPS_DOMAINS_WHITELIST |
String | (empty) | Yes | Comma-separated allowed signup domains |
ORG_EVENTS_ENABLED |
bool | false |
No | Enable organization event logging |
ORG_CREATION_USERS |
String | (empty) | Yes | Comma-separated emails allowed to create orgs, all or none |
INVITATIONS_ALLOWED |
bool | true |
Yes | Allow org admins to invite users |
INVITATION_EXPIRATION_HOURS |
u32 | 120 |
No | Token expiration for invites, email verify, delete requests (min: 1) |
EMERGENCY_ACCESS_ALLOWED |
bool | true |
Yes | Enable emergency access feature |
EMAIL_CHANGE_ALLOWED |
bool | true |
Yes | Allow users to change their email |
PASSWORD_ITERATIONS |
i32 | 600000 |
Yes | Server-side PBKDF2 iterations (min: 100,000) |
PASSWORD_HINTS_ALLOWED |
bool | true |
Yes | Allow users to set password hints |
SHOW_PASSWORD_HINT |
bool | false |
Yes | Show hints on login page when SMTP not configured |
ADMIN_TOKEN |
Password | (unset) | Yes | Admin panel authentication token (Argon2id PHC recommended) |
INVITATION_ORG_NAME |
String | Vaultwarden |
Yes | Organization name in invitation emails |
EVENTS_DAYS_RETAIN |
i64 | (unset) | No | Days to retain events (unset = indefinite) |
Advanced Settings
| Variable | Type | Default | Editable | Description |
|---|---|---|---|---|
IP_HEADER |
String | X-Real-IP |
Yes | Header for client IP detection (none to use remote IP) |
ICON_SERVICE |
String | internal |
No | Icon service: internal, bitwarden, duckduckgo, google, or custom URL with {} |
ICON_REDIRECT_CODE |
u32 | 302 |
Yes | HTTP redirect code for external icon services (301/302/307/308) |
ICON_CACHE_TTL |
u64 | 2592000 |
Yes | Positive icon cache expiry (seconds, default 30 days) |
ICON_CACHE_NEGTTL |
u64 | 259200 |
Yes | Negative icon cache expiry (seconds, default 3 days) |
ICON_DOWNLOAD_TIMEOUT |
u64 | 10 |
Yes | Icon download timeout (seconds) |
HTTP_REQUEST_BLOCK_REGEX |
String | (unset) | Yes | Regex to block internal HTTP requests to specific domains/IPs |
HTTP_REQUEST_BLOCK_NON_GLOBAL_IPS |
bool | true |
Yes | Block HTTP requests to non-global/reserved IPs |
DISABLE_2FA_REMEMBER |
bool | false |
Yes | Force 2FA on every login (ignore "remember" checkbox) |
AUTHENTICATOR_DISABLE_TIME_DRIFT |
bool | false |
Yes | Only accept current TOTP code (no ±30s drift) |
EXPERIMENTAL_CLIENT_FEATURE_FLAGS |
String | (empty) | No | Comma-separated list of client feature flags to enable |
REQUIRE_DEVICE_EMAIL |
bool | false |
Yes | Require new device email notification on login |
RELOAD_TEMPLATES |
bool | false |
Yes | Reload templates on every request (dev only) |
EXTENDED_LOGGING |
bool | true |
No | Enable timestamp, target, and level in log output |
LOG_TIMESTAMP_FORMAT |
String | %Y-%m-%d %H:%M:%S.%3f |
Yes | Timestamp format for extended logging |
USE_SYSLOG |
bool | false |
No | Log to syslog (Unix only) |
LOG_FILE |
String | (unset) | No | File path for log output |
LOG_LEVEL |
String | info |
No | Log level: trace/debug/info/warn/error/off (supports per-module overrides) |
ENABLE_DB_WAL |
bool | true |
No | Enable SQLite WAL mode |
DB_CONNECTION_RETRIES |
u32 | 15 |
No | Database connection retry attempts (0 = infinite) |
DATABASE_TIMEOUT |
u64 | 30 |
No | Database connection acquisition timeout (seconds) |
DATABASE_IDLE_TIMEOUT |
u64 | 600 |
No | Idle connection timeout (seconds) |
DATABASE_MAX_CONNS |
u32 | 10 |
No | Maximum database connection pool size (1-256) |
DATABASE_MIN_CONNS |
u32 | 2 |
No | Minimum database connection pool size (1-256) |
DATABASE_CONN_INIT |
String | (empty) | No | SQL statements to run on new connections |
DISABLE_ADMIN_TOKEN |
bool | false |
No | Disable admin token requirement (use external auth) |
ALLOWED_IFRAME_ANCESTORS |
String | (empty) | Yes | Domains allowed to embed web vault in iframe |
ALLOWED_CONNECT_SRC |
String | (empty) | Yes | Additional CSP connect-src domains (HTTPS only) |
LOGIN_RATELIMIT_SECONDS |
u64 | 60 |
No | Seconds between login attempts before rate limiting |
LOGIN_RATELIMIT_MAX_BURST |
u32 | 10 |
No | Burst size for login rate limiter |
ADMIN_RATELIMIT_SECONDS |
u64 | 300 |
No | Seconds between admin login attempts before rate limiting |
ADMIN_RATELIMIT_MAX_BURST |
u32 | 3 |
No | Burst size for admin rate limiter |
ADMIN_SESSION_LIFETIME |
i64 | 20 |
Yes | Admin session duration (minutes) |
ORG_GROUPS_ENABLED |
bool | false |
No | Enable organization groups (beta) |
INCREASE_NOTE_SIZE_LIMIT |
bool | false |
Yes | Raise secure note limit from 10,000 to 100,000 chars |
ENFORCE_SINGLE_ORG_WITH_RESET_PW_POLICY |
bool | false |
No | Require Single Org policy before Reset Password policy |
DNS_PREFER_IPV6 |
bool | false |
Yes | Prefer IPv6 (AAAA) DNS resolution |
OpenID Connect SSO Settings
| Variable | Type | Default | Editable | Description |
|---|---|---|---|---|
SSO_ENABLED |
bool | false |
Yes | Enable SSO authentication |
SSO_ONLY |
bool | false |
Yes | Disable password login (SSO only) |
SSO_SIGNUPS_MATCH_EMAIL |
bool | true |
Yes | Match existing users by email on SSO login |
SSO_ALLOW_UNKNOWN_EMAIL_VERIFICATION |
bool | false |
Yes | Allow SSO users with unknown email verification status |
SSO_CLIENT_ID |
String | (empty) | Yes | OIDC client ID |
SSO_CLIENT_SECRET |
Password | (empty) | Yes | OIDC client secret |
SSO_AUTHORITY |
String | (empty) | Yes | OIDC provider base URL |
SSO_SCOPES |
String | email profile |
Yes | OIDC scopes (openid is implicit) |
SSO_AUTHORIZE_EXTRA_PARAMS |
String | (empty) | Yes | Extra URL parameters for the authorization request |
SSO_PKCE |
bool | true |
Yes | Use PKCE in OIDC authorization flow |
SSO_AUDIENCE_TRUSTED |
String | (unset) | Yes | Regex for additional trusted ID token audiences |
SSO_MASTER_PASSWORD_POLICY |
String | (unset) | Yes | JSON policy object for SSO master password requirements |
SSO_AUTH_ONLY_NOT_SESSION |
bool | false |
Yes | Use SSO only for auth, not session lifecycle |
SSO_CLIENT_CACHE_EXPIRATION |
u64 | 0 |
Yes | Discovery endpoint cache duration (seconds, 0 = disabled) |
SSO_DEBUG_TOKENS |
bool | false |
Yes | Log SSO tokens (requires debug log level) |
Warning
SSO_CLIENT_ID,SSO_CLIENT_SECRET, andSSO_AUTHORITYmust all be set whenSSO_ENABLED=true. The authority URL must not include/.well-known/openid-configuration.
YubiKey Settings
| Variable | Type | Default | Editable | Description |
|---|---|---|---|---|
YUBICO_CLIENT_ID |
String | (unset) | Yes | YubiCloud API client ID |
YUBICO_SECRET_KEY |
Password | (unset) | Yes | YubiCloud API secret key |
YUBICO_SERVER |
String | (unset) | Yes | Custom YubiKey validation server URL (must be HTTPS) |
Both YUBICO_CLIENT_ID and YUBICO_SECRET_KEY must be set together for custom YubiKey OTP support. Without them, the default YubiCloud service is used.
Duo Settings
| Variable | Type | Default | Editable | Description |
|---|---|---|---|---|
DUO_IKEY |
String | (unset) | Yes | Duo client/integration key |
DUO_SKEY |
Password | (unset) | Yes | Duo secret key |
DUO_HOST |
String | (unset) | Yes | Duo API hostname |
DUO_USE_IFRAME |
bool | false |
No | Use deprecated iframe-based Duo prompt (WebSDK 2) |
All three Duo settings (DUO_IKEY, DUO_SKEY, DUO_HOST) must be set together for global Duo support. Individual users can also configure their own Duo credentials.
SMTP Email Settings
| Variable | Type | Default | Editable | Description |
|---|---|---|---|---|
USE_SENDMAIL |
bool | false |
Yes | Use sendmail command instead of SMTP |
SENDMAIL_COMMAND |
String | (auto-detect) | No | Path to sendmail binary |
SMTP_HOST |
String | (unset) | Yes | SMTP server hostname |
SMTP_SECURITY |
String | starttls |
Yes | starttls, force_tls, or off |
SMTP_PORT |
u16 | (auto) | Yes | SMTP port (auto: 587/465/25 based on security) |
SMTP_FROM |
String | (empty) | Yes | Sender email address |
SMTP_FROM_NAME |
String | Vaultwarden |
Yes | Sender display name |
SMTP_USERNAME |
String | (unset) | Yes | SMTP authentication username |
SMTP_PASSWORD |
Password | (unset) | Yes | SMTP authentication password |
SMTP_AUTH_MECHANISM |
String | (auto) | Yes | Auth methods: Plain, Login, Xoauth2 (comma-separated) |
SMTP_TIMEOUT |
u64 | 15 |
Yes | Connection timeout (seconds) |
HELO_NAME |
String | (hostname) | Yes | Server name sent during SMTP HELO |
SMTP_EMBED_IMAGES |
bool | true |
Yes | Embed images in emails as attachments |
SMTP_DEBUG |
bool | false |
No | Enable detailed SMTP logging (exposes sensitive data) |
SMTP_ACCEPT_INVALID_CERTS |
bool | false |
Yes | Accept invalid TLS certificates (insecure) |
SMTP_ACCEPT_INVALID_HOSTNAMES |
bool | false |
Yes | Accept invalid TLS hostnames (insecure) |
Caution
SMTP_DEBUG=trueoutputs full SMTP session data including credentials. Only use during active troubleshooting and disable immediately after.
Email 2FA Settings
| Variable | Type | Default | Editable | Description |
|---|---|---|---|---|
EMAIL_TOKEN_SIZE |
u8 | 6 |
Yes | Number of digits in email 2FA token (min: 6, max: 255) |
EMAIL_EXPIRATION_TIME |
u64 | 600 |
Yes | Token validity duration (seconds) |
EMAIL_ATTEMPTS_LIMIT |
u64 | 3 |
Yes | Max attempts before token is reset |
EMAIL_2FA_ENFORCE_ON_VERIFIED_INVITE |
bool | false |
Yes | Auto-setup email 2FA on registration |
EMAIL_2FA_AUTO_FALLBACK |
bool | false |
Yes | Auto-enable email 2FA as fallback when needed |
Email 2FA requires a working mail transport (SMTP_HOST or USE_SENDMAIL). It is automatically enabled when SMTP is configured and a mail transport is available.
Other Environment Variables
| Variable | Description |
|---|---|
ENV_FILE |
Path to .env file (default: .env) |
CONFIG_FILE |
Path to config.json (default: {DATA_FOLDER}/config.json) |
ROCKET_PORT |
Listening port (default: 80) |
ROCKET_ADDRESS |
Bind address (default: 0.0.0.0) |
ROCKET_TLS |
Rocket TLS configuration (use reverse proxy instead) |
I_REALLY_WANT_VOLATILE_STORAGE |
Skip persistent volume check in containers |
UMASK |
Set umask before starting (Docker start.sh) |
4. Deployment & Scaling
Docker Deployment
Official images are built for four architectures from both Debian (trixie-slim) and Alpine (3.23) base images.
Production Docker Compose:
services:
vaultwarden:
image: vaultwarden/server:latest
container_name: vaultwarden
restart: unless-stopped
environment:
DOMAIN: "https://vw.example.com"
SIGNUPS_ALLOWED: "false"
ADMIN_TOKEN: "${ADMIN_TOKEN}"
SMTP_HOST: "smtp.example.com"
SMTP_FROM: "[email removed]"
SMTP_PORT: "587"
SMTP_SECURITY: "starttls"
SMTP_USERNAME: "${SMTP_USER}"
SMTP_PASSWORD: "${SMTP_PASS}"
PUSH_ENABLED: "true"
PUSH_INSTALLATION_ID: "${PUSH_ID}"
PUSH_INSTALLATION_KEY: "${PUSH_KEY}"
volumes:
- ./vw-data/:/data/
ports:
- "127.0.0.1:8080:80"
Tip
Bind to
127.0.0.1when running behind a reverse proxy to prevent direct external access to the unencrypted port.
S3 Storage
Set DATA_FOLDER to an S3 URL to store all data (database, attachments, sends, keys) in S3-compatible storage:
DATA_FOLDER=s3://my-vaultwarden-bucket/data
The s3 feature flag must be enabled (included in official Docker images). Vaultwarden uses the standard AWS credential chain via the official AWS SDK for credential resolution, supporting IAM roles, environment variables, SSO profiles, and credential files.
Reverse Proxy Configuration
Vaultwarden requires a reverse proxy for production HTTPS. Key considerations:
- Pass the
X-Real-IPheader (or setIP_HEADERto your proxy's header name) - Forward WebSocket connections for real-time notifications
- The
/aliveendpoint is suitable for health checks
Healthcheck (used in Docker):
curl --insecure --fail --silent --show-error \
"http://localhost:${ROCKET_PORT}/alive"
Source Build for Production
cargo build --release --features "sqlite,enable_mimalloc"
Available feature flags:
| Flag | Purpose |
|---|---|
sqlite |
SQLite database backend |
postgresql |
PostgreSQL database backend |
mysql |
MySQL/MariaDB database backend |
s3 |
S3-compatible object storage |
enable_mimalloc |
Use mimalloc allocator for better performance |
vendored_openssl |
Statically link OpenSSL |
5. Operations
Background Jobs
Vaultwarden runs 9 scheduled background jobs in a dedicated scheduler thread:
| Job | Default Schedule | Purpose |
|---|---|---|
| Send purge | Hourly (:05) | Delete expired Sends |
| Trash purge | Daily (00:05) | Delete trashed items past retention |
| Incomplete 2FA check | Every minute (:30) | Notify on incomplete 2FA logins |
| Emergency request timeout | Hourly (:07) | Auto-grant emergency access requests |
| Emergency reminder | Hourly (:03) | Remind grantors of pending requests |
| Auth request purge | Every minute (:30) | Clean expired auth requests |
| Duo context purge | Every minute (:30) | Clean expired Duo auth contexts |
| Event cleanup | Daily (00:10) | Remove events older than retention period |
| SSO auth purge | Daily (00:20) | Clean incomplete SSO auth records |
Tip
All schedules use 6-field cron syntax (second minute hour day month weekday). Set any schedule to an empty string to disable that specific job. Set
JOB_POLL_INTERVAL_MS=0to disable all jobs.
Backup
SQLite backup methods:
- CLI command:
./vaultwarden backup— createsdb_YYYYMMDD_HHMMSS.sqlite3in the data folder - Signal (Unix):
kill -USR1 $(pidof vaultwarden)— triggers hot backup while running - File copy: Stop the server, copy
db.sqlite3and related WAL/SHM files
Note
The
backupcommand and USR1 signal are SQLite-only. PostgreSQL and MySQL require native database backup tools (pg_dump,mysqldump).
Critical files to back up:
| Path | Contents |
|---|---|
{DATA_FOLDER}/db.sqlite3 |
SQLite database (if using SQLite) |
{DATA_FOLDER}/attachments/ |
Vault item file attachments |
{DATA_FOLDER}/sends/ |
Bitwarden Send files |
{DATA_FOLDER}/rsa_key.pem |
RSA private key for JWT signing |
{DATA_FOLDER}/config.json |
Admin panel configuration overrides |
Monitoring
- Health endpoint:
GET /alive— returns 200 when the server is running - Log levels: Configure via
LOG_LEVELwith per-module overrides
# Enable query logging (slow queries only)
LOG_LEVEL=info,vaultwarden::db::query_logger=warn
# Full query logging
LOG_LEVEL=info,vaultwarden::db::query_logger=debug
Tip
Query logger levels:
warn= queries over 5 seconds,info= queries over 1 second,debug= all queries.
Signal Handling
| Signal | Platform | Action |
|---|---|---|
| SIGINT | Unix | Graceful shutdown |
| SIGTERM | Unix | Graceful shutdown |
| SIGQUIT | Unix | Graceful shutdown |
| SIGUSR1 | Unix (SQLite only) | Trigger SQLite backup |
| SIGHUP | Unix | Reopen log file (for log rotation) |
| Ctrl-C | Windows | Graceful shutdown |
6. Performance
Database Tuning
| Variable | Default | Guidance |
|---|---|---|
DATABASE_MAX_CONNS |
10 |
Increase for high-concurrency deployments (max: 256) |
DATABASE_MIN_CONNS |
2 |
Set to match minimum expected concurrency |
DATABASE_TIMEOUT |
30 |
Connection acquisition timeout (seconds) |
DATABASE_IDLE_TIMEOUT |
600 |
Close idle connections after this many seconds |
DATABASE_CONN_INIT |
(empty) | SQL to run on each new connection (e.g., PRAGMA statements for SQLite) |
ENABLE_DB_WAL |
true |
SQLite WAL mode (better concurrent read performance) |
Memory Optimization
- Enable
mimallocvia theenable_mimallocfeature flag for improved memory allocation performance - The Rocket request limits are fixed: 20 MB for JSON, 525 MB for data-form and file uploads (matches Bitwarden Send max file size)
Icon Caching
| Variable | Effect |
|---|---|
ICON_SERVICE=internal |
Vaultwarden fetches and caches icons locally (default) |
ICON_SERVICE=bitwarden |
Redirect to Bitwarden's icon service |
ICON_SERVICE=duckduckgo |
Redirect to DuckDuckGo's icon service |
ICON_SERVICE=google |
Redirect to Google's favicon service |
DISABLE_ICON_DOWNLOAD=true |
Serve cached icons only, no new downloads |
7. Security
Authentication Architecture
Vaultwarden uses RS256 JWT tokens signed with an auto-generated RSA-2048 key pair stored at {DATA_FOLDER}/rsa_key.pem. If the key file doesn't exist, it is generated automatically on first startup.
Token types and lifetimes:
| Token | Lifetime | Purpose |
|---|---|---|
| Access token | 2 hours | API authentication |
| Refresh token (desktop/web) | 30 days | Token renewal |
| Refresh token (mobile) | 90 days | Token renewal (mobile devices) |
| Invite token | INVITATION_EXPIRATION_HOURS (120h) |
Organization invitations |
| File download token | 5 minutes | Temporary file access |
| Register verify token | 30 minutes | Email verification during registration |
| 2FA remember token | 30 days | Skip 2FA on remembered devices |
| Admin session | ADMIN_SESSION_LIFETIME (20 min) |
Admin panel access |
| Send access token | 2 minutes | Send file download |
Warning
The RSA private key (
rsa_key.pem) is the root of trust for all authentication. If this file is compromised, all tokens can be forged. Protect it with restrictive file permissions and include it in backup procedures.
Rate Limiting
| Endpoint | Seconds Between Requests | Burst Size |
|---|---|---|
| Login / 2FA | LOGIN_RATELIMIT_SECONDS (60) |
LOGIN_RATELIMIT_MAX_BURST (10) |
| Admin panel | ADMIN_RATELIMIT_SECONDS (300) |
ADMIN_RATELIMIT_MAX_BURST (3) |
Admin Panel Security
The admin token supports two formats:
- Argon2id PHC string (recommended): Generated via
vaultwarden hash - Plain text (insecure): Generates a startup warning recommending migration to Argon2id
Set DISABLE_ADMIN_TOKEN=true only when the admin panel is protected by an external authentication layer (e.g., reverse proxy auth).
Network Security
| Setting | Default | Purpose |
|---|---|---|
HTTP_REQUEST_BLOCK_NON_GLOBAL_IPS |
true |
Prevent SSRF by blocking requests to private/reserved IPs |
HTTP_REQUEST_BLOCK_REGEX |
(unset) | Regex to block specific domains/IPs from internal HTTP requests |
IP_HEADER |
X-Real-IP |
Client IP detection header (set to none to use remote IP) |
Caution
Disabling
HTTP_REQUEST_BLOCK_NON_GLOBAL_IPSmay expose internal network services to SSRF attacks through the icon fetching functionality.
Password Security
- Server-side hashing uses PBKDF2-SHA256 with
PASSWORD_ITERATIONS(default: 600,000, minimum: 100,000) - The iteration count is updated on login if the configured value changes
SHOW_PASSWORD_HINT=trueexposes password hints without authentication — not recommended for public instances
Two-Factor Authentication Security
DISABLE_2FA_REMEMBER=trueforces 2FA on every loginAUTHENTICATOR_DISABLE_TIME_DRIFT=trueaccepts only the current 30-second TOTP window- Incomplete 2FA attempts (correct password, missing 2FA) trigger email notifications after
INCOMPLETE_2FA_TIME_LIMITminutes - WebAuthn requires
DOMAINto be set with an HTTPS scheme and a valid domain name
SSO Security Considerations
SSO_ALLOW_UNKNOWN_EMAIL_VERIFICATION=truewithSSO_SIGNUPS_MATCH_EMAIL=truecreates an account takeover risk — only use with trusted identity providersSSO_PKCE=true(default) adds PKCE protection to the OIDC authorization flowSSO_AUDIENCE_TRUSTEDaccepts a regex — validate that it cannot match unintended audiences
8. Developer Internals
Repository Structure
vaultwarden/
├── src/
│ ├── main.rs Entry point, CLI, startup sequence
│ ├── config.rs make_config! macro, env var processing
│ ├── auth.rs JWT authentication, request guards
│ ├── crypto.rs PBKDF2, HMAC, random generation
│ ├── error.rs Error types, API error formatting
│ ├── mail.rs Email sending (SMTP/sendmail)
│ ├── http_client.rs Outbound HTTP with IP blocking
│ ├── ratelimit.rs Rate limiting implementation
│ ├── sso.rs SSO/OIDC flow logic
│ ├── sso_client.rs OIDC client configuration
│ ├── util.rs Shared utilities
│ ├── api/
│ │ ├── mod.rs Route registration
│ │ ├── admin.rs Admin panel routes
│ │ ├── icons.rs Favicon proxy/cache
│ │ ├── notifications.rs WebSocket hub
│ │ ├── web.rs Static file serving
│ │ ├── identity/ Login, token, SSO endpoints
│ │ └── core/
│ │ ├── accounts.rs User account management
│ │ ├── ciphers.rs Vault item CRUD
│ │ ├── collections.rs Collection management
│ │ ├── folders.rs Folder management
│ │ ├── organizations.rs Organization management
│ │ ├── sends.rs Bitwarden Send
│ │ ├── emergency_access.rs Emergency access
│ │ ├── events.rs Event logging
│ │ └── two_factor/ 2FA implementations
│ │ ├── authenticator.rs TOTP
│ │ ├── webauthn.rs WebAuthn/FIDO2
│ │ ├── duo.rs / duo_oidc.rs Duo Security
│ │ ├── yubikey.rs YubiKey OTP
│ │ └── email.rs Email 2FA
│ └── db/
│ ├── mod.rs Database pool, migrations
│ ├── schemas/ Diesel table schemas
│ └── models/ Entity models (User, Cipher, etc.)
├── migrations/ Database migration files per backend
├── macros/ Procedural macros (db_* derives)
├── docker/ Docker build infrastructure
├── playwright/ End-to-end browser tests
└── Cargo.toml Dependencies and feature flags
Configuration System Deep Dive
The make_config! macro in src/config.rs is the central configuration mechanism. It generates:
ConfigBuilder— a struct where all fields areOption<T>, populated from environment variables andconfig.jsonConfigItems— the resolved configuration with concrete types and defaults appliedConfig— the thread-safe wrapper (RwLock<Inner>) exposed as a globalCONFIGstatic- Admin panel data —
prepare_json()serializes all settings for the web admin UI - Support diagnostics —
get_support_json()masks sensitive values for diagnostic output
The config loading order is:
.envfile (via dotenvy)- Environment variables →
ConfigBuilder::from_env() config.json→ConfigBuilder::from_file()- Merge: env ← config.json (config.json overrides env for editable settings)
- Build: apply defaults, auto-computed values, and generated values
- Validate:
validate_config()checks constraints
Request Guard System
Rocket request guards enforce authentication and authorization at the type level:
| Guard | Access Level |
|---|---|
Headers |
Authenticated user with valid JWT |
OrgHeaders |
Authenticated user who is a member of the requested organization |
OrgMemberHeaders |
Non-revoked organization member |
ManagerHeaders |
Confirmed manager with collection access |
ManagerHeadersLoose |
Confirmed manager (no collection check) |
AdminHeaders |
Confirmed admin or owner |
OwnerHeaders |
Confirmed owner only |
ClientHeaders |
Client IP + device type (no auth required) |
ClientIp |
Extracted client IP (honors IP_HEADER) |
API Surface
Vaultwarden implements 268 API endpoints across these mount points:
| Mount | Prefix | Purpose |
|---|---|---|
| Web | / |
Static files, web vault |
| Core API | /api |
Vault operations (ciphers, folders, collections, orgs) |
| Admin | /admin |
Admin panel |
| Events | /events |
Event logging API |
| Identity | /identity |
Authentication, token exchange |
| Icons | /icons |
Favicon proxy |
| Notifications | /notifications |
WebSocket hub |
Database Migrations
Diesel migrations are organized per backend in the migrations/ directory. Migrations run automatically on startup. The application also performs data migrations in code:
TwoFactor::migrate_u2f_to_webauthn()— migrates legacy U2F registrations to WebAuthnTwoFactor::migrate_credential_to_passkey()— migrates credentials to passkey format
Testing
End-to-end tests use Playwright in playwright/ and cover:
- SSO login flows
- 2FA setup and verification (TOTP, Email)
- Organization management
- Collection creation and assignment
Code Patterns
- Error handling: Custom
make_error!macro generates a unifiedErrortype wrapping 20+ source error types witherr!()/err_handler!()macros for ergonomic error creation - Lazy statics:
LazyLockfor global configuration, JWT keys, compiled regexes - Atomic operations:
OnceLockfor RSA keys (initialized once, then immutable) - Template rendering: Handlebars with custom helpers (
case,to_json,webver,vwver) - Feature flags: Compile-time (
cfg(sqlite),cfg(s3)) and runtime (SUPPORTED_FEATURE_FLAGSfor client-side flags)
9. Integration Points
Bitwarden Client Compatibility
Vaultwarden implements the Bitwarden API surface to be compatible with all official clients. The web vault version (v2026.2.0) is bundled and served by the server. Client version detection uses the Bitwarden-Client-Version header.
Push Notification Relay
Push notifications require registration with the Bitwarden push relay service:
- Register at
https://bitwarden.com/hostto get installation ID and key - Set
PUSH_ENABLED=true,PUSH_INSTALLATION_ID, andPUSH_INSTALLATION_KEY - Notifications are relayed through
PUSH_RELAY_URI(default:https://push.bitwarden.com)
OIDC Identity Providers
Vaultwarden supports any OpenID Connect compliant provider. The callback URL is {DOMAIN}/identity/connect/oidc-signin.
Email Integration
28 email templates (HTML + plain text) are built in, covering all user lifecycle events. Templates use Handlebars and can be overridden by placing .hbs files in the templates folder.
HaveIBeenPwned
Set HIBP_API_KEY to enable password breach checking through the HaveIBeenPwned API.
Container Startup Hooks
Docker containers source shell scripts before starting:
/etc/vaultwarden.sh— single initialization script/etc/vaultwarden.d/*.sh— directory of initialization scripts
Legacy paths (/etc/bitwarden_rs.sh, /etc/bitwarden_rs.d/) are supported with deprecation warnings.