SSD Nodes Learn
คู่มือ Matt Connorโดย Matt Connor · อัปเดตเมื่อ 2026-07-24

วิธีติดตั้ง Vaultwarden บน VPS ด้วย Docker

สอนติดตั้ง Vaultwarden บน VPS เพื่อใช้จัดการรหัสผ่านแทน Bitwarden โดยใช้ Docker พร้อมวิธีตั้งค่า HTTPS และการทำ Backup ข้อมูลใน Data Volume อย่างปลอดภัย

สิ่งที่คุณกำลังสร้าง

ระบบจัดการรหัสผ่านที่คุณเป็นเจ้าของอย่างสมบูรณ์: ใช้ Vaultwarden รันใน container ขนาดเล็กผ่าน reverse proxy ที่ทำหน้าที่ terminate HTTPS โดยใช้แอปพลิเคชัน Bitwarden อย่างเป็นทางการบนโทรศัพท์, laptop และ browser เพื่อเชื่อมต่อมายังระบบนี้ Vaultwarden เขียนขึ้นใหม่ด้วยภาษา Rust โดยใช้ API เดียวกับ bitwarden.com ทำให้ client อย่างเป็นทางการทุกตัวสามารถใช้งานได้ทันทีโดยไม่ต้องแก้ไข แต่ใช้ทรัพยากร RAM เพียงประมาณ 100 MB ซึ่งน้อยกว่าการใช้ official stack ที่ต้องรันหลาย container

ขั้นตอนการติดตั้งใช้เพียงคำสั่ง Compose ประมาณ 12 บรรทัด ส่วนปัจจัยสำคัญ 3 ประการที่ต้องระวังและมักเป็นสาเหตุให้ระบบมีปัญหา ได้แก่: ต้องมีการตั้งค่า TLS ก่อนเริ่มใช้งาน web vault, ต้องปิดการสมัครสมาชิกแบบสาธารณะ (public signups) ทันทีหลังจากสร้างบัญชีของคุณเสร็จสิ้น และต้องมีการสำรองข้อมูล (backup) พร้อมทดสอบการกู้คืน (test-restore) ข้อมูลใน data volume เนื่องจาก directory ดังกล่าวเป็นที่เก็บรหัสผ่านทั้งหมดของคุณ

สิ่งที่ต้องเตรียมและข้อควรระวัง

  • VPS ที่ติดตั้ง Docker Engine และ Compose plugin บน Ubuntu 24.04 KVM ที่ติดตั้งใหม่ โดยมีสิทธิ์ root หรือ sudo แนะนำให้ใช้ RAM 1 GB เพื่อความสะดวก แต่ 512 MB ก็เพียงพอต่อการใช้งาน นี่คือหนึ่งในบริการที่ใช้ทรัพยากรน้อยที่สุด ซึ่งอยู่ใน รายการบริการที่ควรติดตั้งด้วยตนเอง
  • Domain ที่มีการตั้งค่า A record (และ AAAA หากใช้ IPv6) ชี้ไปยัง vault.example.com ของ VPS ใบนี้ ใบรับรอง TLS จะถูกออกให้สำหรับชื่อนี้โดยเฉพาะ ดังนั้นต้องตรวจสอบให้แน่ใจว่า DNS สามารถ Resolve ได้ก่อนเริ่มดำเนินการ
  • เปิด Port 80 และ 443 สู่สาธารณะ โดยให้ reverse proxy เป็นผู้จัดการการเชื่อมต่อ — ห้าม เชื่อมต่อกับ Vaultwarden โดยตรง Port 80 ใช้สำหรับ ACME certificate challenge และการทำ HTTP-to-HTTPS redirect เท่านั้น
  • ข้อควรระวังที่สำคัญที่สุด: Bitwarden client จะไม่ยอมเชื่อมต่อกับ server ที่ไม่ได้ใช้งาน HTTPS คุณไม่สามารถ "ทดสอบผ่าน http ก่อน" ได้ เนื่องจากมีข้อจำกัดทางเทคนิคซึ่งจะอธิบายในหัวข้อถัดไป

ทำไมต้อง Vaultwarden แทนที่จะเป็น Bitwarden stack แบบทางการ

