SSD Nodes Learn
Посібники Matt ConnorВід Matt Connor · Оновлено 2026-07-24

Встановлення Vaultwarden на VPS через Docker

Налаштуйте Vaultwarden на Ubuntu 24.04 за допомогою Docker Compose. Покрокова інструкція: HTTPS, адмін-панель, Fail2ban та надійне резервне копіювання даних.

Що ви створюєте

Менеджер паролів під повним вашим контролем: Vaultwarden, запущений у одному контейнері за reverse proxy, який термінує HTTPS. Офіційні додатки Bitwarden на вашому телефоні, ноутбуці та у браузері будуть підключені до нього. Vaultwarden реалізує API сервера Bitwarden на мові Rust і використовує той самий протокол, що й bitwarden.com. Це дозволяє використовувати будь-який офіційний клієнт без змін. При цьому Vaultwarden споживає близько 100 MB RAM, на відміну від офіційного стека з багатьох контейнерів.

Процес встановлення складається з кількох рядків у Compose. Ключовими факторами, через які можуть виникнути проблеми, є наступні: наявність TLS перед першим використанням web vault; вимкнення публічної реєстрації одразу після створення власного облікового запису; регулярне резервне копіювання та перевірка відновлення даних з volume, оскільки саме в цій директорії зберігаються всі ваші паролі.

Prerequisites and the honest gotchas

  • VPS з Docker Engine та плагіном Compose на чистій системі Ubuntu 24.04 KVM з правами root або sudo. 512 MB RAM достатньо; 1 GB забезпечує комфортну роботу. Це один із найлегших сервісів — він займає топові позиції у списку сервісів для self-hosting.
  • Домен з A-записом (та AAAA, якщо використовується IPv6), що вказує на vault.example.com вашого VPS. TLS-сертифікат видається саме на це ім'я, тому DNS має працювати до початку налаштування.
  • Порти 80 та 443 мають бути відкриті для зовнішнього світу та спрямовані на ваш reverse proxy — ніколи не на Vaultwarden напряму. Порт 80 використовується лише для ACME certificate challenge та перенаправлення HTTP-to-HTTPS.
  • Головна особливість: клієнти Bitwarden не працюють із сервером без HTTPS. Варіант «спочатку протестувати через http» неможливий — цей шлях не працює з конкретної причини, наведеної далі.

Чому Vaultwarden, а не офіційний стек Bitwarden

Ті самі клієнти, але значно менше навантаження. Офіційний self-hosted Bitwarden постачається як набір контейнерів (MSSQL, Nginx, Identity, Api, Admin тощо) і потребує приблизно 2 GB RAM. Vaultwarden — це один binary, який за замовчуванням зберігає всі дані у SQLite database і споживає лише кілька десятків мегабайт у стані спокою. Для однієї особи, сім'ї або невеликої команди це очевидний вибір. Оскільки Vaultwarden точно реалізує Bitwarden API, ваші дані залишаються портативними між ним та bitwarden.com.

