Back to planner

migration notes

How kp2bw moves a vault

The planner gives you a command. This page explains the Bitwarden model behind it, especially the folder and collection choices that matter for organization migrations.

what moves

kp2bw is not just a CSV-style import

It reads a KeePass 2.x database and creates Bitwarden items through bw serve. Entries, group paths, attachments, passkey-related fields, OTP-related fields, tags, expiry data, and login URIs are mapped into Bitwarden shapes instead of being dumped as plain notes.

Entries

Titles, usernames, passwords, notes, custom fields, and identities are migrated as Bitwarden item data.

Attachments

Files attached to KeePass entries are uploaded after their Bitwarden item exists.

Metadata

Tags and expiry are kept in a readable KP2BW_META field when Bitwarden has no native slot.

Identity stamps

KP2BW_ID lets reruns match the same KeePass entry instead of guessing by title.

planner options

Each option changes placement, filtering, or rerun behavior

Target vault

Organization means shared Bitwarden collections. Personal means your own vault and optional personal folders.

Organization ID

The value passed with -o. Use the Bitwarden organization ID for the destination org.

Collection ID

The value passed with -c when every imported item should land in one existing collection.

KeePass file

The final command argument. Point it at the .kdbx database to migrate.

Key file

Adds -K when the KeePass database also needs a key file.

Tag filter

Adds -t values. Empty means every non-excluded entry is included.

Filters

Filters decide which KeePass entries are part of the plan before collections or folders are calculated.

Skip expired

Adds --skip-expired. Expired KeePass entries are included unless this is enabled.

Recycle Bin

Adds --include-recycle-bin. Recycle Bin entries are excluded by default.

folders vs collections

Bitwarden folders are personal. Organization structure is collections.

This is the main migration trap. KeePass groups look like folders, but a Bitwarden organization does not have org folders. Shared structure in an organization is made with collections. Nested collections are names with slashes, such as Work/Servers.

KeePass group
Nested org collections
Top-level org collections
Personal folders
KeePass groupWork/Servers
Nested org collectionsWork/Servers
Top-level org collectionsWork
Personal foldersWork/Servers
KeePass groupInternet/Banking
Nested org collectionsInternet/Banking
Top-level org collectionsInternet
Personal foldersInternet/Banking

For a Vaultwarden organization migration, the usual shape is nested collections and no personal folders. That keeps shared data in the org model instead of creating a private folder tree beside it.

Want both? Pass --folder alongside -o (the planner's Also create personal folders toggle) and every item is filed into its org collection and a personal folder — the same double-filing Bitwarden's own org import does. Leave it off unless you specifically want that private copy.

Full-path collections

-c nested: KeePass Work/Servers becomes collection Work/Servers. With -o set, personal folders are already off, so no extra flag is needed.

Top-folder collections

-c auto: KeePass Work/Servers and Work/Engineering both land in collection Work.

Single collection

-c 11111111-1111-1111-1111-111111111111: every imported item lands in one existing collection.

Flat organization

-o with no collection mapping: items are created in the organization without a generated hierarchy or personal folders.

Personal folders

KeePass groups become personal Bitwarden folders. Use this only when importing into a personal vault.

Flat personal

--no-folder: items stay at the personal vault root.

running again

Choose the situation, not the flag name

Every migrated item carries KP2BW_ID, the KeePass UUID used to match the same item next time. kp2bw also writes a KP2BW_SYNC content stamp. Those markers let kp2bw decide whether an existing Bitwarden item is still safe to update.

  • KeePass is still my main vault: use --force-update.
  • Mostly Bitwarden, sync forgotten KeePass edits: default mode.
  • Testing migrations against an existing Vaultwarden DB: use --no-update.

KeePass is still my main vault

--force-update: KeePass content wins over later Bitwarden edits. Use this when KeePass is still the source of truth.

Mostly Bitwarden, sync forgotten KeePass edits

Default mode. kp2bw updates migrated items when Bitwarden has not been edited since the last kp2bw run.

Testing migrations; keep the existing Vaultwarden DB

--no-update: create missing items only. Existing migrated items stay untouched, so you do not need to wipe the DB just to try another pass.

url handling

Additional URLs become real login URIs

KeePass fields like KP2A_URL, URL_1, and AndroidApp are folded into Bitwarden login URIs where they can drive autofill. Free-text fields like API Url stay as custom fields so API endpoints do not accidentally become login matches.

Plain URLs use your Bitwarden account default. KeePassXC-style quoted URLs become exact matches, and wildcards become starts-with or regex matches. If too many subdomains surface together, use the URI collision report to inspect the problem before changing match behavior.

after migration

Only strip kp2bw stamps when you are truly done

kp2bw --strip-ids removes KP2BW_ID and KP2BW_SYNC from migrated items. That is final adoption: Bitwarden becomes the source of truth and future KeePass reruns become unreliable because kp2bw can no longer match by KeePass UUID.

This is irreversible. Do not run it while you still expect to rerun a KeePass migration.

credentials

Use a .env file for passwords

The command can prompt for passwords. For repeated runs, put secrets in .env instead of in command arguments.

KP2BW_KEEPASS_PASSWORD=<keepass password>
KP2BW_BITWARDEN_PASSWORD=<bitwarden password>

kp2bw loads .env automatically. Real environment variables still win when both are set.