Why
A password manager is the one service whose data you most want on a machine you own. Vaultwarden is a small server that the official Bitwarden apps, browser extensions and web vault all talk to.
What
One app, vaultwarden, on the place vaultwarden, on none. That is deliberate: the Bitwarden phone apps, desktop apps and browser extensions cannot get past an Edgible sign-in, so org would lock them out. Vaultwarden's own protections take its place: every vault is encrypted with its owner's master password, sign-ups are off (SIGNUPS_ALLOWED: "false"), so nobody can create an account unless you invite them, and the /admin page needs VAULTWARDEN_ADMIN_TOKEN.
Vaultwarden must know its own address, so DOMAIN is https://vaultwarden.<ORG_LABEL>.edgible.com, or VAULTWARDEN_URL on your own domain. The image is vaultwarden/server:1.37.4, with its own healthcheck.
The admin token here is a long random string. Vaultwarden also accepts an Argon2 hash of it (docker run --rm -it vaultwarden/server:1.37.4 /vaultwarden hash), which it recommends; in card.env, write each $ in the hash as $$.
Vaults, attachments and settings live in the volume vaultwarden-data. Back it up; it holds everyone's encrypted vault. Minimum recommended for the place vaultwarden: 256 MB of memory and 1.5 GB of disk, on an arm64, amd64 or 32-bit arm machine, with no GPU.
The Compose file is docker-compose.yml, the settings card.env, the card card.yml, and the last test test.yml.
How
Five steps. Edit card.env before you start.
1. Fetch
mkdir -p vaultwarden
curl -fsSL https://github.com/Edgible/cards/archive/refs/heads/main.tar.gz \
| tar -xz --strip-components=2 -C vaultwarden cards-main/vaultwarden
2. Edit card.env
Set DEVICE to a name edgible device list prints, and ORG_LABEL to the part after the app name in a hostname edgible app list prints. The admin token is generated in the next step.
nano vaultwarden/card.env
3. Check
curl -fsSLo check-env.py https://raw.githubusercontent.com/Edgible/card-kit/main/check-env.py
python3 check-env.py vaultwarden
It fills each empty secret and moves a taken port, as lines to paste. Run it again until it says no conflicts.
4. Start
docker compose --env-file vaultwarden/card.env -f vaultwarden/docker-compose.yml up -d --wait
docker compose --env-file vaultwarden/card.env -f vaultwarden/docker-compose.yml ps
--wait returns when each service is running, and healthy when it has a healthcheck. The image's own healthcheck applies.
5. Publish
set -euo pipefail
set -a
. vaultwarden/card.env
set +a
device_id=$(edgible device list --json | jq -er --arg name "$DEVICE" '
map(select(.name == $name))
| if length == 1 then .[0].id
else error("need exactly one device named " + $name + " (" + (map(.status + " " + .id) | join(", ")) + ")")
end
')
edgible app create existing \
--non-interactive \
--name vaultwarden \
--port "$VAULTWARDEN_PORT" \
--protocol https \
--auth-modes none \
--device-id "$device_id"
edgible app list
Verify
vaultwarden uses none.
curl -sS -o /dev/null -w '%{http_code}\n' "https://<vaultwarden hostname>"
That prints 200: the web vault, with no Edgible sign-in, as the apps need. Open https://<vaultwarden hostname>/admin, enter VAULTWARDEN_ADMIN_TOKEN, and invite yourself under Users. With no mail server set up, the invited address can then create its account on the web vault. Then point a Bitwarden app at https://<vaultwarden hostname> as a self-hosted server.
The rest of the setup is in the Vaultwarden docs.
Tear down
Four steps, in this order. Step 1 runs wherever edgible is logged in. Steps 2 to 4 run on the machine that runs the containers. Steps 2 and 3 read card.env, so keep it until step 4.
1. Unpublish
edgible app delete vaultwarden --yes
2. Stop
docker compose --env-file vaultwarden/card.env -f vaultwarden/docker-compose.yml down
3. Delete the data
Skip this step to keep the data. The loop copies each volume to a .tgz file in this directory first.
for volume in $(docker volume ls -q --filter label=com.docker.compose.project=vaultwarden); do
docker run --rm -v "$volume:/data:ro" -v "$PWD:/backup" alpine tar -czf "/backup/$volume.tgz" -C /data .
done
docker compose --env-file vaultwarden/card.env -f vaultwarden/docker-compose.yml down --volumes
4. Remove the card
rm -rf vaultwarden