Ви втрачаєте більшість функцій корпоративного рівня: немає SCIM provisioning (хоча експериментальний OpenID Connect SSO з'явився у версії 1.35.0). Також ви є адміністратором, тому встановлення патчів, налаштування HTTPS та створення резервних копій — це ваші завдання. Цей посібник присвячений саме цим трьом завданням.

Чому HTTPS є обов'язковим

Веб-сховище Bitwarden та розширення для браузерів генерують ключі шифрування у браузері за допомогою Web Crypto API (window.crypto.subtle). Браузери надають доступ до crypto.subtle лише у secure context — через HTTPS або у винятковому випадку http://localhost. Через звичайний http://vault.example.com доступ undefined, тому під час спроби додатка згенерувати ключ виникає помилка, яку відображає консоль:

Uncaught (in promise) TypeError: Cannot read properties of undefined (reading 'importKey')

Сторінка зависає або видає загальну помилку криптографії, і вхід у систему неможливий. Десктопні, мобільні та браузерні клієнти виконують власну перевірку URL-адреси хостингу. При спробі з'єднання з http (або недоступним) ендпоїнтом вони видають помилку:

This is not a recognized Bitwarden server. You may need to check with your provider or update your server.

Причина в обох випадках однакова: відсутність валідного HTTPS. Тому спочатку необхідно налаштувати TLS і ніколи не відкривати сховище через http, навіть для швидкого перегляду.

Step 1 — DNS та реверс-проксі (спочатку TLS)

Направте запис на ваш VPS і переконайтеся, що він резолвиться у правильну адресу:

dig +short vault.example.com

Виведений рядок має містити IP-адресу вашого VPS. Якщо він порожній або некоректний, виправте DNS і зачекайте завершення TTL — видача сертифіката неможлива для імені, яке не резолвиться.

Для HTTPS front end у цьому посібнику використовується Traefik, який автоматично видає та оновлює сертифікати Let's Encrypt і інтегрується безпосередньо в Compose. Якщо ви ще не використовуєте його, спочатку виконайте налаштування реверс-проксі Traefik та автоматичного TLS; це створює зовнішню мережу Docker (proxy нижче) та ACME resolver (letsencrypt), до яких підключається сервіс Vaultwarden. З боку Vaultwarden звичайний nginx із вручну виданим сертифікатом працює так само.

Віддаєте перевагу nginx та Certbot замість Traefik? Розмістіть Vaultwarden на 127.0.0.1:8080 (додайте ports: ["127.0.0.1:8080:80"] до сервісу та видаліть labels Traefik), потім видайте сертифікат і налаштуйте проксі до нього. Частину з сертифікатом описано в видача сертифікатів Let's Encrypt за допомогою Certbot та nginx. Важливо додатково налаштувати WebSocket upgrade на шляху сповіщень:

server {
    listen 443 ssl;
    server_name vault.example.com;

    client_max_body_size 525M;

    location / {
        proxy_pass http://127.0.0.1:8080;
        proxy_set_header Host $host;
        proxy_set_header X-Real-IP $remote_addr;
        proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
        proxy_set_header X-Forwarded-Proto $scheme;
        proxy_http_version 1.1;
        proxy_set_header Upgrade $http_upgrade;
        proxy_set_header Connection "upgrade";
    }
}

Зверніть увагу на рядок X-Real-IP — саме він дозволяє Fail2ban пізніше бачити реального зловмисника замість 127.0.0.1. Усі інші кроки в цьому посібнику однакові, незалежно від того, чи використовується Traefik, чи nginx.

Step 2 — файл Compose

Спочатку створіть директорію проєкту. У цьому посібнику використовується /opt/vaultwarden, що робить назву проєкту Compose — і, відповідно, том даних vaultwarden_vw-data — передбачуваною; наступні кроки з налаштування Fail2ban та резервного копіювання залежать саме від цієї назви.

sudo mkdir -p /opt/vaultwarden
cd /opt/vaultwarden

Створіть .env для секретного ключа адміністратора та файл Compose у цій директорії.

# .env
ADMIN_TOKEN=paste-a-strong-token-here

Згенеруйте цей токен за допомогою openssl rand -base64 48 і вставте його. (Наступним кроком розглядається використання складнішої хеш-форми; для початку підійде довгий випадковий рядок.)

# docker-compose.yml
services:
  vaultwarden:
    image: vaultwarden/server:latest
    container_name: vaultwarden
    restart: unless-stopped
    environment:
      DOMAIN: "https://vault.example.com"
      SIGNUPS_ALLOWED: "true"          # closed in Step 4, keep true just to register
      ADMIN_TOKEN: "${ADMIN_TOKEN}"
      IP_HEADER: "X-Forwarded-For"     # X-Real-IP if your proxy sends that instead
      LOG_FILE: "/data/vaultwarden.log"
      LOG_LEVEL: "warn"
    volumes:
      - vw-data:/data
    networks:
      - proxy
    labels:
      - "traefik.enable=true"
      - "traefik.http.routers.vw.rule=Host(`vault.example.com`)"
      - "traefik.http.routers.vw.entrypoints=websecure"
      - "traefik.http.routers.vw.tls.certresolver=letsencrypt"
      - "traefik.http.services.vw.loadbalancer.server.port=80"

volumes:
  vw-data:

networks:
  proxy:
    external: true

Дві особливості цього файлу визначають архітектуру системи. Тут немає мапінгу ports:, тому Vaultwarden доступний лише через Traefik та його TLS — публікація порту на хості призводить до випадкового використання HTTP. Також DOMAIN має бути повним публічним URL-адресою HTTPS: вона використовується у посиланнях для вкладень, WebAuthn 2FA та ендпоінті сповіщень, тому неправильне значення або використання http порушить їхню роботу, навіть якщо сайт завантажиться. Тег latest є свідомим винятком із загального правила "ніколи не latest" — Vaultwarden випускає стабільні релізи як єдиний rolling image, а :testing є окремим каналом для pre-release версій — тому оновлюйтесь свідомо та переглядайте примітки до релізу перед виконанням pull.

Запустіть контейнери та перевірте лог:

docker compose up -d
docker compose logs -f vaultwarden

Правильний запуск завершується рядком на кшталт Rocket has launched from http://0.0.0.0:80. Дайте Traefik кілька секунд на отримання сертифіката, потім завантажте https://vault.example.com — ви маєте отримати веб-сховище Bitwarden з дійсним значком замка та без попереджень про сертифікат.

Step 3 — надійний ADMIN_TOKEN та пастка $$

ADMIN_TOKEN захищає /admin — панель, яка має доступ до всіх користувачів та налаштувань вашого екземпляра. Ставтеся до неї як до пароля root. Можливі два варіанти.

Простий варіант — це випадковий рядок, який ви вже згенерували за допомогою openssl rand -base64 48. Оскільки base64 не містить символ $, він передається в .env без необхідності екранування.

Посилений варіант — це хеш Argon2 PHC, при якому відкритий токен не зберігається на диску. Згенеруйте його для того самого образу:

docker run --rm -it vaultwarden/server /vaultwarden hash --preset owasp

Програма запитає пароль двічі та виведе рядок, що починається з $argon2id$v=19$.... Ось пастка, через яку користувачі втрачають годину часу: Docker Compose сприймає $ як інтерполяцію змінних. Тому при вставці хешу у файл Compose необхідно подвоїти кожен символ $ до $$. Розміщуйте його безпосередньо під environment:, а не через .env, і не беріть у лапки:

    environment:
      ADMIN_TOKEN: $$argon2id$$v=19$$m=19456,t=2,p=1$$c29tZXNhbHQ$$RdescudvJCsgt3ub+b+dWRWJTmaaJObG

Якщо залишити одинарні знаки $, Compose видасть попередження The "argon2id" variable is not set і зробить токен порожнім. Через це /admin відхилить ваш правильний пароль. Виконайте docker compose up -d і збережіть введений вами відкритий токен у власному сховищі паролів.

Step 4 — зареєструйте обліковий запис, а потім закрийте доступ

За допомогою SIGNUPS_ALLOWED: "true" відкрийте https://vault.example.com, натисніть Create account та пройдіть реєстрацію, використовуючи ваш email та надійний головний пароль. Головний пароль неможливо відновити — функцію скидання пароля відсутня — тому спочатку збережіть його у надійному місці.

Тепер закрийте доступ. Відредагуйте файл Compose, щоб вимкнути реєстрацію нових користувачів:

      SIGNUPS_ALLOWED: "false"

Застосуйте зміни за допомогою docker compose up -d. Це не критичне налаштування безпеки, яке можна відкласти. Якщо залишити реєстрацію відкритою, будь-хто, хто знайде URL — а це роблять пошукові роботи — зможе створити обліковий запис на вашому сервері. Вони не зможуть прочитати ваш сховище, але вони споживатимуть ресурси та перетворять ваш приватний екземпляр на відкритий сервіс. Ознака того, що реєстрацію не вимкнули: /admin містить облікові записи, які ви не створювали.

Щоб пізніше додати членів родини або колег без повторного відкриття публічної реєстрації, використовуйте кнопку Invite User у /admin; для цього шляху має бути налаштовано SMTP, щоб запрошений отримав посилання.

Step 5 — перехід до /admin

Перейдіть за адресою https://vault.example.com/admin та введіть токен адміна у відкритому вигляді (випадковий рядок або пароль, який ви хешували — не сам хеш). У панелі можна переглядати список користувачів, змінювати налаштування, надсилати тестовий email та створювати знімок (snapshot) бази даних.

Якщо сторінка повертає 404 Not Found, це означає, що ADMIN_TOKEN порожній або не встановлений, що повністю вимикає панель — це прийнятне рішення, якщо панель вам не потрібна. Якщо сторінка завантажується, але токен не підходить, перегляньте помилку $$ у списку нижче. Забули токен? Функції відновлення немає; відредагуйте .env або Compose file, встановіть новий токен і docker compose up -d.

Step 6 — підключення клієнтів Bitwarden

Кожен офіційний клієнт підтримує роботу з self-hosted сервером. Встановіть Bitwarden desktop, mobile або browser client зі звичайних магазинів додатків — спеціальна збірка Vaultwarden не потрібна.

Перед входом у систему натисніть іконку налаштувань на екрані входу (позначену як Self-hosted або Region → Self-hosted). Встановіть значення Server URL на https://vault.example.com і збережіть зміни. Після цього увійдіть у систему, використовуючи ваш email та master password; клієнт має підключитися миттєво та запропонувати автоматичне заповнення та збереження облікових даних.

Якщо клієнт відображає This is not a recognized Bitwarden server. You may need to check with your provider or update your server., це означає, що URL-адреса вказана невірно, використовується протокол http або сертифікат не є надійним. Спочатку перевірте, чи коректно завантажується https://vault.example.com у браузері. Повільне оновлення даних на інших пристроях пов'язане з роботою WebSocket push, про це описано нижче.

Step 7 — jail для Fail2ban для endpoint авторизації

Vaultwarden записує кожну невдалу спробу входу у файл, визначений LOG_FILE — це саме те, що потрібно для захисту від brute-force. Якщо Fail2ban ще не встановлено, інструкції з інсталяції та основи наведено в посібнику з налаштування Fail2ban для SSH; тут ми додамо один jail для vault.

Спочатку знайдіть шлях до іменованого volume на хості, щоб Fail2ban міг читати лог:

docker volume inspect vaultwarden_vw-data --format '{{ .Mountpoint }}'

Це виведе щось на кшталт /var/lib/docker/volumes/vaultwarden_vw-data/_data; лог знаходиться за шляхом vaultwarden.log всередині нього. Створіть filter:

# /etc/fail2ban/filter.d/vaultwarden.conf
[Definition]
failregex = ^.*Username or password is incorrect\. Try again\. IP: <ADDR>\. Username:.*$
ignoreregex =

Та jail:

# /etc/fail2ban/jail.d/vaultwarden.local
[vaultwarden]
enabled   = true
filter    = vaultwarden
logpath   = /var/lib/docker/volumes/vaultwarden_vw-data/_data/vaultwarden.log
banaction = iptables-allports
chain     = DOCKER-USER
maxretry  = 5
findtime  = 600
bantime   = 3600

Перезавантажте конфігурацію за допомогою sudo systemctl restart fail2ban та перевірте статус через sudo fail2ban-client status vaultwarden.

Ефективність захисту залежить від трьох налаштувань Docker. По-перше, якщо в логах при кожній невдалій спробі відображається IP: 127.0.0.1 або IP-адреса вашого проксі, Vaultwarden буде блокувати проксі — встановіть IP_HEADER на заголовок, який реально надсилає ваш проксі (X-Forwarded-For для Traefik, X-Real-IP для блоку nginx вище, CF-Connecting-IP для Cloudflare). По-друге, вибір ланцюжка iptables залежить від вашого проксі: якщо Traefik запущено як контейнер із опублікованими портами, трафік проходить через шлях Docker FORWARD, тому бан має бути в DOCKER-USER, як вказано вище; але якщо ви обрали варіант з host-nginx на Step 1, з'єднання завершуються на nginx у ланцюжку INPUT хоста, і бан у DOCKER-USER їх не побачить — у такому разі видаліть рядок chain = DOCKER-USER, щоб Fail2ban використовував стандартний ланцюжок INPUT. По-третє, використовуйте banaction = iptables-allports замість стандартного блокування за портами — цей jail не визначає порт, тому блокування всіх портів у DOCKER-USER надійно закриває порушнику доступ до всіх опублікованих сервісів на машині.

Step 8 — зробіть резервну копію сховища, а потім відновіть його

Том vw-data є вашим менеджером паролів. Він містить db.sqlite3 (кожен запис), директорії attachments/ та sends/, файли rsa_key.* для підпису сесій входу та config.json з панелі адміністратора. Резервна копія, що пропускає будь-який із цих елементів, не спрацює, коли вона знадобиться.

Копіювання db.sqlite3 під час запису Vaultwarden може призвести до створення пошкодженого файлу, тому зробіть snapshot системи — час простою становитиме лише кілька секунд:

#!/usr/bin/env bash
set -euo pipefail
STAMP=$(date +%F)
DEST=/root/vw-backups
VOL=$(docker volume inspect vaultwarden_vw-data --format '{{ .Mountpoint }}')
mkdir -p "$DEST"
docker compose -f /opt/vaultwarden/docker-compose.yml stop vaultwarden
tar czf "$DEST/vw-$STAMP.tgz" -C "$VOL" .
docker compose -f /opt/vaultwarden/docker-compose.yml start vaultwarden

Налаштуйте запуск через cron щоночі та копіюйте .tgz з сервера — резервна копія, яка зберігається лише на сервері, що захищається, не є повноцінною резервною копією. Найкращий спосіб передачі — щоденне резервне копіювання restic на інший сервер або object storage, що забезпечує шифрування архіву та дедуплікацію повторюваних snapshot. Кнопка Backup Database у панелі адміністратора створює швидкий snapshot лише файлу SQLite, але вона не включає вкладення та ключі.

Тепер виконайте перевірку, яка відрізняє справжню резервну копію від потенційної — відновіть її один раз, щоб переконатися у її працездатності:

mkdir -p /tmp/vw-restore
tar xzf /root/vw-backups/vw-2026-07-15.tgz -C /tmp/vw-restore
docker run --rm -p 127.0.0.1:8888:80 -v /tmp/vw-restore:/data vaultwarden/server

З вашого ноутбука створіть тунель через ssh -L 8888:127.0.0.1:8888 you@your-vps та відкрийте http://localhost:8888. Оскільки localhost є безпечним контекстом, crypto.subtle буде доступний, і сховище розшифрується через plain http — це єдине дозволене місце. Увійдіть за допомогою головного пароля та перевірте наявність записів: якщо вони на місці, то ваша база даних, RSA ключі та головний пароль успішно пройшли цикл перевірки, і ви зможете розгорнути систему на новому VPS за лічені хвилини. Зупиніть контейнер за допомогою Ctrl-C та видаліть /tmp/vw-restore.

Failure modes, with the strings you will see

Cannot read properties of undefined (reading 'importKey') in the browser console. The vault was loaded over http, so crypto.subtle is undefined; reach it only via https:// and add the HTTP-to-HTTPS redirect at the proxy.

This is not a recognized Bitwarden server... in a client. The Server URL is http, mistyped, or the certificate is untrusted; confirm https://vault.example.com shows a valid padlock, then re-enter it in the client's self-hosted settings.

/admin rejects the correct password. The Argon2 hash lost its escaping — every $ must be $$ in Compose — or you entered the hash instead of the plaintext it represents.

Slow cross-device sync; console shows WebSocket connection to 'wss://vault.example.com/notifications/hub' failed. The proxy is not forwarding the Upgrade/Connection headers; Traefik does this automatically, nginx needs the two upgrade lines from Step 1. The vault still works, just syncing on open. The old dedicated port 3012 is gone since v1.31.0, so no separate WebSocket route is needed.

Fail2ban reports a ban but the attacker keeps connecting. It is banning 127.0.0.1 because IP_HEADER is wrong, or the ban sits in the wrong iptables chain — set chain = DOCKER-USER and banaction = iptables-allports.

Upgrades

Завантажте новий образ і перестворіть контейнер; іменований volume та всі ваші дані збережуться:

docker compose pull
docker compose up -d

Vaultwarden випускає оновлення часто. Слідкуйте за release notes проєкту замість фіксації конкретної версії патча, оскільки деякі релізи містять примітки щодо міграції. Перед будь-яким значним оновленням зробіть свіжу резервну копію; ви можете відкотитися, відновивши tarball у новий volume.

FAQ

Чи є Vaultwarden тим самим, що й Bitwarden?

Це сумісний незалежний сервер, а не офіційний продукт. Vaultwarden реалізує Bitwarden server API мовою Rust. Це дозволяє використовувати офіційні клієнти для desktop, mobile, browser та CLI, споживаючи значно менше ресурсів, ніж офіційний стек. Формат сховища однаковий, тому ви можете здійснювати міграцію в обох напрямках через експорт та імпорт.

Чи справді мені потрібен HTTPS, чи можна використовувати http у локальній мережі (LAN)?

HTTPS необхідний для будь-яких цілей, окрім localhost тестування. Веб-сховище та розширення Bitwarden використовують браузерний Web Crypto API. Цей API доступний лише в безпечному контексті (secure context), тому при використанні plain http клієнт видає помилку Cannot read properties of undefined і не дозволяє увійти в систему. Єдиною робочою http-адресою є http://localhost, тому для тесту відновлення у Кроці 8 використовується SSH-тунель.

Як заборонити стороннім особам реєстрацію на моєму сервері?

Встановіть SIGNUPS_ALLOWED: "false" у Compose файлі та запустіть docker compose up -d одразу після створення власного облікового запису. Після цього додавайте нових користувачів через кнопку Invite User у /admin. Для отримання посилання на запрошення у користувачів має бути налаштовано SMTP. Періодично перевіряйте список адміністраторів, щоб переконатися у відсутності сторонніх облікових записів.

Як зробити резервну копію сховища Vaultwarden?

Здійснюйте зупинку контейнера на короткий час та архівуйте весь том vw-data — файли db.sqlite3, attachments/, sends/, config.json та rsa_key.*. Потім скопіюйте архів з сервера, бажано за розкладом cron щоночі. Копіювання активного файлу SQLite під час роботи сервера може призвести до пошкодження знімка (snapshot), тому робіть резервну копію при зупиненому сервері. Найголовніше — один раз відновіть дані у тестовий контейнер і увійдіть у систему. Це підтвердить працездатність бекапу перед тим, як ви почнете на нього покладатися.

Чи безпечно хостити свої паролі самостійно (self-host)?

Так, якщо ви виконаєте три умови, описані в цьому посібнику: налаштуєте справжній HTTPS, закриєте реєстрацію та встановите надійний admin token, а також протестуєте резервні копії. Ваше сховище шифрується на стороні клієнта за допомогою вашого головного пароля. Сервер ніколи не бачить паролі у відкритому вигляді — вкрадений db.sqlite3 не матиме цінності без пароля. Натомість ви берете на себе відповідальність за оновлення та резервне копіювання, тому використання Fail2ban та регулярне тестування відновлення є обов'язковими.

#vaultwarden#passwords#security#docker#self-hosting