A command-line tool to migrate your data from 1Password to Bitwarden, preserving folder structure, custom fields, TOTP secrets, and more.
- Exports all items from 1Password vaults via CLI
- Transforms data to Bitwarden-compatible format
- Supports flexible vault-to-destination mapping:
- Personal vault (with or without folders)
- Organization vaults with collections
- Preserves:
- Logins (username, password, URLs, TOTP)
- Credit cards
- Identities
- Secure notes
- SSH keys
- Software licenses (as secure notes with custom fields)
- Custom fields on all item types
- 1Password CLI (
op) - Install guide - Bitwarden CLI (
bw) - Install guide - Python 3.6+
- jq - JSON processor (
brew install jqon macOS)
# macOS with Homebrew
brew install 1password-cli bitwarden-cli jq
# Verify installation
op --version
bw --version# 1Password - sign in and export session
eval $(op signin)
# Bitwarden - login and unlock
bw login
export BW_SESSION=$(bw unlock --raw)Edit config.json to map your 1Password vaults to Bitwarden destinations:
{
"vault_mapping": {
"Personal": {
"destination": "personal",
"folder": null
},
"Work": {
"destination": "personal",
"folder": "Work"
},
"Family": {
"destination": "organization",
"organization": "Family Org",
"collection": "Shared"
}
}
}# Full migration (export + transform + import)
./src/migrate.sh
# Or run steps individually:
./src/export.sh # Export from 1Password
python3 src/transform.py # Transform to Bitwarden format
./src/import.sh # Import to Bitwarden| Option | Description |
|---|---|
destination |
"personal" or "organization" |
folder |
Folder name for personal vault (null for no folder) |
organization |
Organization name (required if destination is "organization") |
collection |
Collection name within organization |
All to personal vault, no folders:
{
"vault_mapping": {
"Private": {"destination": "personal", "folder": null},
"Work": {"destination": "personal", "folder": null}
}
}Vaults to folders:
{
"vault_mapping": {
"Private": {"destination": "personal", "folder": null},
"Work": {"destination": "personal", "folder": "Work"},
"Finance": {"destination": "personal", "folder": "Finance"}
}
}Mixed personal and organization:
{
"vault_mapping": {
"Private": {"destination": "personal", "folder": null},
"Family": {
"destination": "organization",
"organization": "Family",
"collection": "Shared Logins"
}
}
}| 1Password Type | Bitwarden Type |
|---|---|
| Login | Login |
| Password | Login |
| Credit Card | Card |
| Identity | Identity |
| Secure Note | Secure Note |
| SSH Key | SSH Key |
| Software License | Secure Note + Custom Fields |
| API Credential | Login + Custom Fields |
| Database | Login + Custom Fields |
| Server | Login + Custom Fields |
| Document | Secure Note (attachment separate) |
- Passkeys - Cannot be exported due to WebAuthn security design. You must manually re-register passkeys on each site.
- Document attachments - May fail to download via CLI for some document types. Check logs and transfer manually if needed.
- Watchtower data - 1Password's security reports don't transfer (Bitwarden has its own).
- Password history - Not fully preserved in migration.
- Duplicate folders may be created if a folder with the same name already exists
- Large vaults (1000+ items) may take 20-30 minutes to export
1pe/
├── README.md # This file
├── config.json # Vault mapping configuration
├── src/
│ ├── export.sh # 1Password export (READ-ONLY)
│ ├── transform.py # Data transformation
│ ├── import.sh # Bitwarden import
│ └── migrate.sh # Main orchestrator
├── data/
│ ├── export/ # Exported 1Password data
│ ├── attachments/ # Downloaded attachments
│ └── transformed/ # Bitwarden-ready JSON
├── logs/
│ └── migration.log # Operation logs
└── reports/
├── pre-migration.md
└── post-migration.md
-
Exported data contains secrets - The
data/export/directory contains all your passwords in plain text. Delete it after migration. -
1Password is READ-ONLY - This tool never modifies or deletes anything in 1Password.
-
Test first - Run
./src/export.shalone first to see what will be migrated before importing. -
Bitwarden is reversible - If something goes wrong, you can delete imported items from Bitwarden and try again.
eval $(op signin)export BW_SESSION=$(bw unlock --raw)This is normal. The 1Password CLI fetches each item individually. Expect ~1 second per item.
- Check
logs/migration.logfor errors - Verify item counts:
bw list items | jq length - Run
bw syncto refresh
Issues and pull requests welcome!
MIT License - See LICENSE file