← Blog

Hospede o Vaultwarden, o gerenciador de senhas compatível com Bitwarden

Railyard

9 de maio de 2026 · 9 min de leitura

Siga isso no seu próprio servidor. Comece grátis, sem cartão

Os planos hospedados do Bitwarden são bons até você preferir não manter as senhas da sua equipe no servidor de outra pessoa, ou simplesmente querer um cofre que responde no seu próprio domínio. O Vaultwarden é uma reimplementação em Rust, feita do zero, da API do servidor Bitwarden, compatível com as extensões de navegador, apps mobile e CLI reais do Bitwarden, e leve o suficiente para rodar com conforto no servidor mais barato que um provedor de nuvem vende. No final deste guia, você vai ter ele rodando atrás de HTTPS com a sincronização ao vivo funcionando, um token de admin que não está em texto puro em lugar nenhum, cadastros travados só para a sua equipe, e um backup do qual você de fato consegue restaurar.

O que você vai precisar

  • Um servidor com Docker e o plugin Docker Compose. Um Hetzner CX23 (2 vCPUs, 4 GB de RAM, 40 GB NVMe, €5,99/mês sem VAT) é mais que suficiente para o cofre de uma casa ou equipe pequena.
  • Um domínio ou subdomínio que você controla, apontado para o IP do servidor (um registro A para algo como vault.yourdomain.com).
  • Qualquer cliente compatível com Bitwarden: a extensão de navegador oficial, o app desktop, o app mobile ou a CLI, todos funcionam sem modificação contra o Vaultwarden.
  • Acesso SSH ao servidor.

Etapa 1: Gere um token de admin hasheado, não um simples

A página /admin do Vaultwarden, onde você gerencia usuários, organizações e configurações do servidor, é protegida por um único valor ADMIN_TOKEN. Uma string simples ainda funciona, mas a própria documentação do projeto aponta isso como inseguro, já que qualquer um que consiga ler suas variáveis de ambiente ou arquivo compose recebe o token literalmente. A recomendação atual é guardá-lo como um hash Argon2id em vez disso, usando o próprio comando de hashing embutido do Vaultwarden:

docker run --rm -it vaultwarden/server:1.37.4 /vaultwarden hash

Isso pede uma senha e imprime um hash em formato PHC que se parece com $argon2id$v=19$m=19456,t=2,p=1$<salt>$<hash> (a string acima é ilustrativa, a sua vai ter um salt e hash diferentes). Essa string inteira é o que você define como ADMIN_TOKEN; o Vaultwarden hasheia as tentativas que chegam e compara, então o token real fica na sua cabeça, não num arquivo de configuração que qualquer um pode grepar.

Coloque num arquivo .env ao lado do seu docker-compose.yml, sem fazer commit no controle de versão:

ADMIN_TOKEN='$argon2id$v=19$m=19456,t=2,p=1$c29tZXNhbHR2YWx1ZQ$c29tZWhhc2h2YWx1ZQ'

Um arquivo .env não precisa de nenhum escape para os caracteres $. Se em vez disso você colar o hash direto no bloco environment: do docker-compose.yml, cada $ precisa ser duplicado para $$ ou o Compose tenta interpretar como substituição de variável, que é mais um motivo pelo qual o arquivo .env é o caminho mais fácil.

Etapa 2: Escreva o arquivo Docker Compose

services:
  vaultwarden:
    image: vaultwarden/server:1.37.4
    restart: unless-stopped
    environment:
      DOMAIN: "https://vault.yourdomain.com"
      SIGNUPS_ALLOWED: "false"
      ADMIN_TOKEN: "${ADMIN_TOKEN}"
    volumes:
      - ./vaultwarden-data:/data
    expose:
      - "80"

  caddy:
    image: caddy:2
    restart: unless-stopped
    ports:
      - "80:80"
      - "443:443"
    volumes:
      - ./Caddyfile:/etc/caddy/Caddyfile
      - caddy-data:/data
      - caddy-config:/config
    depends_on:
      - vaultwarden

volumes:
  caddy-data:
  caddy-config:

1.37.4 é a tag de release estável atual no Docker Hub no momento em que isso foi escrito; fixe uma versão real em vez de latest para que um upgrade seja algo que você roda de propósito. DOMAIN precisa ser a URL HTTPS exata pela qual os clientes vão acessar o servidor, o Vaultwarden usa isso para construir links em e-mails e para validar origens de WebAuthn e passkey, então uma divergência aqui é um bug funcional real, não só cosmético. SIGNUPS_ALLOWED começa como true por padrão numa instalação nova do Vaultwarden, o que significa que qualquer um que encontre a URL pode criar uma conta; defina como "false" depois que suas próprias contas existirem, que é a Etapa 5 abaixo. O diretório de dados é um bind mount simples em vez de um volume nomeado especificamente para que um backup seja só copiar uma pasta, veja a Etapa 6.