ใช้งานร่วมกับ client ได้เหมือนกัน แต่ใช้ทรัพยากรน้อยกว่ามาก Bitwarden แบบ self-hosted อย่างเป็นทางการมาในรูปแบบของกลุ่ม container (MSSQL, Nginx, Identity, Api, Admin และอื่นๆ) และต้องการ RAM ประมาณ 2 GB ส่วน Vaultwarden เป็น single binary ที่เก็บข้อมูลทั้งหมดใน SQLite database โดยค่าเริ่มต้น และใช้ทรัพยากรขณะ idle เพียงไม่กี่สิบ megabytes เท่านั้น สำหรับผู้ใช้งานคนเดียว ครอบครัว หรือทีมขนาดเล็ก Vaultwarden คือตัวเลือกที่เหมาะสมที่สุด และเนื่องจากมีการ implement Bitwarden API อย่างถูกต้อง ข้อมูลของคุณจึงสามารถย้ายไปมาระหว่าง Vaultwarden และ bitwarden.com ได้อย่างสะดวก

สิ่งที่คุณต้องแลกมาคือฟีเจอร์ระดับ enterprise ส่วนใหญ่ เช่น ไม่มี SCIM provisioning (แม้ว่าจะมี OpenID Connect SSO แบบ experimental ในเวอร์ชัน 1.35.0) และคุณต้องเป็นผู้ดูแลระบบเอง ดังนั้นการ patch ระบบ, การตั้งค่า HTTPS และการทำ backup จึงเป็นหน้าที่ของคุณ คู่มือนี้จะครอบคลุมทั้ง 3 งานดังกล่าว

ทำไม HTTPS จึงเป็นสิ่งจำเป็น

Bitwarden web vault และ browser extensions ใช้ Web Crypto API (window.crypto.subtle) ในการสร้าง encryption keys ภายใน browser โดย browser จะอนุญาตให้ใช้งาน crypto.subtle ได้เฉพาะใน secure context เท่านั้น ซึ่งได้แก่ HTTPS หรือกรณีพิเศษอย่าง http://localhost หากใช้งานผ่าน http://vault.example.com จะถือว่า undefined ส่งผลให้แอปพลิเคชันเกิดข้อผิดพลาดทันทีที่พยายามสร้าง key และ console จะแสดงข้อความ:

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

หน้าเว็บจะค้างหรือแสดงข้อผิดพลาดทาง crypto ทั่วไป และไม่สามารถเข้าสู่ระบบได้ ส่วน client บน desktop, mobile และ browser จะทำการตรวจสอบ URL ที่ติดตั้งเอง (self-hosted) หากพบว่าเป็น http (หรือ endpoint ที่เข้าถึงไม่ได้) ระบบจะปฏิเสธการเชื่อมต่อพร้อมแสดงข้อความ:

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

ทั้งสองกรณีมีสาเหตุเดียวกันคือ ไม่มี HTTPS ที่ถูกต้อง ดังนั้นควรติดตั้ง TLS ให้เรียบร้อยก่อน และห้ามเปิดใช้งาน vault ผ่าน http โดยเด็ดขาด แม้จะเป็นการใช้งานเพียงชั่วคราวก็ตาม

Step 1 — DNS and the reverse proxy (TLS first)

ชี้ record ไปที่ VPS ของคุณ และตรวจสอบว่า resolve ไปยัง address ที่ถูกต้อง:

dig +short vault.example.com

ผลลัพธ์ที่แสดงต้องเป็น IP ของ VPS หากค่าว่างหรือผิดพลาด ให้แก้ไข DNS และรอจนกว่า TTL จะหมดอายุ การออก certificate จะล้มเหลวหากชื่อโดเมนไม่สามารถ resolve ได้

