Niektórzy z administratorów sceptycznie patrzą na konteneryzację, pomimo faktu, że jest z nami na prawdę bardzo długo. Czasem wynika to z braku wiedzy, czasem z braku możliwości i chęci utrzymywania całego technologicznego zaplecza dla jednego czy też dwóch kontenerów, a czasem - minimalizacji narzutu kolejnych warstw i optymalizacji prędkości działania. W poniższym opisie przedstawię jak zainstalować serwer Vaultwarden bezpośrednio na systemie Linux z pominięciem kompilacji oprogramowania. Plik binarny jak również pliki aplikacji webowej zostaną pobrane i wypakowane z dostępnego obrazu Dockera, udostępnianego publicznie.
Założenia:
apt-get update && apt-get -y install curl git wget ssl-cert
adduser --system --shell /usr/sbin/nologin --comment "Vaultwarden User" --create-home --home /usr/lib/vaultwarden/ vaultwarden adduser vaultwarden ssl-cert
mkdir -p /etc/vaultwarden/ && chown vaultwarden:vaultwarden /etc/vaultwarden && chmod 0750 /etc/vaultwarden
mkdir -p /usr/lib/vaultwarden/data/ && chown vaultwarden:vaultwarden /usr/lib/vaultwarden/data && chmod 0750 /usr/lib/vaultwarden/data mkdir -p /usr/lib/vaultwarden/data/tmp && chown vaultwarden:vaultwarden /usr/lib/vaultwarden/data/tmp && chmod 0750 /usr/lib/vaultwarden/data/tmp
mkdir -p /var/log/vaultwarden && chown vaultwarden:vaultwarden /var/log/vaultwarden && chmod 0751 /var/log/vaultwarden
cat </etc/logrotate/vaultwarden /var/log/vaultwarden/vaultwarden.log { su vaultwarden vaultwarden rotate 60 daily missingok copytruncate notifempty compress sharedscripts postrotate invoke-rc.d rsyslog rotate >/dev/null 2>&1 || true endscript } EOF
W celu wypakowania plików niezbędnych do uruchomienia usługi można ręcznie pobrać każdą z warstw dostępnych w manifeście dla obrazu vaultwarden/server lub też skorzystać z przygotowanego skryptu powłoki (instalujac uprzednio jq apt-get install jq -y):
#!/usr/bin/env bash
DEST_WEB="/usr/lib/vaultwarden"
DEST_BIN="/usr/bin"
VAULTWARDEN_USER="vaultwarden"
VAULTWARDEN_GROUP="vaultwarden"
IMAGE="vaultwarden/server"
TAG="latest"
case "$(uname -m)" in
x86_64) ARCH="amd64" ;;
aarch64) ARCH="arm64" ;;
*) echo "Unsupported architecture, exiting..."; exit 1 ;;
esac
TARGET_DIR="web-vault"
TARGET_BIN="vaultwarden"
mkdir -p "$DEST_WEB"
echo "[*] Getting registry token..."
TOKEN=$(curl -s \
"https://auth.docker.io/token?service=registry.docker.io&scope=repository:${IMAGE}:pull" \
| jq -r '.token')
echo "[*] Fetching manifest list..."
MANIFEST_LIST=$(curl -s \
-H "Authorization: Bearer ${TOKEN}" \
-H "Accept: application/vnd.docker.distribution.manifest.list.v2+json" \
"https://registry-1.docker.io/v2/${IMAGE}/manifests/${TAG}")
echo "[*] Selecting manifest for $ARCH..."
MANIFEST_DIGEST=$(echo "$MANIFEST_LIST" \
| jq -r ".manifests[] | select(.platform.architecture == \"$ARCH\") | .digest")
if [[ -z "$MANIFEST_DIGEST" ]]; then
echo "Error: no manifest for architecture '$ARCH'"
exit 1
fi
echo "[*] Using manifest: $MANIFEST_DIGEST"
echo "[*] Fetching image manifest..."
MANIFEST=$(curl -s \
-H "Authorization: Bearer ${TOKEN}" \
-H "Accept: application/vnd.docker.distribution.manifest.v2+json" \
"https://registry-1.docker.io/v2/${IMAGE}/manifests/${MANIFEST_DIGEST}")
echo "[*] Downloading layers..."
rm -f layer_*.tar
i=0
for DIGEST in $(echo "$MANIFEST" | jq -r '.layers[].digest'); do
i=$((i+1))
FILE="layer_${i}.tar"
echo " - downloading $FILE ($DIGEST)"
curl -s -L \
-H "Authorization: Bearer ${TOKEN}" \
"https://registry-1.docker.io/v2/${IMAGE}/blobs/${DIGEST}" \
-o "$FILE"
done
echo "[*] Searching for '$TARGET_DIR' and '$TARGET_BIN' in layers..."
FOUND_DIR=0
FOUND_BIN=
for LAYER in layer_*.tar; do
if [[ "$FOUND_DIR" -eq 0 ]]; then
if tar -tf "$LAYER" | grep "${TARGET_DIR}/index.html"; then
echo "[*] Found '$TARGET_DIR' in $LAYER — extracting to $DEST_WEB"
tar -xf "$LAYER" -C "$DEST_WEB" "$TARGET_DIR"
FOUND_DIR=1
fi
fi
if [[ "$FOUND_BIN" -eq 0 ]]; then
if tar -tf "$LAYER" | grep "${TARGET_BIN}"; then
echo "[*] Found '$TARGET_BIN' in $LAYER — extracting to $DEST_BIN"
tar -xf "$LAYER" -C "$DEST_BIN" "$TARGET_BIN"
FOUND_BIN=1
fi
fi
if [[ "$FOUND_DIR" -eq 1 && "$FOUND_BIN" -eq 1 ]]; then
break
fi
done
rm -f layer_*.tar
if [[ "$FOUND_DIR" -eq 0 || "$FOUND_BIN" -eq 0 ]]; then
echo "Error: Could not find one or both targets ('$TARGET_DIR' and '$TARGET_BIN') in the image layers."
exit 1
fi
echo "[*] Change permissions..."
chown $VAULTWARDEN_USER:$VAULTWARDEN_GROUP $DEST_WEB/$TARGET_DIR -R
chown $VAULTWARDEN_USER:$VAULTWARDEN_GROUP $DEST_BIN/$TARGET_BIN
echo "[+] Done!"
echo "→ Extracted files are in: $DEST_WEB and $DEST_BIN"
echo " - Web Vault: $DEST_WEB/$TARGET_DIR"
echo " - Binary: $DEST_BIN/$TARGET_BIN"
Po zapisaniu skryptu pod nazwą "/root/dedocker-vaultwarden.sh" oraz po włączeniu bitu wykonywania i uruchomieniu go, na konsoli powinny pojawić się stosowne komunikaty:
# ./dedocker-vaultwarden.sh
[*] Getting registry token...
[*] Fetching manifest list...
[*] Selecting manifest for arm64...
[*] Using manifest: sha256:a24a108a90f6380a8f990308831025b038ee8df5d5be7f4c04922441ac7d6ff0
[*] Fetching image manifest...
[*] Downloading layers...
- downloading layer_1.tar (sha256:2ae15a20160209c6fd6cff4886e4ba2e666fa5bedd7b54a2c0097ea6646f0273)
- downloading layer_2.tar (sha256:45431c76674b0df695a222114e087abf9d53d313517bc2b072f06913e8c89dcd)
- downloading layer_3.tar (sha256:7c614f5e5b12d769ab1c7209670d560dfa11dad61b1b47e205d239ba64a8b0e6)
- downloading layer_4.tar (sha256:6043b4e5998fa5bb92ed2956636c3718d33dea6c277cdd7510d2f6814db914a7)
- downloading layer_5.tar (sha256:389eb04f03834ad4fa7d5b2fa18ae377990943fffd16b96397bfae35dfa6a6ba)
[*] Searching for 'web-vault' and 'vaultwarden' in layers...
web-vault/index.html
[*] Found 'web-vault' in layer_4.tar — extracting to /usr/lib/vaultwarden
vaultwarden
[*] Found 'vaultwarden' in layer_5.tar — extracting to /usr/bin
[*] Change permissions...
[+] Done!
→ Extracted files are in: /usr/lib/vaultwarden nad /usr/bin
- Web Vault: /usr/lib/vaultwarden/web-vault
- Binary: /usr/bin/vaultwarden
W katalogu /usr/bin/ powinien pojawić się plik vaultwarden, a w katalogu /usr/lib/vaultwarden/ katalog web-vault/. Sprawdźmy, czy program działa poprawnie:
cd /usr/lib/vaultwarden /usr/bin/vaultwarden --version Vaultwarden 1.35.2 Web-Vault 2025.12.1+build.3
Następnie możemy przystąpić do generowania certyfikatu SSL oraz konfiguracji usługi.
apt-get install -y openssl openssl genrsa -out /etc/ssl/private/self-vaultwarden.home.arpa.key 4096 chgrp ssl-cert /etc/ssl/private/self-vaultwarden.home.arpa.key chmod 0640 /etc/ssl/private/self-vaultwarden.home.arpa.key openssl req -x509 -new \ -key /etc/ssl/private/self-vaultwarden.home.arpa.key \ -sha256 \ -days 825 \ -out /etc/ssl/certs/self-vaultwarden.home.arpa.crt \ -subj "/CN=vaultwarden.home.arpa" \ -addext "subjectAltName=DNS:vaultwarden.home.arpa" chgrp ssl-cert /etc/ssl/certs/self-vaultwarden.home.arpa.crt chmod 0644 /etc/ssl/certs/self-vaultwarden.home.arpa.crt
Vaultwarden wymaga klucza rsa do podpisywania tokenów, zaproszeń dla użytkowników czy też danych w bazie danych. Należy zatem wygenerować go:
openssl genrsa -out /etc/vaultwarden/rsa_key.pem 4096 chown vaultwarden:vaultwarden /etc/vaultwarden/rsa_key.pem chmod 0600 /etc/vaultwarden/rsa_key.pem
Skonfigurujmy teraz Vaultwarden, według założeń:
cat <> /etc/vaultwarden/vaultwarden.conf DATA_FOLDER=/usr/lib/vaultwarden/data RSA_KEY_FILENAME=/etc/vaultwarden/rsa_key WEB_VAULT_FOLDER=/usr/lib/vaultwarden/web-vault/ WEB_VAULT_ENABLED=true DATABASE_URL=/usr/lib/vaultwarden/data/db.sqlite3 WEBSOCKET_ENABLED=false WEBSOCKET_ADDRESS=0.0.0.0 WEBSOCKET_PORT=3012 PUSH_ENABLED=False PUSH_INSTALLATION_ID=CHANGEME PUSH_INSTALLATION_KEY=CHANGEME DOMAIN=https://vaultwarden.home.arpa:8443 SENDS_ALLOWED=False SIGNUPS_ALLOWED=True SIGNUPS_VERIFY=False SIGNUPS_DOMAINS_WHITELIST=localhost,home.arpa [email protected] INVITATIONS_ALLOWED=False INVITATION_ORG_NAME=Snakeoil EMAIL_CHANGE_ALLOWED=False LOF_FILE=/var/log/vaultwarden/vaultwarden.log LOG_LEVEL=error ROCKET_ADDRESS=0.0.0.0 ROCKET_PORT=8443 ROCKET_TLS={certs="/etc/ssl/certs/self-vaultwarden.home.arpa.crt",key="/etc/ssl/private/self-vaultwarden.home.arpa.key"} EOF chown vaultwarden:vaultwarden /etc/vaultwarden/vaultwarden.conf chmod 0640 /etc/vaultwarden/vaultwarden.conf
Pozostała tylko instalacja usługi oraz jej uruchomienie. W tym celu należy utworzyć jednostkę systemd w /etc/systemd/system/vaultwarden.service:
cat <> /etc/systemd/system/vaultwarden.service [Unit] Description=Vaultwarden server After=network.target auditd.service [Service] RestartSec=5s Type=simple User=vaultwarden Group=vaultwarden EnvironmentFile=/etc/vaultwarden/vaultwarden.conf WorkingDirectory=/usr/lib/vaultwarden ExecStart=/usr/bin/vaultwarden Restart=always PrivateTmp=true PrivateDevices=true ProtectHome=true NoNewPrivileges=true ProtectSystem=strict ReadOnlyPaths=/usr/lib/vaultwarden/web-vault/ /etc/vaultwarden ReadWritePaths=/usr/lib/vaultwarden/data/ /var/log/vaultwarden/ LimitNOFILE=1048576 LimitNPROC=64 [Install] WantedBy=multi-user.target EOF systemctl daemon-reload systemctl enable --now vaultwarden.service
Sprawdzenie poprawności działania usługi
# ss -plnat |grep 8443
LISTEN 0 4096 0.0.0.0:8443 0.0.0.0:* users:(("vaultwarden",pid=3147,fd=24))
# systemctl status vaultvarden.service
● vaultwarden.service - Vaultwarden server
Loaded: loaded (/etc/systemd/system/vaultwarden.service; enabled; preset: enabled)
Active: active (running) since Sat 2026-01-24 10:50:33 CET; 1min 21s ago
Invocation: ba3204b2476940c9b3d82cbbf64d2e35
Main PID: 3147 (vaultwarden)
Tasks: 13 (limit: 8749)
CPU: 195ms
CGroup: /system.slice/vaultwarden.service
└─3147 /usr/bin/vaultwarden
Jan 24 10:50:33 r4pie vaultwarden[3147]: | This is an *unofficial* Bitwarden implementation, DO NOT use the |
Jan 24 10:50:33 r4pie vaultwarden[3147]: | official channels to report bugs/features, regardless of client. |
Jan 24 10:50:33 r4pie vaultwarden[3147]: | Send usage/configuration questions or feature requests to: |
Jan 24 10:50:33 r4pie vaultwarden[3147]: | https://github.com/dani-garcia/vaultwarden/discussions or |
Jan 24 10:50:33 r4pie vaultwarden[3147]: | https://vaultwarden.discourse.group/ |
Jan 24 10:50:33 r4pie vaultwarden[3147]: | Report suspected bugs/issues in the software itself at: |
Jan 24 10:50:33 r4pie vaultwarden[3147]: | https://github.com/dani-garcia/vaultwarden/issues/new |
Jan 24 10:50:33 r4pie vaultwarden[3147]: \--------------------------------------------------------------------/
Można już z powodzeniem utworzyć konta użytkowników i korzystać do woli. Podobny efekt, z tym, że automatycznie, można uzyskać wykonując polecenia:
sudo apt-get install -y ansible git cat </tmp/localhost_vars.yml linux_vaultwarden_domain: vaultwarden.home.arpa linux_vaultwarden_signups_domains_whitelist: - localhost - home.arpa linux_vaultwarden_org_creation_users: - [email protected] EOF ansible-pull \ -U https://github.com/michalsternadel/linux_vaultwarden.git \ -e @/tmp/localhost_vars.yml \ local.yml
Zalecam skonfigurować Vaultwarden tak, aby słuchał jedynie na hoście lokalnym oraz wykorzystać reverse-proxy do udostępniania usługi z odpowiadnią terminacją SSL. Przy dużych wdrożeniach również rozsądniej byłoby zamiast SQLite użyć silnika MariaDB.