Filling a fresh Infisical — bulk-importing from Vaultwarden, and using tags to keep it sane
-
Following up on my earlier notes about the Infisical package. Getting it installed turned out to be the easy part; getting our scattered credentials into it was the real work. Here is what worked, including the bits I'd do differently.
The shape of the problem
Credentials were spread across: plain text files in a sync folder,
~/.config/*token*
files, shell profile exports, app containers themselves, the Cloudron box database, and many hundreds of items in a self-hosted Vaultwarden. Nobody remembers all of that. The only way I
found to make it tractable was to stop treating it as "move my secrets" and start treating it as "discover, verify, then move".a) Using an AI assistant to do the filling
**1. Be careful about including financial details.
2. Verify liveness before trusting anything. Cheap probes, per credential type:
curl -sS -o /dev/null -w '%{http_code}' -H "Authorization: token $T" https://<forge>/api/v1/user curl -sSI -H "Authorization: token $T" https://api.github.com/user | grep -i expiration # Porkbun: POST /api/json/v3/ping with {"apikey":…,"secretapikey":…} -> {"status":"SUCCESS"}The GitHub expiry header is the single most useful one — it turns "a token" into "a
token that dies on a known date".3. Read the licence gating from the source, not the docs. The community tier's
feature set is a literal table in the shipped backend
(/app/code/src/ee/services/license/license-fns.ts,getDefaultOnPremFeatures), which
you can read via the app's terminal. That settled several questions faster than
searching, and corrected one belief I had — secret versioning is available on the
free tier, whatever older posts say.b) Migrating from Vaultwarden
The Bitwarden CLI (
bw) is the tool, and it self-hosts against Vaultwarden fine:bw config server https://<your-vaultwarden> bw login <email> # interactive; use `bw login` not `bw unlock` if the token expired export BW_SESSION=$(bw unlock --raw) bw syncTwo things that cost me time and may save you some:
Credentials hide in three different places per item —
.login.password,
.notes, and custom fields. In our vault the newest keys had been appended as
custom fields at the bottom of existing entries, so anything that only read
.login.passwordwould have silently missed most of them. This finds all three:bw list items --session "$BW_SESSION" | jq -r ' .[] | select((.fields // []) | length > 0) | "\(.name) || \((.fields // []) | map(.name) | join(", "))"'Dedupe by value, not by name. The same key was frequently in several entries under
different names. I hashed every candidate and compared against what was already stored:fp() { sha256sum | cut -c1-8; } # compare fingerprints, never valuesThat turned ~35 candidates into 25 genuinely new secrets, and revealed two services
that were unknowingly sharing one API key — worth knowing on its own.Since
bw getneeds an exact name or ID, drive the import from item IDs collected
programmatically rather than names typed by hand.c) Tagging
Infisical's tags are free on the community tier and are the thing that stops a flat
list of secrets becoming unusable. There's no CLI command to create them, but the API
is simple:curl -X POST "$URL/api/v1/workspace/<projectId>/tags" \ -H "Authorization: Bearer $TOKEN" -H 'Content-Type: application/json' \ -d '{"slug":"vaultwarden-import","color":"#3b82f6"}'then apply with the CLI (note: tags replace existing ones on update, so pass the
full set each time):infisical secrets set --silent "NAME=$VALUE" --path /ai \ --tag ai-service --tag third-party --tag unverifiedThe scheme I settled on, after a false start tagging by service (too granular, one
tag per secret, useless for filtering):- What it is —
git-forge,ai-service,dns,cloudron-app,backup - Where it runs —
self-hostedvsthird-party. Surprisingly the most useful
axis: "what breaks if this box dies" vs "what needs an account somewhere". - Trust state —
verified-live,unverified,invalid-or-expired. Being honest
that most imported credentials are unverified is more useful than pretending
otherwise, and it gives you a work queue. - Time bombs — a tag naming the expiry month (
expires-YYYY-MM) for anything with
a known death date. Grep-able reminders beat calendar entries you'll ignore. - Provenance —
vaultwarden-import, so you can tell migrated from native.
Folders answer "where does this belong", tags answer "what do I need to look at".
Things worth knowing before you start
- Back up before you fill it. The instance's encryption key lives in
/app/data/
and everything in Postgres is encrypted with it. On a fresh install, before the first
backup runs, that key exists in exactly one place. Take a backup first. - Memory. The package sets a 1 GB limit and mine sat at ~90% of it near-idle. Raise
it before doing bulk work. - The bootstrap problem is real. Machine identities (Universal Auth) work well for
headless retrieval, but the client secret has to live somewhere on each host.
Infisical reduces N secrets to 1; it doesn't reduce them to 0, and any design that
claims otherwise is hiding the root credential rather than removing it. - Cloudron integration is push, not pull, for stock apps. They can only receive
values viacloudron env set, which restarts the app. Packages you build yourself
can pull natively at start-up, which is much nicer.
- What it is —
Hello! It looks like you're interested in this conversation, but you don't have an account yet.
Getting fed up of having to scroll through the same posts each visit? When you register for an account, you'll always come back to exactly where you were before, and choose to be notified of new replies (either via email, or push notification). You'll also be able to save bookmarks and upvote posts to show your appreciation to other community members.
With your input, this post could be even better 💗
Register Login