สำหรับ HTTPS front end คู่มือนี้ใช้ Traefik ซึ่งทำหน้าที่ออกและต่ออายุ Let's Encrypt certificates โดยอัตโนมัติ และสามารถใช้งานร่วมกับ Compose ได้ทันที หากคุณยังไม่ได้ติดตั้ง ให้ทำตาม the Traefik reverse proxy and automatic TLS setup ก่อน เนื่องจากระบบจะสร้าง external Docker network (proxy ด้านล่าง) และ ACME resolver (letsencrypt) เพื่อให้ Vaultwarden service ใช้งาน การใช้ nginx แบบปกติพร้อมกับ certificate ที่ออกเองสามารถใช้งานร่วมกับ Vaultwarden ได้เช่นเดียวกัน

หากต้องการใช้ nginx และ Certbot แทน Traefik? ให้ติดตั้ง Vaultwarden บน 127.0.0.1:8080 (เพิ่ม ports: ["127.0.0.1:8080:80"] ใน service และลบ Traefik labels ออก) จากนั้นจึงออก certificate และทำ proxy ไปยัง service ดังกล่าว ข้อมูลเกี่ยวกับการออก certificate สามารถอ่านได้ใน issuing Let's Encrypt certificates with Certbot and nginx ส่วนที่สำคัญเพิ่มเติมคือการทำ WebSocket upgrade บน notifications path:

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 สามารถตรวจพบ attacker ตัวจริงแทนที่จะเป็น 127.0.0.1 ขั้นตอนอื่น ๆ ในคู่มือนี้จะเหมือนกันไม่ว่าจะใช้ Traefik หรือ nginx เป็น front end ก็ตาม

Step 2 — the Compose file

สร้าง directory ของโปรเจกต์ก่อน คู่มือนี้ใช้ /opt/vaultwarden เพื่อกำหนดชื่อโปรเจกต์ Compose ซึ่งจะทำให้ชื่อของ data volume คือ vaultwarden_vw-data มีรูปแบบที่แน่นอน ขั้นตอนการตั้งค่า Fail2ban และการ backup ด้านล่างนี้จำเป็นต้องใช้ชื่อที่ถูกต้องนี้

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

สร้าง .env สำหรับเก็บ admin secret และ Compose file ไว้ใน directory ดังกล่าว

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

สร้าง token ด้วย openssl rand -base64 48 แล้วนำมาวางในไฟล์ (ขั้นตอนถัดไปจะกล่าวถึงการใช้ hash ที่ปลอดภัยกว่านี้ สำหรับเริ่มต้นสามารถใช้ string สุ่มที่มีความยาวมากได้)

# 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

องค์ประกอบสองส่วนในไฟล์นี้คือหัวใจสำคัญของระบบ การตั้งค่านี้จะ ไม่มี การทำ mapping ports: เพื่อให้ Vaultwarden เข้าถึงได้ผ่านทาง Traefik และ TLS เท่านั้น การเปิด port ของ Vaultwarden บน host จะทำให้ผู้ใช้เข้าถึง vault ผ่าน http โดยไม่ตั้งใจ และ DOMAIN จะต้องเป็น URL แบบ HTTPS ที่เข้าถึงได้จากสาธารณะเท่านั้น เนื่องจากค่านี้จะถูกนำไปใช้ใน link ของไฟล์แนบ, WebAuthn 2FA และ endpoint ของการแจ้งเตือน หากระบุค่าผิดหรือใช้ http จะทำให้ฟังก์ชันเหล่านี้ใช้งานไม่ได้แม้ว่าเว็บไซต์จะโหลดขึ้นมาก็ตาม สำหรับ tag latest ถือเป็นข้อยกเว้นที่ตั้งใจให้ต่างจากกฎการไม่ใช้ latest ทั่วไป เนื่องจาก Vaultwarden ปล่อย release ที่เสถียรในรูปแบบ single rolling image โดยมี :testing เป็นช่องทางสำหรับ pre-release แยกต่างหาก ดังนั้นควรตั้งใจอัปเดตและตรวจสอบ release notes ก่อนทำการ pull

เริ่มการทำงานและตรวจสอบ log:

docker compose up -d
docker compose logs -f vaultwarden

หากเริ่มต้นระบบได้ถูกต้อง จะต้องปรากฏบรรทัดลักษณะ Rocket has launched from http://0.0.0.0:80 ให้เวลา Traefik สักครู่เพื่อดึง certificate จากนั้นให้โหลด https://vault.example.com คุณควรเข้าใช้งาน Bitwarden web vault ได้โดยมีสัญลักษณ์แม่กุญแจที่ถูกต้องและไม่มีการแจ้งเตือนเรื่อง certificate

