Deploying Bitwarden
Bitwarden is a leading open-source password manager and vault service that enables individuals and organizations to securely store, manage, and share passwords, login credentials, and sensitive information. Built with security as the core principle, Bitwarden provides end-to-end encrypted password management without reliance on proprietary cloud services. Whether you’re managing personal passwords, team credentials, or enterprise-wide secrets, Bitwarden delivers transparent, auditable, and completely self-hosted password management infrastructure.
Why Bitwarden?
Bitwarden stands out as the premier choice for password management with exceptional security and transparency:
- End-to-End Encryption: All data encrypted on client side before transmission
- Open Source: Complete source code available for audit and verification
- Zero-Knowledge Architecture: Server cannot decrypt your data
- Self-Hosted: Full control over infrastructure and data
- No Cloud Dependency: Run entirely on your own servers
- Cross-Platform: Available for Windows, Mac, Linux, iOS, Android
- Browser Extensions: Native support for Chrome, Firefox, Safari, Edge
- Organization Management: Create teams and manage shared credentials
- User Roles and Permissions: Fine-grained access control
- Two-Factor Authentication: Support for TOTP, WebAuthn, Duo, and more
- Password Generator: Built-in secure password generation
- Secure Notes: Store encrypted notes and documents
- Identity Management: Store and auto-fill identity information
- Group-Based Access: Collections for organizing shared items
- Audit Logging: Complete access logs for compliance
- LDAP/Active Directory: Enterprise authentication integration
- API Access: Programmatic access to vault data
- Mobile Apps: Full-featured iOS and Android applications
Bitwarden is ideal for individuals seeking privacy, teams requiring credential sharing, enterprises needing compliance-ready password management, and organizations that want complete infrastructure control. With persistent storage on Klutch.sh, all your password data is permanently secured and backed up.
Prerequisites
Before deploying Bitwarden, ensure you have:
- A Klutch.sh account
- A GitHub repository with your Bitwarden deployment configuration
- Basic familiarity with Docker, Git, and security concepts
- A custom domain name (recommended for production)
- SSL/TLS certificate requirements (automatic via Klutch.sh)
- Understanding of password management best practices
- Optional: LDAP or Active Directory for enterprise integration
Important Considerations
Deploying Bitwarden
Create a New Project
Log in to your Klutch.sh dashboard and create a new project for your Bitwarden deployment.
Prepare Your Repository
Create a GitHub repository with the following structure for your Bitwarden deployment:
bitwarden-deploy/├─ Dockerfile├─ .env.example├─ docker-entrypoint.sh├─ nginx.conf├─ .gitignore└─ README.mdHere’s a Dockerfile for Bitwarden:
FROM node:18-alpine AS builder# Install build dependenciesRUN apk add --no-cache \python3 \make \g++ \curl \gitWORKDIR /app# Clone Bitwarden repositoryRUN git clone https://github.com/bitwarden/server.git . && \git checkout master# Build the application (Node.js components)RUN npm ci && npm run buildFROM node:18-alpine# Install runtime dependenciesRUN apk add --no-cache \curl \ca-certificates \openssl \postgresql-clientWORKDIR /app# Copy built application from builderCOPY --from=builder /app /app# Create necessary directoriesRUN mkdir -p /app/data \/app/logs \/app/attachments \/app/keys && \chmod -R 755 /app# Copy entrypoint scriptCOPY docker-entrypoint.sh /RUN chmod +x /docker-entrypoint.sh# Expose portEXPOSE 8000# Health checkHEALTHCHECK --interval=30s --timeout=10s --start-period=60s --retries=3 \CMD curl -f http://localhost:8000/alive || exit 1# Run entrypointENTRYPOINT ["/docker-entrypoint.sh"]CMD ["start"]Create a
docker-entrypoint.shfile:#!/bin/bashset -e# Wait for database if configuredif [ -n "$DATABASE_URL" ]; thenecho "Waiting for database connection..."MAX_ATTEMPTS=30ATTEMPTS=0while [ $ATTEMPTS -lt $MAX_ATTEMPTS ]; doif pg_isready -h $(echo $DATABASE_URL | cut -d/ -f3 | cut -d@ -f2 | cut -d: -f1) \-p $(echo $DATABASE_URL | cut -d: -f4 | cut -d/ -f1) 2>/dev/null; thenecho "Database is ready!"breakfiATTEMPTS=$((ATTEMPTS + 1))sleep 1donefi# Generate encryption keys if not presentif [ ! -f /app/keys/encryption.key ]; thenecho "Generating encryption keys..."openssl rand -out /app/keys/encryption.key 32chmod 600 /app/keys/encryption.keyfi# Initialize databaseif [ ! -f /app/data/initialized ]; thenecho "Initializing database..."npm run db:migrate || truetouch /app/data/initializedfi# Set permissionschmod -R 755 /appif [ "$1" = "start" ]; then# Start the applicationnpm startelseexec "$@"fiCreate a
.env.examplefile:Terminal window # Application ConfigurationNODE_ENV=productionAPP_PORT=8000APP_URL=https://vault.yourdomain.comDOMAIN=yourdomain.comLOG_LEVEL=info# Database ConfigurationDATABASE_URL=postgresql://bitwarden_user:password@db:5432/bitwardenDATABASE_TYPE=postgresqlDATABASE_POOL_SIZE=20# Encryption ConfigurationENCRYPTION_KEY_PATH=/app/keys/encryption.keyJWT_SECRET=your-jwt-secret-key-hereADMIN_TOKEN=your-secure-admin-token# Admin SettingsADMIN_PASSWORD=secure_password_hereADMIN_EMAIL=admin@yourdomain.com# User RegistrationALLOW_USER_REGISTRATION=trueALLOW_ORGANIZATION_CREATION=trueINVITE_EXPIRES_DAYS=7# Email ConfigurationMAIL_FROM=noreply@yourdomain.comMAIL_FROM_NAME=Bitwarden VaultSMTP_HOST=smtp.gmail.comSMTP_PORT=587SMTP_USERNAME=your-email@gmail.comSMTP_PASSWORD=your-app-passwordSMTP_SECURE=trueSMTP_TIMEOUT=10# Security ConfigurationREQUIRE_HTTPS=trueSESSION_TIMEOUT_MINUTES=30MASTER_PASSWORD_MINIMUM_LENGTH=8PASSWORD_COMPLEXITY=trueREQUIRE_TWO_FACTOR=false# Two-Factor AuthenticationENABLE_TOTP=trueENABLE_WEBAUTHN=trueENABLE_DUO=false# LDAP Configuration (Optional)LDAP_ENABLED=falseLDAP_SERVER=ldap://ldap.yourdomain.comLDAP_BASE_DN=dc=yourdomain,dc=comLDAP_BIND_DN=cn=admin,dc=yourdomain,dc=comLDAP_BIND_PASSWORD=password# Organization SettingsMAX_USERS_PER_ORGANIZATION=1000MAX_ORGANIZATIONS_PER_USER=10# API ConfigurationAPI_ENABLED=trueAPI_KEY_ENABLED=true# LoggingLOG_FILE=/app/logs/bitwarden.logLOG_SIZE_LIMIT=104857600LOG_RETENTION_DAYS=30# BackupBACKUP_ENABLED=trueBACKUP_SCHEDULE=0 2 * * *BACKUP_RETENTION_DAYS=30Commit and push to your GitHub repository:
Terminal window git initgit add .git commit -m "Initial Bitwarden deployment"git remote add origin https://github.com/yourusername/bitwarden-deploy.gitgit push -u origin mainCreate a New App
In the Klutch.sh dashboard:
- Click “Create New App”
- Select your GitHub repository containing the Dockerfile
- Choose the branch (typically
mainormaster) - Klutch.sh will automatically detect the Dockerfile in the root directory
Configure Environment Variables
Set up these essential environment variables in your Klutch.sh dashboard:
Variable Description Example APP_URLYour Bitwarden domain https://vault.yourdomain.comAPP_PORTApplication port 8000NODE_ENVEnvironment type productionDATABASE_URLDatabase connection postgresql://user:pass@host/dbJWT_SECRETJWT signing secret secure-random-stringADMIN_TOKENAdmin API token (generate secure) secure-tokenADMIN_PASSWORDAdmin account password secure_passwordADMIN_EMAILAdmin email address admin@yourdomain.comENCRYPTION_KEY_PATHPath to encryption key /app/keys/encryption.keyALLOW_USER_REGISTRATIONAllow new user signups trueSMTP_HOSTSMTP server hostname smtp.gmail.comSMTP_PORTSMTP port 587SMTP_USERNAMESMTP username your-email@gmail.comSMTP_PASSWORDSMTP password app_passwordMAIL_FROMEmail sender address noreply@yourdomain.comSESSION_TIMEOUT_MINUTESSession timeout duration 30ENABLE_TOTPEnable TOTP 2FA trueENABLE_WEBAUTHNEnable WebAuthn/FIDO2 trueLOG_LEVELLogging verbosity infoConfigure Persistent Storage
Bitwarden requires persistent storage for vault data, attachments, and keys. Add persistent volumes:
Mount Path Description Recommended Size /app/dataVault database and configuration 100GB+ /app/keysEncryption keys and certificates 5GB /app/attachmentsUser file attachments 500GB+ /app/logsApplication logs 20GB In the Klutch.sh dashboard:
- Navigate to your app settings
- Go to the “Volumes” section
- Click “Add Volume” for each mount path
- Set mount paths and sizes as specified above
Set Network Configuration
Configure your app’s network settings:
- Select traffic type: HTTP (Bitwarden uses standard web ports)
- Recommended internal port: 8000 (as specified in Dockerfile)
- Klutch.sh will automatically handle HTTPS termination via reverse proxy
- Ensure ports 80 and 443 are accessible from your domain
Configure Custom Domain
Bitwarden requires a custom domain for secure operation:
- Navigate to your app’s “Domains” section in Klutch.sh
- Click “Add Custom Domain”
- Enter your domain (e.g.,
vault.yourdomain.com) - Configure DNS with a CNAME record to point to your Klutch.sh app
- Update
APP_URLenvironment variable to match your domain - Klutch.sh will automatically provision SSL certificates
Deploy Your App
- Review all settings and environment variables
- Click “Deploy”
- Klutch.sh will build the Docker image and start your Bitwarden instance
- Wait for the deployment to complete (typically 5-10 minutes)
- Access your Bitwarden instance at your configured domain
- Create your admin account and begin configuring users
Initial Setup and Configuration
After deployment completes, access your Bitwarden instance to complete setup.
Accessing Bitwarden Admin Panel
Navigate to your domain with /admin path: https://vault.yourdomain.com/admin/
Log in with the admin token or admin account you configured.
Creating Your First User Account
- Navigate to your Bitwarden instance:
https://vault.yourdomain.com - Click “Create Account”
- Enter email and strong master password
- Confirm your email address
- Log in with your credentials
- Set up two-factor authentication for security
Setting Up Two-Factor Authentication
Enhance security with multiple authentication methods:
- Log in to your account
- Navigate to “Settings” → “Two-step Login”
- Choose authentication method:
- Authenticator (TOTP): Use apps like Google Authenticator
- WebAuthn/FIDO2: Use hardware security keys
- Email: Receive codes via email
- Remember device: Optional trusted device tracking
- Complete setup process and save backup codes
- Logout and re-login to verify 2FA works
Importing Passwords
Migrate existing passwords to Bitwarden:
- Export passwords from your existing password manager
- Log in to Bitwarden
- Navigate to “Tools” → “Import Data”
- Select your source password manager
- Upload exported file
- Review and confirm import
- Organize imported items into collections
Creating an Organization
Set up team password sharing:
- Log in to your account
- Navigate to “Organizations” → “New Organization”
- Name your organization
- Select billing plan
- Add organization users
- Create collections for shared passwords
- Assign users to collections
- Configure roles and permissions
Managing Collections
Organize shared passwords with collections:
- In organization settings, go to “Collections”
- Click “New Collection”
- Name the collection
- Set collection access:
- Read-only access
- Read and write access
- Admin access
- Assign users to collection
- Move items to collection
- Manage collection permissions
Environment Variable Examples
Basic Configuration
APP_URL=https://vault.yourdomain.comAPP_PORT=8000NODE_ENV=productionDATABASE_URL=postgresql://user:password@db:5432/bitwardenJWT_SECRET=your-secure-jwt-secretADMIN_TOKEN=your-secure-admin-tokenALLOW_USER_REGISTRATION=trueSMTP_HOST=smtp.gmail.comSMTP_PORT=587Complete Production Configuration
# Application SettingsAPP_URL=https://vault.yourdomain.comAPP_PORT=8000NODE_ENV=productionDOMAIN=yourdomain.comLOG_LEVEL=infoLOG_FILE=/app/logs/bitwarden.logLOG_RETENTION_DAYS=30
# Database ConfigurationDATABASE_URL=postgresql://bitwarden_user:secure_password@db:5432/bitwardenDATABASE_TYPE=postgresqlDATABASE_POOL_SIZE=20DATABASE_TIMEOUT=30
# Encryption and SecurityENCRYPTION_KEY_PATH=/app/keys/encryption.keyJWT_SECRET=your-secure-jwt-secret-keyADMIN_TOKEN=your-secure-admin-api-tokenREQUIRE_HTTPS=true
# Admin ConfigurationADMIN_PASSWORD=secure_admin_passwordADMIN_EMAIL=admin@yourdomain.com
# User ManagementALLOW_USER_REGISTRATION=trueALLOW_ORGANIZATION_CREATION=trueINVITE_EXPIRES_DAYS=7SESSION_TIMEOUT_MINUTES=30MASTER_PASSWORD_MINIMUM_LENGTH=8PASSWORD_COMPLEXITY=true
# Email ConfigurationMAIL_FROM=noreply@yourdomain.comMAIL_FROM_NAME=Bitwarden VaultSMTP_HOST=smtp.gmail.comSMTP_PORT=587SMTP_USERNAME=your-email@gmail.comSMTP_PASSWORD=your-app-passwordSMTP_SECURE=trueSMTP_TIMEOUT=10
# Two-Factor AuthenticationENABLE_TOTP=trueENABLE_WEBAUTHN=trueENABLE_BACKUP_CODES=true
# Organization SettingsMAX_USERS_PER_ORGANIZATION=1000MAX_ORGANIZATIONS_PER_USER=10
# API ConfigurationAPI_ENABLED=trueAPI_KEY_ENABLED=trueAPI_RATE_LIMIT=100
# Backup ConfigurationBACKUP_ENABLED=trueBACKUP_SCHEDULE=0 2 * * *BACKUP_RETENTION_DAYS=30BACKUP_PATH=/app/backups
# LDAP Configuration (Optional)LDAP_ENABLED=falseLDAP_SERVER=ldap://ldap.yourdomain.comLDAP_BASE_DN=dc=yourdomain,dc=comLDAP_BIND_DN=cn=admin,dc=yourdomain,dc=comLDAP_BIND_PASSWORD=passwordLDAP_SYNC_INTERVAL_HOURS=24Sample Code and Getting Started
Python - Bitwarden API Integration
import requestsimport jsonfrom typing import Optional, List, Dict
class BitwardenClient: def __init__(self, base_url: str, admin_token: str): self.base_url = base_url.rstrip('/') self.admin_token = admin_token self.api_url = f'{self.base_url}/api' self.headers = { 'Content-Type': 'application/json', 'Authorization': f'Bearer {admin_token}' }
def create_user(self, email: str, password: str, name: str = None) -> Optional[Dict]: """Create a new user account""" try: payload = { 'email': email, 'password': password, 'name': name or email.split('@')[0] }
response = requests.post( f'{self.api_url}/accounts/register', headers=self.headers, json=payload ) response.raise_for_status() return response.json() except requests.exceptions.RequestException as e: print(f"Error creating user: {e}") return None
def invite_user_to_organization(self, org_id: str, email: str, role: str = 'User') -> Optional[Dict]: """Invite user to organization""" try: payload = { 'emails': [email], 'type': self._role_to_type(role) }
response = requests.post( f'{self.api_url}/organizations/{org_id}/users/invite', headers=self.headers, json=payload ) response.raise_for_status() return response.json() except requests.exceptions.RequestException as e: print(f"Error inviting user: {e}") return None
def list_organizations(self) -> Optional[List[Dict]]: """List all organizations""" try: response = requests.get( f'{self.api_url}/organizations', headers=self.headers ) response.raise_for_status() return response.json().get('data', []) except requests.exceptions.RequestException as e: print(f"Error listing organizations: {e}") return None
def create_organization(self, name: str, billing_email: str) -> Optional[Dict]: """Create a new organization""" try: payload = { 'name': name, 'billingEmail': billing_email }
response = requests.post( f'{self.api_url}/organizations', headers=self.headers, json=payload ) response.raise_for_status() return response.json() except requests.exceptions.RequestException as e: print(f"Error creating organization: {e}") return None
def create_collection(self, org_id: str, name: str) -> Optional[Dict]: """Create a collection in organization""" try: payload = { 'name': name, 'externalId': None }
response = requests.post( f'{self.api_url}/organizations/{org_id}/collections', headers=self.headers, json=payload ) response.raise_for_status() return response.json() except requests.exceptions.RequestException as e: print(f"Error creating collection: {e}") return None
def list_users(self) -> Optional[List[Dict]]: """List all users in system""" try: response = requests.get( f'{self.api_url}/users', headers=self.headers ) response.raise_for_status() return response.json().get('data', []) except requests.exceptions.RequestException as e: print(f"Error listing users: {e}") return None
def deactivate_user(self, user_id: str) -> bool: """Deactivate a user account""" try: response = requests.post( f'{self.api_url}/users/{user_id}/deactivate', headers=self.headers ) response.raise_for_status() return True except requests.exceptions.RequestException as e: print(f"Error deactivating user: {e}") return False
def export_organization_vault(self, org_id: str, format: str = 'json') -> Optional[bytes]: """Export organization vault data""" try: response = requests.get( f'{self.api_url}/organizations/{org_id}/export', headers=self.headers, params={'format': format} ) response.raise_for_status() return response.content except requests.exceptions.RequestException as e: print(f"Error exporting vault: {e}") return None
@staticmethod def _role_to_type(role: str) -> int: """Convert role name to type integer""" role_map = { 'Owner': 0, 'Admin': 1, 'User': 2, 'Manager': 3, 'Provider': 4 } return role_map.get(role, 2)
# Usage exampleif __name__ == "__main__": client = BitwardenClient('https://vault.yourdomain.com', admin_token='your-admin-token')
# Create a new organization org = client.create_organization('Acme Corp', 'admin@acmecorp.com') if org: org_id = org['id'] print(f"Organization created: {org_id}")
# Create a collection collection = client.create_collection(org_id, 'Engineering Team') print(f"Collection created: {collection}")
# Invite users users_to_invite = ['engineer1@acmecorp.com', 'engineer2@acmecorp.com'] for email in users_to_invite: result = client.invite_user_to_organization(org_id, email, 'User') print(f"Invited {email}: {result}")
# List all users all_users = client.list_users() print(f"Total users: {len(all_users) if all_users else 0}")
# Export vault vault_export = client.export_organization_vault(org_id) if vault_export: with open('vault_backup.json', 'wb') as f: f.write(vault_export)JavaScript - Bitwarden Client Setup
const API_BASE_URL = 'https://vault.yourdomain.com/api';
class BitwardenVaultManager { constructor(email, masterPassword) { this.email = email; this.masterPassword = masterPassword; this.accessToken = null; this.refreshToken = null; }
async login() { try { const response = await fetch(`${API_BASE_URL}/accounts/login`, { method: 'POST', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify({ email: this.email, masterPassword: this.masterPassword, rememberMe: false }) });
if (!response.ok) throw new Error('Login failed');
const data = await response.json(); this.accessToken = data.access_token; this.refreshToken = data.refresh_token; return true; } catch (error) { console.error('Login error:', error); return false; } }
async getVault() { try { const response = await fetch(`${API_BASE_URL}/ciphers`, { headers: { 'Authorization': `Bearer ${this.accessToken}`, 'Content-Type': 'application/json' } });
if (!response.ok) throw new Error('Failed to fetch vault'); const data = await response.json(); return data.data || []; } catch (error) { console.error('Vault fetch error:', error); return []; } }
async createCipher(name, login, password, folderId = null) { try { const response = await fetch(`${API_BASE_URL}/ciphers`, { method: 'POST', headers: { 'Authorization': `Bearer ${this.accessToken}`, 'Content-Type': 'application/json' }, body: JSON.stringify({ type: 1, // Login type name: name, login: { uri: login.uri || null, username: login.username, password: password, totp: login.totp || null }, folderId: folderId, favorite: false }) });
if (!response.ok) throw new Error('Failed to create cipher'); return await response.json(); } catch (error) { console.error('Create cipher error:', error); return null; } }
async searchVault(query) { const vault = await this.getVault(); return vault.filter(cipher => cipher.name.toLowerCase().includes(query.toLowerCase()) || (cipher.login && cipher.login.username.toLowerCase().includes(query.toLowerCase())) ); }
async generatePassword(length = 16, uppercase = true, lowercase = true, numbers = true, symbols = true) { try { const response = await fetch(`${API_BASE_URL}/tools/password`, { method: 'POST', headers: { 'Authorization': `Bearer ${this.accessToken}`, 'Content-Type': 'application/json' }, body: JSON.stringify({ length: length, uppercase: uppercase, lowercase: lowercase, numbers: numbers, symbols: symbols }) });
if (!response.ok) throw new Error('Failed to generate password'); const data = await response.json(); return data.password; } catch (error) { console.error('Password generation error:', error); return null; } }
async deleteVaultItem(cipherId) { try { const response = await fetch(`${API_BASE_URL}/ciphers/${cipherId}`, { method: 'DELETE', headers: { 'Authorization': `Bearer ${this.accessToken}`, 'Content-Type': 'application/json' } });
if (!response.ok) throw new Error('Failed to delete item'); return true; } catch (error) { console.error('Delete error:', error); return false; } }}
// Usage example(async () => { const manager = new BitwardenVaultManager('user@yourdomain.com', 'masterPassword');
// Login const loggedIn = await manager.login(); if (!loggedIn) { console.error('Failed to login'); return; }
// Get vault items const vault = await manager.getVault(); console.log(`Vault contains ${vault.length} items`);
// Create new password item const newPassword = await manager.generatePassword(20); const cipher = await manager.createCipher( 'GitHub Account', { username: 'user@github.com', uri: 'https://github.com' }, newPassword ); console.log('Created cipher:', cipher);
// Search vault const results = await manager.searchVault('github'); console.log('Search results:', results);})();cURL - API Integration Examples
# Login to Bitwardencurl -X POST https://vault.yourdomain.com/api/accounts/login \ -H "Content-Type: application/json" \ -d '{ "email": "user@yourdomain.com", "masterPassword": "your-master-password" }'
# Create new usercurl -X POST https://vault.yourdomain.com/api/accounts/register \ -H "Content-Type: application/json" \ -H "Authorization: Bearer YOUR_ADMIN_TOKEN" \ -d '{ "email": "newuser@yourdomain.com", "password": "secure-password", "name": "New User" }'
# Get vault itemscurl -X GET https://vault.yourdomain.com/api/ciphers \ -H "Authorization: Bearer YOUR_ACCESS_TOKEN"
# Create new password itemcurl -X POST https://vault.yourdomain.com/api/ciphers \ -H "Content-Type: application/json" \ -H "Authorization: Bearer YOUR_ACCESS_TOKEN" \ -d '{ "type": 1, "name": "Gmail Account", "login": { "username": "yourname@gmail.com", "password": "your-password", "uri": "https://mail.google.com" } }'
# Generate secure passwordcurl -X POST https://vault.yourdomain.com/api/tools/password \ -H "Content-Type: application/json" \ -H "Authorization: Bearer YOUR_ACCESS_TOKEN" \ -d '{ "length": 20, "uppercase": true, "lowercase": true, "numbers": true, "symbols": true }'
# List organizationscurl -X GET https://vault.yourdomain.com/api/organizations \ -H "Authorization: Bearer YOUR_ADMIN_TOKEN"
# Invite user to organizationcurl -X POST https://vault.yourdomain.com/api/organizations/{org-id}/users/invite \ -H "Content-Type: application/json" \ -H "Authorization: Bearer YOUR_ADMIN_TOKEN" \ -d '{ "emails": ["user@yourdomain.com"], "type": 2 }'
# Export organization vaultcurl -X GET "https://vault.yourdomain.com/api/organizations/{org-id}/export?format=json" \ -H "Authorization: Bearer YOUR_ADMIN_TOKEN" \ -o vault_backup.jsonBash - Backup Script
#!/bin/bash
# Bitwarden Backup ScriptBACKUP_DIR="/backups/bitwarden"TIMESTAMP=$(date +%Y%m%d_%H%M%S)RETENTION_DAYS=30
# Create backup directorymkdir -p $BACKUP_DIR
# Database backupecho "Backing up Bitwarden database..."PGPASSWORD="${DATABASE_PASSWORD}" pg_dump -h ${DATABASE_HOST} \ -U ${DATABASE_USER} -d ${DATABASE_NAME} \ > $BACKUP_DIR/bitwarden_db_$TIMESTAMP.sql
# Attachments backupecho "Backing up vault attachments..."tar -czf $BACKUP_DIR/bitwarden_attachments_$TIMESTAMP.tar.gz \ /app/attachments
# Configuration backupecho "Backing up configuration..."tar -czf $BACKUP_DIR/bitwarden_config_$TIMESTAMP.tar.gz \ /app/keys /app/data
# Encrypt backupsecho "Encrypting backups..."openssl enc -aes-256-cbc -salt -in \ $BACKUP_DIR/bitwarden_db_$TIMESTAMP.sql \ -out $BACKUP_DIR/bitwarden_db_$TIMESTAMP.sql.enc \ -pass pass:${BACKUP_ENCRYPTION_PASSWORD}
# Remove unencrypted database backuprm $BACKUP_DIR/bitwarden_db_$TIMESTAMP.sql
# Cleanup old backupsecho "Cleaning up old backups..."find $BACKUP_DIR -name "bitwarden_*" -mtime +$RETENTION_DAYS -delete
# Backup summaryecho "Backup completed: $TIMESTAMP"ls -lh $BACKUP_DIR | tail -5
# Optional: Upload to cloud storage (S3 example)# aws s3 cp $BACKUP_DIR/bitwarden_db_$TIMESTAMP.sql.enc s3://your-bucket/backups/# aws s3 cp $BACKUP_DIR/bitwarden_attachments_$TIMESTAMP.tar.gz s3://your-bucket/backups/# aws s3 cp $BACKUP_DIR/bitwarden_config_$TIMESTAMP.tar.gz s3://your-bucket/backups/Docker Compose for Local Development
For local testing before deploying to Klutch.sh:
version: '3.8'
services: bitwarden: build: . container_name: bitwarden-app environment: APP_URL: http://localhost:8000 APP_PORT: 8000 NODE_ENV: development DATABASE_URL: postgresql://bitwarden:bitwarden@db:5432/bitwarden JWT_SECRET: dev-secret-key-change-in-production ADMIN_TOKEN: dev-admin-token-change-in-production ADMIN_PASSWORD: admin123 ADMIN_EMAIL: admin@localhost ALLOW_USER_REGISTRATION: "true" MAIL_FROM: noreply@localhost SMTP_HOST: mailhog SMTP_PORT: 1025 ports: - "8000:8000" volumes: - ./:/app - ./data:/app/data - ./attachments:/app/attachments - ./logs:/app/logs - ./keys:/app/keys depends_on: - db - mailhog restart: unless-stopped
db: image: postgres:15-alpine container_name: bitwarden-db environment: POSTGRES_USER: bitwarden POSTGRES_PASSWORD: bitwarden POSTGRES_DB: bitwarden ports: - "5432:5432" volumes: - postgres_data:/var/lib/postgresql/data restart: unless-stopped
mailhog: image: mailhog/mailhog:latest container_name: bitwarden-mailhog ports: - "1025:1025" - "8025:8025" restart: unless-stopped
volumes: postgres_data:To run locally:
docker-compose up -dAccess Bitwarden at http://localhost:8000
Access MailHog (test email) at http://localhost:8025
User Management and Administration
Managing User Accounts
Administer users through the admin panel:
- Navigate to admin panel:
https://vault.yourdomain.com/admin/ - Go to “Users” section
- View all user accounts and activity
- Enable/disable user accounts
- Reset user passwords (users can change)
- Delete users and their data
- View user login history
Organization Administration
Manage organizations and access control:
- Create organizations for different teams
- Set organization name and settings
- Manage organization members
- Assign user roles (Owner, Admin, User)
- Create and manage collections
- Configure collection access permissions
- View organization activity logs
User Roles and Permissions
Configure fine-grained access control:
| Role | Permissions |
|---|---|
| Owner | Full organization control, user management, billing |
| Admin | Manage users, create collections, view logs |
| Manager | Manage collections and users within collections |
| User | Access assigned collections, create personal items |
Audit Logging
Monitor all vault activity:
- Access admin panel logs
- Review user login attempts
- Track password item access
- Monitor organization changes
- Audit user invitations and removals
- Export audit logs for compliance
Security Best Practices
Master Password Requirements
Enforce strong master passwords:
- Minimum length: 8-12 characters
- Require uppercase, lowercase, numbers, symbols
- Prevent common passwords
- Force periodic password changes
- Educate users on password strength
Two-Factor Authentication
Require 2FA for enhanced security:
- Enable TOTP authenticator apps
- Enable WebAuthn/FIDO2 security keys
- Backup codes for account recovery
- Enforce 2FA for organization admins
- Optional: Require 2FA for all users
Data Encryption
Bitwarden’s encryption model:
- Client-side encryption of all vault data
- Master password never stored on server
- AES-256 encryption standard
- RSA 4096 for encryption keys
- End-to-end encryption for sharing
Access Control
Implement strong access policies:
- Principle of least privilege
- LDAP/Active Directory integration
- Single Sign-On (SSO) for enterprises
- IP whitelisting for admin access
- Session timeout configuration
- Disable user registration if needed
Network Security
Secure your Bitwarden deployment:
- HTTPS required for all connections
- Strong SSL/TLS certificates (automatic via Klutch.sh)
- Firewall rules limiting access
- Rate limiting on authentication endpoints
- DDoS protection considerations
- Regular security patching
Backup Security
Protect your backup data:
- Encrypt backups with strong passwords
- Store offline copies for disaster recovery
- Test restore procedures regularly
- Maintain backup access logs
- Secure backup storage location
- Automated backup retention policies
Monitoring and Performance
Database Optimization
Optimize database performance:
-- Create indexes for common queriesCREATE INDEX idx_user_email ON users(email);CREATE INDEX idx_cipher_organization ON ciphers(organization_id);CREATE INDEX idx_folder_user ON folders(user_id);CREATE INDEX idx_organization_user ON organization_users(user_id);
-- Analyze query performanceANALYZE;Performance Monitoring
Monitor application health:
- Response time metrics
- Database query performance
- API endpoint usage
- User activity metrics
- Storage utilization
- CPU and memory usage
Health Checks
Monitor deployment status:
# Check if Bitwarden is respondingcurl -s -o /dev/null -w "%{http_code}" https://vault.yourdomain.com/alive
# Expected response: 200Troubleshooting
Common Issues and Solutions
Issue: Cannot login to vault
Solutions:
- Verify username/email is correct
- Ensure master password is correct
- Check if account is locked
- Review email for account reset link
- Verify database connection
- Check application logs for errors
Issue: 2FA not working
Troubleshooting:
- Verify TOTP app time is synchronized
- Generate new backup codes
- Try alternative 2FA method
- Check email receiving capability
- Verify SMTP configuration
Issue: Slow vault access
Solutions:
- Check database performance
- Optimize SQL queries
- Enable caching
- Review application logs
- Check system resources
- Verify network connectivity
Issue: Users cannot register
Solutions:
- Verify
ALLOW_USER_REGISTRATIONis true - Check email configuration
- Review registration logs
- Check SMTP settings
- Verify email is valid format
- Check for rate limiting
Updating Bitwarden
To update Bitwarden to a newer version:
-
Update your Dockerfile:
RUN git clone https://github.com/bitwarden/server.git . && \git checkout latest-version -
Commit and push to GitHub
-
Klutch.sh will automatically rebuild
-
Always backup database before upgrading
-
Test in development first
-
Monitor logs after upgrade
-
Verify all features working correctly
Use Cases
Individual Password Management
- Secure personal password storage
- Password sharing with family
- Multi-device access
- Emergency contacts vault items
Team Credential Management
- Shared password access
- Role-based access control
- Secure API key storage
- Database credential management
Enterprise Deployments
- Company-wide password management
- LDAP/AD integration
- Compliance audit trails
- Department-based organization structure
Development Team Secrets
- API key management
- Database credentials
- SSH keys storage
- Environment secrets
Additional Resources
- Bitwarden Server GitHub Repository - Source code and documentation
- Bitwarden Help Documentation - User guides and FAQ
- Bitwarden Client Downloads - Browser extensions and mobile apps
- Bitwarden Issues - Bug reports and feature requests
- Bitwarden Community Forum - Community discussions
- Klutch.sh Getting Started Guide
- Klutch.sh Volumes Documentation
- Klutch.sh Custom Domains Guide
Conclusion
Deploying Bitwarden on Klutch.sh provides you with a secure, transparent, and self-hosted password management solution that maintains complete control over your sensitive credentials. With end-to-end encryption, comprehensive user management, organization support, two-factor authentication, and audit logging, Bitwarden enables organizations and individuals to manage passwords with complete confidence. Klutch.sh’s managed infrastructure ensures your password vault is always available, secure, and performant, while maintaining privacy through open-source code you can audit.
Start hosting your secure password vault today by deploying Bitwarden on Klutch.sh and take control of your credential management.