levish
levish
이전으로
Vaultwarden 세팅하기

Vaultwarden 세팅하기

Vaultwarden 설치, 쉽게 해보자!

June 26, 2026

개발인프라셀프호스팅

시작하며

저희 팀에서는 함께 사용해야 하는 비밀번호나 API 키를 주로 Vaultwarden에 모아 관리합니다.

그동안은 제가 팀에 합류하기 전부터 이미 Vaultwarden이 구축되어 있는 경우가 많았습니다. 초대를 받고 필요한 키를 꺼내 쓰기만 하면 됐죠. 그런데 이번에는 개발 리드를 맡으면서 처음부터 직접 세팅해볼 기회가 생겼습니다.

앞으로도 비슷한 일을 할 일이 꽤 있을 것 같아, 이번에 구성했던 과정을 기록해두려고 합니다.

Vaultwarden: Unofficial Bitwarden compatible server written in Rust, formerly known as bitwarden_rs

Vaultwarden은 Bitwarden Client API를 Rust로 재구현한 비공식 서버입니다. 공식 Bitwarden 클라이언트를 그대로 사용하면서 서버는 직접 운영할 수 있고, 공식 Bitwarden 서버보다 훨씬 가볍게 구성할 수 있다는 점이 특징입니다.

한 번 서버를 만들어두면 팀원들은 각자 Bitwarden 앱이나 브라우저 확장 프로그램으로 접속해 비밀번호와 API 키를 저장하고 공유할 수 있습니다. 누군가에게 키를 전달할 때마다 메신저로 복사해 보내거나 별도의 문서에 적어둘 필요도 없습니다.

팀이나 프로젝트별로 보관함을 나누고 필요한 사람에게만 접근 권한을 줄 수도 있고, 값이 바뀌더라도 한 곳만 수정하면 됩니다. 여러 사람이 같은 자격 증명을 사용하는 상황에서는 꽤 편리합니다.

구성은 단순하게

구성은 단순합니다. Vaultwarden은 Docker 컨테이너 하나로 띄우고, 컨테이너의 /data를 호스트 디렉터리에 마운트해 데이터를 영속화했습니다.

외부 접속은 이미 서버에서 사용하고 있던 Cloudflare Tunnel을 그대로 이용했습니다.

파일은 /opt/vaultwarden 아래에 두었습니다.

/opt/vaultwarden/
├── docker-compose.yml
└── vw-data/
sudo mkdir -p /opt/vaultwarden
cd /opt/vaultwarden

docker-compose.yml 만들기

compose 파일도 크게 복잡하지 않습니다.

services:
  vaultwarden:
    image: vaultwarden/server:1.36.0-alpine
    container_name: vaultwarden
    restart: unless-stopped
    environment:
      DOMAIN: 'https://vault.example.com'
      SIGNUPS_ALLOWED: 'true'
      ADMIN_TOKEN: '$$argon2id$$v=19$$...'
    volumes:
      - ./vw-data/:/data/
    ports:
      - 127.0.0.1:12000:80

DOMAIN에는 실제로 사용할 Vaultwarden 주소를 넣었습니다.

SIGNUPS_ALLOWED는 첫 계정을 만들기 위해 우선 true로 두고, 가입이 끝난 뒤 다시 잠그기로 했습니다.

여기서 조금 신경 써야 했던 건 ADMIN_TOKEN이었습니다.

ADMIN_TOKEN 만들기

/admin 관리 페이지에 들어가려면 ADMIN_TOKEN이 필요합니다.

평문을 그대로 넣을 수도 있지만, 저는 Argon2로 해시해서 넣었습니다. 먼저 실제 로그인에 사용할 값을 하나 만듭니다.

openssl rand -base64 48

그리고 Vaultwarden에 들어있는 hash 명령을 사용합니다.

docker run --rm -it vaultwarden/server:1.36.0-alpine /vaultwarden hash

앞에서 만든 값을 입력하면 다음처럼 $argon2id$...로 시작하는 해시가 나옵니다.

$argon2id$v=19$m=65540,t=3,p=4$...

이 값을 ADMIN_TOKEN에 넣으면 됩니다.

ADMIN_TOKEN: '$$argon2id$$v=19$$m=65540,t=3,p=4$$...'

여기서 한 번 막혔던 부분이 있습니다.

compose 파일 안에서는 $가 환경변수 치환에 사용되기 때문에, 해시 안의 $를 그대로 넣으면 값이 깨집니다. $를 모두 $$로 바꿔줘야 합니다.

실제 /admin에 로그인할 때는 해시가 아니라 처음에 만든 평문 값을 입력합니다.

실행하기

sudo docker compose up -d

외부 접속은 앞에서 이야기한 Cloudflare Tunnel에 vault.example.com을 http://localhost:12000으로 연결했습니다.

이제 브라우저에서 Vaultwarden에 접속해 첫 계정을 만듭니다.

계정을 만들고 로그인이 되는 것까지 확인했다면 가입도 바로 잠갔습니다.

SIGNUPS_ALLOWED: 'false'
sudo docker compose up -d

이제 관리자가 초대한 사람만 계정을 만들 수 있습니다.

메일도 보내보자

팀원을 초대하려면 메일이 나가는 편이 아무래도 편합니다.

저는 다른 프로젝트에서도 종종 쓰고 있던 Resend를 SMTP로 연결했습니다. 발송 도메인을 인증하고 API Key를 하나 만든 뒤 compose에 다음 설정을 추가하면 됩니다.

environment:
  SMTP_HOST: 'smtp.resend.com'
  SMTP_FROM: 'noreply@example.com'
  SMTP_FROM_NAME: 'Vaultwarden'
  SMTP_SECURITY: 'starttls'
  SMTP_PORT: '587'
  SMTP_USERNAME: 'resend'
  SMTP_PASSWORD: 're_...'

설정을 적용한 뒤 /admin에서 테스트 메일을 보내봤고, 별다른 문제 없이 잘 도착했습니다.

이제 Users에서 이메일 주소를 입력해 팀원을 초대할 수 있습니다.

같이 쓰기

여러 사람이 같은 비밀번호나 API 키를 사용해야 할 때는 Organization을 만들어 공유합니다.

저는 Collection을 프로젝트나 용도별로 나누고, 각 사람에게 필요한 Collection만 열어주는 식으로 사용하고 있습니다.

Organization
├── Infra
├── Project A
├── Project B
└── External

API 키처럼 아이디와 비밀번호 형태가 아닌 값은 Secure Note에 넣어두고 있습니다. 키만 적어두면 나중에 무엇에 쓰던 것인지 기억이 안 나는 경우가 있어서, 서비스와 용도, 권한 정도도 함께 적어두는 편입니다.

그리고 Vaultwarden에는 사람이 확인하고 공유해야 하는 secret만 넣었습니다.

실제 애플리케이션이 실행될 때 사용하는 값까지 Vaultwarden에서 가져오게 하지는 않았습니다. 그런 값들은 각 배포 환경에서 따로 관리하고 있습니다.

나중에 애플리케이션용 secret까지 한곳에서 관리해야 할 정도로 규모가 커진다면, 그때는 Infisical이나 HashiCorp Vault 같은 도구를 따로 쓰면 될 것 같습니다.

마지막으로 계정마다 2FA까지 켜두면 제가 필요했던 세팅은 끝입니다!

© 2026 levish