Step 3 — การใช้ ADMIN_TOKEN ที่มีความปลอดภัยสูง และข้อควรระวังเรื่อง $$

ADMIN_TOKEN ใช้สำหรับป้องกัน /admin ซึ่งเป็นแผงควบคุมที่สามารถอ่านข้อมูลผู้ใช้และค่าการตั้งค่าทั้งหมดใน instance ของคุณได้ ดังนั้นควรเก็บรักษาเสมือนเป็นรหัสผ่าน root โดยสามารถใช้ได้ 2 รูปแบบ

รูปแบบพื้นฐานคือข้อความสุ่มที่คุณสร้างไว้แล้วด้วย openssl rand -base64 48 เนื่องจาก base64 ไม่มีเครื่องหมาย $ จึงสามารถนำไปใส่ใน .env ได้ทันทีโดยไม่ต้องทำ escaping

รูปแบบที่มีความปลอดภัยสูงคือ Argon2 PHC hash เพื่อไม่ให้มีการเก็บ token แบบ plaintext ไว้ใน disk สำหรับการสร้าง hash ให้ใช้ image ตัวเดียวกัน:

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

ระบบจะให้คุณป้อนข้อมูลสองครั้ง และแสดงข้อความที่เริ่มต้นด้วย $argon2id$v=19$... ข้อผิดพลาดที่ทำให้ผู้ใช้เสียเวลาไปกว่าหนึ่งชั่วโมงคือ Docker Compose จะมองว่า $ คือการแทนค่าตัวแปร (variable interpolation) ดังนั้นคุณต้อง ใส่ $ ซ้ำสองครั้งเป็น $$ เมื่อวาง hash ลงในไฟล์ 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 และทำให้ token ว่างเปล่า ซึ่งจะส่งผลให้ /admin ปฏิเสธรหัสผ่านที่ถูกต้องของคุณ ให้รันคำสั่ง docker compose up -d และควรเก็บข้อความ plaintext ที่คุณป้อนไว้ใน password store ของคุณเอง

Step 4 — register your account, then lock the door

ใช้ SIGNUPS_ALLOWED: "true" เปิด https://vault.example.com จากนั้นคลิก Create account และลงทะเบียนด้วยอีเมลและ master password ที่คาดเดายาก คุณไม่สามารถกู้คืน master password นี้ได้เนื่องจากไม่มีระบบรีเซ็ต ดังนั้นควรบันทึกรหัสผ่านไว้ในที่ที่ปลอดภัยก่อน

จากนั้นทำการปิดการลงทะเบียน ให้แก้ไขไฟล์ Compose เพื่อปิดการสมัครสมาชิก:

      SIGNUPS_ALLOWED: "false"

ใช้คำสั่ง docker compose up -d เพื่อเริ่มการทำงานใหม่ การตั้งค่านี้ไม่ใช่ขั้นตอนการเพิ่มความปลอดภัยที่จำเป็นต้องทำทันที แต่หากปล่อยทิ้งไว้ ผู้ที่พบ URL — รวมถึง crawler — จะสามารถสร้างบัญชีบน server ของคุณได้ แม้พวกเขาจะไม่สามารถอ่านข้อมูลใน vault ของคุณได้ แต่จะทำให้เกิดการใช้ทรัพยากรและเปลี่ยน instance ส่วนตัวของคุณให้กลายเป็นบริการสาธารณะ หากคุณลืมปิดระบบลงทะเบียน ให้ตรวจสอบจาก /admin ซึ่งจะแสดงรายชื่อบัญชีที่คุณไม่ได้เป็นผู้สร้างเอง

หากต้องการเพิ่มสมาชิกในครอบครัวหรือเพื่อนร่วมงานในภายหลังโดยไม่เปิดระบบสมัครสมาชิกสาธารณะ ให้ใช้ปุ่ม Invite User ใน /admin โดยต้องตั้งค่า SMTP เพื่อให้ผู้รับได้รับลิงกเชิญ