Etapa 3: Aponte o domínio para o servidor e inicie a stack

Crie um registro A para o seu subdomínio apontando para o IP público do servidor, depois suba os containers:

docker compose up -d

O Caddy solicita um certificado do Let’s Encrypt automaticamente na primeira vez que vê tráfego para o domínio no seu Caddyfile (Etapa 4), o que exige que o registro de DNS já esteja resolvendo e as portas 80 e 443 estejam acessíveis pela internet, não só pela sua própria máquina.

Etapa 4: Adicione o proxy reverso Caddy

vault.yourdomain.com {
  reverse_proxy vaultwarden:80
}

Essa única linha de reverse_proxy também é toda a configuração de WebSocket. O Vaultwarden costumava precisar de uma porta separada (3012) e uma flag WEBSOCKET_ENABLED para as notificações de sincronização ao vivo que enviam um item novo ou alterado instantaneamente para seus outros clientes abertos; desde a versão 1.29.0, essas notificações rodam na mesma porta que o resto do app, e a diretiva reverse_proxy do Caddy já repassa os cabeçalhos Upgrade e Connection que um handshake de WebSocket precisa, sem nenhuma configuração extra. Se você estiver adaptando isso para o nginx em vez disso, esse é o único par de cabeçalho que você teria que adicionar à mão e que o Caddy te dá de graça.

Etapa 5: Crie sua conta, depois tranque a porta

Acesse https://vault.yourdomain.com, registre a primeira conta pelo formulário de cadastro normal, e confirme que consegue fazer login e adicionar um item de teste. Depois que todas as contas que a sua equipe precisa existirem, desligue os cadastros:

environment:
  SIGNUPS_ALLOWED: "false"

Rode docker compose up -d de novo para aplicar. A partir daqui, contas novas são criadas pela página /admin usando seu token hasheado em Argon2 da Etapa 1, com convites enviados por e-mail se você configurou SMTP, ou compartilhando um link de convite manualmente se não configurou.

Etapa 6: Faça backup do diretório de dados

Tudo que importa, dados do cofre criptografados, anexos e configuração do servidor, vive sob ./vaultwarden-data. Um tarball diário para algum lugar fora do servidor já basta para a maioria das configurações:

tar czf vaultwarden-backup-$(date +%F).tar.gz ./vaultwarden-data

Mantenha alguns backups recentes e copie pelo menos um deles para algum lugar diferente do próprio servidor, um segundo servidor, object storage, qualquer lugar que sobreviva ao disco original falhando. Restaurar é a mesma operação ao contrário: pare a stack, extraia o tarball de volta sobre ./vaultwarden-data, inicie a stack de novo.

Etapa 7: Faça upgrade sem perder dados

docker compose pull vaultwarden
docker compose up -d vaultwarden

O Vaultwarden roda qualquer migração de schema que precisar automaticamente na inicialização contra o SQLite (ou o banco externo configurado) em /data, então um upgrade normal é só baixar a nova imagem e recriar o único container. Leia as release notes da versão para a qual você está saltando antes de um salto de várias versões, já que o projeto ocasionalmente indica uma mudança que quebra compatibilidade para quem faz self-host numa release específica.

O caminho curto: faça com o Railyard

O Railyard oferece o Vaultwarden como um template de aplicação de um clique (vaultwarden no catálogo de templates), construído a partir da imagem oficial vaultwarden/server com um volume /data persistente já ligado e ADMIN_TOKEN definido como o único segredo que você mesmo preenche. Faça o deploy direto: app.railyard.run/deploy?template=vaultwarden.

Duas diferenças do caminho manual que vale saber: o template hoje acompanha a tag flutuante latest em vez de uma versão fixa, então onde este guia faz você escolher e fazer upgrade de uma versão específica por conta própria, o Railyard se move com o upstream automaticamente. E os cadastros também vêm abertos por padrão aqui, a mesma etapa SIGNUPS_ALLOWED=false da Etapa 5 ainda se aplica depois que as contas da sua equipe existirem, só que é uma variável de ambiente na página Settings do app em vez de uma linha no docker-compose.yml. Veja Deploy with a button para entender como templates de um clique funcionam em geral.

Solução de problemas

