Privacy Tools Guide

Migrating your passwords from a local KeePass database to Bitwarden’s cloud vault is a practical upgrade if you need cross-device synchronization, easier sharing with family or team members, or a more accessible backup solution. This guide walks you through the entire process using command-line tools and automation scripts, ideal for developers who prefer terminal-based workflows.

Why Migrate From KeePass to Bitwarden

KeePass and KeePassXC store your password database in an encrypted file (.kdbx) on your local machine. This approach gives you full control over your data but lacks native cloud synchronization. You likely sync your database manually or through a service like Dropbox, which can create version conflicts and leaves your vault vulnerable if your sync folder is compromised.

Bitwarden stores your encrypted vault on their servers while maintaining zero-knowledge encryption—your master password never leaves your device. The cloud approach means your passwords are available instantly on any device without manual file management.

Prerequisites

Before starting the migration, ensure you have:

Install the Bitwarden CLI:

# macOS
brew install bitwarden-cli

# Ubuntu/Debian
sudo apt install bitwarden

# Or download from https://github.com/bitwarden/cli/releases

Step 1: Export Your KeePass Database

KeePass provides a built-in export feature, but for programmatic access, you have two reliable options.

Option A: Using KeePassXC CLI

KeePassXC includes keepassxc-cli for command-line operations:

# Export database to XML (unencrypted)
keepassxc-cli export -o keepass_export.xml your_database.kdbx

You’ll be prompted for your master password. The XML output contains all entries with fields like username, password, URL, notes, and custom attributes.

Option B: Using Python with pykeepass

For more control, use the pykeepass library:

pip install pykeepass

Create a Python script to extract your entries:

from pykeepass import PyKeePass

kp = PyKeePass('your_database.kdbx', password='your_master_password')

entries = []
for entry in kp.entries:
    entries.append({
        'title': entry.title,
        'username': entry.username,
        'password': entry.password,
        'url': entry.url,
        'notes': entry.notes,
        'tags': entry.tags
    })

# Output to JSON for easier parsing
import json
with open('keepass_export.json', 'w') as f:
    json.dump(entries, f, indent=2)

This approach preserves custom fields and allows selective migration based on tags or folders.

Step 2: Authenticate with Bitwarden CLI

Initialize the Bitwarden CLI and log in:

bw login your@email.com

You’ll receive a code to complete authentication via browser. For CI/CD environments, use an API key:

bw config apiurl https://api.bitwarden.com
bw login --apikey

Set up your encryption key:

bw unlock

The CLI returns a session key—store this securely. You can also use the BW_SESSION environment variable:

export BW_SESSION="your_session_key"

Step 3: Import Entries to Bitwarden

Bitwarden supports CSV import, which works well for most migrations. Generate a CSV from your export:

import json
import csv

with open('keepass_export.json', 'r') as f:
    entries = json.load(f)

with open('bitwarden_import.csv', 'w', newline='') as f:
    writer = csv.writer(f)
    writer.writerow(['title', 'username', 'password', 'url', 'notes', 'folder'])

    for entry in entries:
        folder = entry.get('tags', [''])[0] if entry.get('tags') else ''
        writer.writerow([
            entry.get('title', ''),
            entry.get('username', ''),
            entry.get('password', ''),
            entry.get('url', ''),
            entry.get('notes', ''),
            folder
        ])

Import using the Bitwarden CLI:

bw import bitwarden_import.csv --formats keepass

The --formats keepass flag tells Bitwarden to map KeePass fields correctly.

Step 4: Automate the Full Migration

For a complete automation solution, here’s a consolidated Python script:

#!/usr/bin/env python3
import os
import json
import csv
import subprocess
import sys
from pykeepass import PyKeePass

def export_keepass(db_path, password, output_json):
    """Export KeePass database to JSON."""
    kp = PyKeePass(db_path, password=password)
    entries = []

    for entry in kp.entries:
        entries.append({
            'title': entry.title,
            'username': entry.username,
            'password': entry.password,
            'url': entry.url,
            'notes': entry.notes,
            'tags': entry.tags,
            'custom_fields': [
                {'name': cf.key, 'value': cf.value}
                for cf in entry.custom_properties
            ]
        })

    with open(output_json, 'w') as f:
        json.dump(entries, f, indent=2)

    return len(entries)

def json_to_csv(json_file, csv_file):
    """Convert JSON export to Bitwarden CSV format."""
    with open(json_file, 'r') as f:
        entries = json.load(f)

    with open(csv_file, 'w', newline='') as f:
        writer = csv.writer(f)
        writer.writerow(['title', 'username', 'password', 'url', 'notes', 'folder'])

        for entry in entries:
            folder = entry.get('tags', [''])[0] if entry.get('tags') else ''
            writer.writerow([
                entry.get('title', ''),
                entry.get('username', ''),
                entry.get('password', ''),
                entry.get('url', ''),
                entry.get('notes', ''),
                folder
            ])

def import_to_bitwarden(csv_file):
    """Import CSV to Bitwarden using CLI."""
    result = subprocess.run(
        ['bw', 'import', csv_file, '--formats', 'keepass'],
        capture_output=True, text=True
    )
    return result.returncode == 0

def main():
    if len(sys.argv) != 4:
        print("Usage: migrate.py <keepass.db> <password> <output_dir>")
        sys.exit(1)

    db_path, password, output_dir = sys.argv[1], sys.argv[2], sys.argv[3]
    os.makedirs(output_dir, exist_ok=True)

    json_file = os.path.join(output_dir, 'keepass_export.json')
    csv_file = os.path.join(output_dir, 'bitwarden_import.csv')

    print(f"Exporting {db_path}...")
    count = export_keepass(db_path, password, json_file)
    print(f"Exported {count} entries")

    print("Converting to CSV...")
    json_to_csv(json_file, csv_file)

    print("Importing to Bitwarden...")
    if import_to_bitwarden(csv_file):
        print("Migration completed successfully!")
    else:
        print("Import failed. Check Bitwarden CLI authentication.")

if __name__ == '__main__':
    main()

Run the migration:

python migrate.py your_database.kdbx "your_master_password" ./migration_output

Step 5: Verify and Clean Up

After migration, verify your entries in the Bitwarden web vault or desktop app:

  1. Check that passwords match between old and new entries
  2. Verify custom fields transferred correctly
  3. Ensure folders/tags mapped properly
  4. Test login with a few critical accounts

Delete the temporary export files securely:

# macOS
srm keepass_export.json bitwarden_import.csv

# Linux
shred -u keepass_export.json bitwarden_import.csv

Handling Edge Cases

Two-Factor Authentication: KeePass doesn’t store TOTP seeds in the standard entry fields. If you used a separate TOTP authenticator, you’ll need to re-add 2FA to your Bitwarden entries manually or export from your TOTP app if supported.

Custom Attributes: KeePass allows arbitrary key-value pairs on entries. Bitwarden’s custom fields support this, but the Python script handles basic custom properties. Review entries with complex custom fields after import.

Database Merging: If you have multiple KeePass databases, run the migration script for each and import the resulting CSVs sequentially. Bitwarden handles duplicates based on title and username.

Security Considerations

During migration, your passwords briefly exist in unencrypted files. Work in an isolated environment:

Bitwarden’s zero-knowledge architecture means even their servers cannot read your vault. Your master password is the only key—ensure it’s strong and unique.

Built by theluckystrike — More at zovo.one