Step 5 — การเข้าถึง /admin

เปิดไปที่ https://vault.example.com/admin และป้อน admin token แบบ plaintext (ใช้ข้อความสุ่มหรือรหัสผ่านที่คุณนำไป hash — ห้ามใช้ตัว hash เอง) ภายในส่วนนี้ คุณสามารถเรียกดูรายชื่อผู้ใช้, ปรับแต่งการตั้งค่า, ส่งอีเมลทดสอบ และทำ database snapshot ได้

หากหน้าเว็บแสดงผล 404 Not Found แสดงว่า ADMIN_TOKEN ว่างเปล่าหรือไม่ได้ตั้งค่าไว้ ซึ่งจะทำให้ panel ถูกปิดการใช้งานโดยสิ้นเชิง ซึ่งเป็นทางเลือกที่เหมาะสมหากคุณไม่จำเป็นต้องใช้งาน หากหน้าเว็บโหลดขึ้นมาแต่ปฏิเสธ token ของคุณ โปรดตรวจสอบปัญหาเรื่องการ escaping ของ $$ ในรายการข้อผิดพลาดด้านล่าง หากลืม token จะไม่มีระบบกู้คืน ให้แก้ไขไฟล์ .env หรือ Compose file โดยกำหนด token ใหม่ แล้ว docker compose up -d

Step 6 — การเชื่อมต่อ Bitwarden clients

Official client ทุกตัวสามารถเชื่อมต่อกับ self-hosted server ได้ ดังนั้นคุณสามารถติดตั้ง Bitwarden desktop, mobile หรือ browser client จาก store ปกติได้ทันที โดย ไม่จำเป็น ต้องใช้ build พิเศษของ Vaultwarden

ก่อนทำการ login ให้คลิกที่ไอคอนรูปฟันเฟืองบนหน้าจอ login (ระบุว่า Self-hosted หรือ Region → Self-hosted) จากนั้นตั้งค่า Server URL เป็น https://vault.example.com แล้วกด save เมื่อ login ด้วย email และ master password ที่ลงทะเบียนไว้แล้ว client จะเชื่อมต่อทันที และพร้อมสำหรับการกรอกข้อมูลหรือบันทึก credentials

หาก client แสดงข้อความ This is not a recognized Bitwarden server. You may need to check with your provider or update your server. แสดงว่า URL ไม่ถูกต้อง, มีการใช้ http หรือ certificate ไม่ได้รับความเชื่อถือ ให้ตรวจสอบก่อนว่า https://vault.example.com สามารถโหลดผ่าน browser ได้ตามปกติหรือไม่ สำหรับปัญหาการอัปเดตข้อมูลล่าช้าบนอุปกรณ์อื่น เกิดจากการทำงานของ WebSocket push ซึ่งมีรายละเอียดระบุไว้ด้านล่าง

Step 7 — การตั้งค่า Fail2ban jail สำหรับ login endpoint

Vaultwarden บันทึกการเข้าสู่ระบบที่ล้มเหลวทุกครั้งลงในไฟล์ที่กำหนดโดย LOG_FILE ซึ่งเป็นข้อมูลที่จำเป็นสำหรับการป้องกันการโจมตีแบบ brute-force หากคุณยังไม่ได้ติดตั้ง Fail2ban สามารถดูวิธีการติดตั้งและพื้นฐานได้ที่ คู่มือการทำ Fail2ban SSH hardening ส่วนในบทนี้เราจะเพิ่ม jail สำหรับ vault

ขั้นตอนแรก ให้หาตำแหน่งที่ตั้งของ named volume บน host เพื่อให้ Fail2ban สามารถอ่าน log ได้:

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

ผลลัพธ์จะแสดงค่าประมาณ /var/lib/docker/volumes/vaultwarden_vw-data/_data โดยไฟล์ log จะอยู่ที่ 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

ทำการ reload ด้วยคำสั่ง sudo systemctl restart fail2ban และตรวจสอบความถูกต้องด้วย sudo fail2ban-client status vaultwarden