O HTTPS nunca é emitido, o Caddy só continua tentando de novo. Quase sempre é um problema de DNS: confirme que vault.yourdomain.com já resolve para o IP do servidor de fora da sua própria rede (não só do próprio servidor), e que nada upstream está bloqueando a entrada na 80 ou 443. Os próprios logs do Caddy (docker compose logs caddy) imprimem o erro exato de ACME se o Let’s Encrypt rejeitou a requisição.

A sincronização ao vivo não atualiza outros dispositivos, mas o cofre em si funciona bem. Confira se nada entre o cliente e o Caddy está removendo os cabeçalhos de upgrade de WebSocket, uma CDN ou uma camada extra de proxy na frente do Caddy é o culpado usual, já que o próprio Caddy trata isso corretamente logo de início, como coberto na Etapa 4.

docker compose up falha com uma porta já em uso. Alguma outra coisa no servidor já está vinculada à 80 ou 443, geralmente um proxy reverso anterior ou um servidor web existente. sudo ss -tlnp | grep ':80\|:443' mostra o que está ocupando a porta.

A página de admin rejeita um token que você tem certeza que está correto. Confirme que você está colando a string de hash PHC completa, incluindo o prefixo $argon2id$, como ADMIN_TOKEN, não só a senha que você hasheou. Se você editou o valor direto dentro do docker-compose.yml em vez de um arquivo .env, confira se cada $ está duplicado para $$, um único $ sem escape é engolido pela própria substituição de variável do Compose antes de o Vaultwarden sequer vê-lo.

Escritas em /data falham com um erro de permissão. Isso aparece mais frequentemente depois de restaurar um backup como root quando o container roda como um usuário diferente; faça chown -R no diretório para corresponder ao UID que os logs do container mostram que ele está usando, depois reinicie.

Perguntas frequentes

O Vaultwarden é de fato compatível com os apps reais do Bitwarden? Sim, ele implementa a mesma API que os apps oficiais e as extensões de navegador falam, então você aponta eles para a URL do seu próprio servidor em vez de bitwarden.com e nada mais muda do lado do cliente.

Posso usar um banco de dados diferente do arquivo SQLite padrão? O Vaultwarden também suporta MySQL e PostgreSQL, através de uma variável de ambiente DATABASE_URL, o que vale a pena quando mais de um punhado de pessoas depende do cofre e você quer que os backups sejam tratados pelas suas ferramentas de banco de dados existentes em vez de um tarball.

Eu preciso de SMTP configurado para convidar pessoas? Não, você pode compartilhar um link de convite manualmente pela página /admin sem nunca configurar e-mail de saída, embora convites por e-mail de verdade sejam mais amigáveis para quem não está confortável em colar um link bruto num navegador.

O que acontece se eu perder o servidor sem backup? Todo item do cofre se perde, não há caminho de redefinição de senha de volta para dados que nunca foram salvos em outro lugar, que é todo o motivo da Etapa 6 não ser opcional na prática, mesmo que o Docker rode sem problema sem ela.

Isso é seguro para uma empresa inteira, ou só uso pessoal? Organizações, coleções compartilhadas e permissões de grupo são todos suportados, da mesma forma que o Bitwarden hospedado; a principal coisa que escala com menos elegância à mão é tudo na Etapa 6 e na Etapa 7, que é exatamente a parte que uma opção gerenciada tira das suas mãos.

Depois que estiver rodando, um cofre self-hosted basicamente some para o segundo plano, o trabalho contínuo real é o hábito de backup e upgrade da Etapa 6 e da Etapa 7, não a configuração inicial.

Por que escolhem o Railyard para isso

  • Sua própria conta de cloud. Faça deploy no DigitalOcean, Hetzner, Vultr, AWS, OVHcloud, ou em qualquer servidor Linux via SSH, cobrado direto pelo seu provedor, sem markup.
  • Um preço fixo por equipe. O Free é grátis para sempre: um servidor mais um servidor de banco de dados, três apps, sem cartão. O Pro custa US$19 por mês para até cinco servidores e apps ilimitados. O Team custa US$49 por mês com servidores ilimitados. Sem cobrança por app, por deploy, por assento ou por gigabyte.
  • Templates de um clique. Faça deploy de apps open source, como bancos de dados, analytics e ferramentas de dev, no seu próprio servidor em um clique.
  • Bancos de dados gerenciados. Postgres, MySQL e Redis com backups programados, verificados por restauração, e recuperação point-in-time.
  • Push-to-deploy. Faça deploy a partir do GitHub, GitLab ou da CLI, com HTTPS automático e releases sem downtime.

Suba o Vaultwarden com um clique

O template de Vaultwarden do Railyard configura a imagem e o volume de dados para você, então você só preenche o token de admin.

Plano grátis, sem cartão, e 14 dias de Pro para cada equipe nova. Um preço fixo por equipe.