รายละเอียดของ Docker 3 ประการมีผลต่อประสิทธิภาพในการป้องกัน ประการแรก หาก log แสดงค่า IP: 127.0.0.1 หรือ IP ของ proxy ในทุกความพยายามที่ล้มเหลว Vaultwarden จะทำการแบน proxy แทน ให้ตั้งค่า IP_HEADER เป็น header ที่ proxy ของคุณส่งมาจริง (ใช้ X-Forwarded-For สำหรับ Traefik, X-Real-IP สำหรับ nginx block ด้านบน หรือ CF-Connecting-IP หากใช้งานผ่าน Cloudflare) ประการที่สอง การเลือก iptables chain ที่ถูกต้องขึ้นอยู่กับ proxy ของคุณ หากใช้ Traefik ในรูปแบบ container ที่มีการ publish ports ข้อมูลจะวิ่งผ่านเส้นทาง FORWARD ของ Docker ดังนั้นการแบนต้องกำหนดไว้ใน DOCKER-USER ตามตัวอย่างข้างต้น แต่หากคุณเลือกใช้ host-nginx จาก Step 1 การเชื่อมต่อจะสิ้นสุดที่ nginx บน chain INPUT ของ host และการแบนผ่าน DOCKER-USER จะไม่สามารถตรวจพบการเชื่อมต่อได้ ในกรณีนี้ให้ลบบรรทัด chain = DOCKER-USER ออกเพื่อให้ Fail2ban ใช้ chain INPUT ซึ่งเป็นค่าเริ่มต้น ประการที่สาม ให้ใช้ banaction = iptables-allports แทนการใช้ port แบบเริ่มต้น เนื่องจาก jail นี้ไม่ได้ระบุ port การแบนทุก port ใน DOCKER-USER จะช่วยบล็อกผู้กระทำผิดจากการเข้าถึงทุก service ที่ publish ไว้บนเครื่องได้อย่างสมบูรณ์

Step 8 — สำรองข้อมูล vault จากนั้นทำการ restore

volume vw-data คือ password manager ของคุณ ข้อมูลนี้ประกอบด้วย db.sqlite3 (ทุก entry), directory attachments/ และ sends/, ไฟล์ rsa_key.* สำหรับลงลายเซ็น login sessions และ config.json จาก admin panel การสำรองข้อมูลที่ไม่ครอบคลุมส่วนใดส่วนหนึ่งข้างต้นจะทำให้การกู้คืนล้มเหลวเมื่อจำเป็นต้องใช้งาน

การคัดลอก db.sqlite3 ในขณะที่ Vaultwarden กำลังเขียนข้อมูลอาจทำให้ได้ไฟล์ที่เขียนไม่สมบูรณ์และเสียหาย ดังนั้นควรทำ cold 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 ออกจากเครื่อง — การสำรองข้อมูลที่เก็บไว้บน server เครื่องเดียวกับที่กำลังป้องกันอยู่นั้นไม่ใช่การสำรองข้อมูลที่ถูกต้อง วิธีการที่เหมาะสมคือ การทำ nightly restic backup ไปยัง server อื่นหรือ object storage ซึ่งจะทำการเข้ารหัส archive และทำ deduplication สำหรับ snapshot ที่ซ้ำกันให้โดยอัตโนมัติ ปุ่ม Backup Database ใน admin panel เป็นวิธีที่สะดวกในการทำ snapshot ของไฟล์ SQLite เพียงอย่างเดียว แต่จะไม่รวมไฟล์แนบและ keys

ขั้นตอนต่อไปคือการพิสูจน์ว่าการสำรองข้อมูลนั้นใช้งานได้จริง ไม่ใช่แค่การคาดเดา — ให้ทำการ restore หนึ่งครั้งเพื่อทดสอบ:

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

ใช้ laptop เชื่อมต่อผ่าน tunnel ด้วย ssh -L 8888:127.0.0.1:8888 you@your-vps และเปิด http://localhost:8888 เนื่องจาก localhost เป็น secure context จึงทำให้ crypto.subtle สามารถใช้งานได้ และ vault จะถอดรหัสผ่าน plain http ในขั้นตอนนี้ ซึ่งเป็นจุดเดียวที่อนุญาตให้ทำได้ ให้เข้าสู่ระบบด้วย master password และตรวจสอบว่า entry ต่างๆ ปรากฏขึ้นครบถ้วน หากข้อมูลมาครบ แสดงว่า database, RSA keys และ master password สามารถใช้งานได้สมบูรณ์ และคุณสามารถติดตั้งระบบใหม่บน VPS เครื่องใหม่ได้ภายในไม่กี่นาที ให้หยุดการทำงานของ container ด้วย Ctrl-C และลบ /tmp/vw-restore ทิ้ง

รูปแบบข้อผิดพลาดและข้อความที่จะปรากฏ

Cannot read properties of undefined (reading 'importKey') ใน browser console. ข้อมูลใน vault ถูกโหลดผ่าน http ทำให้ crypto.subtle มีค่าเป็น undefined; ให้เข้าถึงผ่านทาง https:// เท่านั้น และควรตั้งค่า HTTP-to-HTTPS redirect ที่ proxy

This is not a recognized Bitwarden server... ใน client. Server URL เป็น http, พิมพ์ผิด หรือใบรับรอง (certificate) ไม่ได้รับความเชื่อถือ; ให้ตรวจสอบว่า https://vault.example.com แสดงรูปแม่กุญแจที่ถูกต้อง จากนั้นให้กรอก URL ใหม่ใน settings ของ client ที่ติดตั้งแบบ self-hosted

/admin ปฏิเสธรหัสผ่านที่ถูกต้อง Argon2 hash สูญเสียการ escaping — ทุก $ ต้องเป็น $$ ใน Compose — หรือคุณกรอกค่า hash แทนที่จะเป็นรหัสผ่าน plaintext

การซิงค์ข้ามอุปกรณ์ล่าช้า; console แสดง WebSocket connection to 'wss://vault.example.com/notifications/hub' failed proxy ไม่ได้ส่งต่อ (forward) header Upgrade/Connection; Traefik จะดำเนินการส่วนนี้โดยอัตโนมัติ แต่ nginx จำเป็นต้องใช้บรรทัด upgrade สองบรรทัดจาก Step 1 ข้อมูลใน vault ยังคงใช้งานได้ปกติ แต่จะซิงค์เมื่อมีการเปิดโปรแกรมเท่านั้น พอร์ต 3012 เดิมถูกยกเลิกไปตั้งแต่ v1.31.0 จึงไม่จำเป็นต้องตั้งค่า WebSocket route แยกต่างหาก

Fail2ban รายงานการแบนแต่ผู้โจมตียังคงเชื่อมต่อได้ ระบบกำลังแบน 127.0.0.1 เนื่องจาก IP_HEADER ไม่ถูกต้อง หรือการแบนถูกเก็บไว้ใน iptables chain ที่ผิด — ให้ตั้งค่า chain = DOCKER-USER และ banaction = iptables-allports

Upgrades

ดึง image ใหม่และสร้าง container ใหม่ โดยที่ named volume และข้อมูลทั้งหมดจะยังคงอยู่:

docker compose pull
docker compose up -d

Vaultwarden มีการออก release บ่อยครั้ง แนะนำให้ติดตาม release notes ของโปรเจกต์ แทนการระบุ patch version เนื่องจากบาง release อาจมีหมายเหตุเกี่ยวกับการ migration ควรสำรองข้อมูลชุดใหม่ก่อนการอัปเดตเวอร์ชันหลัก โดยคุณสามารถย้อนกลับได้ด้วยการ restore ไฟล์ tarball ลงใน volume ใหม่

FAQ

Vaultwarden คือสิ่งเดียวกับ Bitwarden หรือไม่?

Vaultwarden เป็นเซิร์ฟเวอร์อิสระที่ใช้งานร่วมกันได้ แต่ไม่ใช่เซิร์ฟเวอร์อย่างเป็นทางการ Vaultwarden เขียน Bitwarden server API ขึ้นมาใหม่ด้วยภาษา Rust ทำให้สามารถใช้งานร่วมกับ client ทั้งบน desktop, mobile, browser และ CLI ได้ โดยใช้ทรัพยากรน้อยกว่า stack อย่างเป็นทางการมาก รูปแบบของ vault นั้นเหมือนกัน ดังนั้นคุณจึงสามารถย้ายข้อมูลไปมาได้ด้วยการ export และ import

จำเป็นต้องใช้ HTTPS หรือไม่ หรือสามารถรันผ่าน http บน LAN ได้?

คุณจำเป็นต้องใช้ HTTPS ยกเว้นกรณีที่เป็นการทดสอบแบบ localhost เท่านั้น เนื่องจาก Bitwarden web vault และ extension ใช้ Web Crypto API ของ browser ซึ่งใช้งานได้เฉพาะใน secure context เท่านั้น หากใช้งานผ่าน http ปกติ client จะแสดงข้อผิดพลาด Cannot read properties of undefined และไม่สามารถ login ได้ มีเพียง address http เพียงหนึ่งเดียวที่ใช้งานได้คือ http://localhost ด้วยเหตุนี้การทดสอบการ restore ใน Step 8 จึงต้องใช้ SSH tunnel

จะป้องกันไม่ให้บุคคลอื่นลงทะเบียนบนเซิร์ฟเวอร์ได้อย่างไร?

ให้ตั้งค่า SIGNUPS_ALLOWED: "false" ใน Compose file และรัน docker compose up -d ทันทีหลังจากสร้างบัญชีของคุณเอง หลังจากนั้นให้เพิ่มผู้ใช้ใหม่ผ่านปุ่ม Invite User ใน /admin ซึ่งจำเป็นต้องตั้งค่า SMTP เพื่อให้ผู้ใช้ได้รับลิงก์คำเชิญ ควรตรวจสอบรายการผู้ใช้ในหน้า admin เป็นระยะเพื่อยืนยันว่าไม่มีบัญชีที่ไม่พึงประสงค์ปรากฏขึ้น

จะสำรองข้อมูล Vaultwarden vault ได้อย่างไร?

ให้หยุดการทำงานของ container ชั่วคราวและ archive ข้อมูลใน volume vw-data ทั้งหมด ซึ่งประกอบด้วยไฟล์ db.sqlite3, attachments/, sends/, config.json และ rsa_key.* จากนั้นให้คัดลอกไฟล์ archive ออกจากเซิร์ฟเวอร์ โดยควรตั้งค่าเป็น nightly cron การคัดลอกไฟล์ SQLite ขณะที่เซิร์ฟเวอร์กำลังทำงานอยู่มีความเสี่ยงที่จะทำให้ snapshot เสียหาย ดังนั้นควรทำการสำรองข้อมูลขณะที่หยุดการทำงานของระบบ (cold backup) สิ่งที่สำคัญที่สุดคือ ให้ลอง restore ข้อมูลลงใน container สำหรับทดสอบและลอง login เพื่อยืนยันว่าข้อมูลสำรองใช้งานได้จริงก่อนนำไปใช้งานจริง

การติดตั้งใช้งานด้วยตนเอง (self-host) ปลอดภัยจริงหรือไม่?

ปลอดภัย หากคุณปฏิบัติตาม 3 สิ่งที่คู่มือนี้แนะนำ ได้แก่: การใช้ HTTPS ของจริง, การปิดการลงทะเบียน (signups) พร้อมกับใช้ admin token ที่แข็งแกร่ง และการทดสอบระบบสำรองข้อมูล ข้อมูลใน vault ของคุณจะถูกเข้ารหัสที่ฝั่ง client ด้วย master password ดังนั้นเซิร์ฟเวอร์จึงไม่เห็นรหัสผ่านของคุณในรูปแบบข้อความปกติ แม้จะถูกขโมยไฟล์ db.sqlite3 ไปก็ไม่สามารถใช้งานได้หากไม่มีรหัสผ่านดังกล่าว ข้อแลกเปลี่ยนคือคุณต้องรับผิดชอบเรื่องการ patch และการสำรองข้อมูลด้วยตนเอง ด้วยเหตุนี้การใช้ Fail2ban และการทดสอบระบบ restore จึงเป็นสิ่งที่จำเป็นต้องทำ

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