Servidores

Vaultwarden: tu propio Bitwarden self-hosted en 2026

TL;DR — Lo que vas a tener al final

  • Tiempo de setup: 30 minutos a 2 horas dependiendo de la complejidad del entorno.
  • Requisitos: Acceso root, Linux moderno, conexión a internet para descargar dependencias.
  • Resultado: Servicio funcional y estable, documentado paso a paso.
  • Troubleshooting: Logs estándar en /var/log/, comandos de verificación al final de cada sección.

¿Por qué autoalojar tu gestor de contraseñas en 2026?

El modelo SaaS de Bitwarden cuesta 10 USD por usuario al año en el plan Families. Para una pareja son 20 USD, y para un equipo pequeño con cinco cuentas la cifra sube a 60 USD anuales solo para que tu bóveda viva en un centro de datos de Virginia. Vaultwarden — fork comunitario escrito en Rust por dani-garcia, con más de 66.000 estrellas en GitHub y releases quincenales — corre el mismo API oficial de Bitwarden sobre tu propio hardware con 256 MB de RAM. La versión 1.37.2 (22 de agosto de 2026) es la que vas a desplegar en esta guía.

Hay tres diferencias operativas que importan al sysadmin:

  • Coste recurrente cercano a cero: un VPS de 4 USD al mes en Hetzner o un NAS Synology en casa cubre el servicio para toda tu familia.
  • Latencia local: el desbloqueo del móvil tarda entre 80 y 200 ms contra tu LAN, frente a los 600 ms típicos de un servidor remoto en cada autenticación.
  • Control de datos: la bóveda cifrada vive en tu disco, no en infraestructura compartida. El precio es que tú eres responsable del backup y de aplicar parches de seguridad.

El caso típico de uso es un servidor casero o un VPS pequeño con dominio propio. No es la opción correcta si necesitas alta disponibilidad geográfica ni SSO empresarial; para eso Bitwarden cloud sigue siendo la mejor elección.

Requisitos antes de empezar

Vaultwarden es una aplicación Rust compilada a un binario único, pero la recomendación oficial y de la comunidad es ejecutarla en contenedor porque la imagen ya incluye el web vault (la SPA HTML que cargan los navegadores).

Recurso Mínimo Recomendado para uso familiar
CPU 1 vCPU ARM o x86_64 2 vCPU
RAM 256 MB 512 MB
Disco 2 GB para datos 10 GB SSD
Sistema operativo Linux con kernel 5.10+ Debian 12, Ubuntu 22.04 LTS, Fedora 41
Docker 20.10.x 27.x con compose v2
Dominio uno propio apuntando a la IP pública subdominio dedicado (vault.dominio.com)
Puertos 80 y 443 accesibles desde Internet los mismos, con Caddy como reverse proxy

Si vas a exponer el servicio desde casa, abre los puertos 80 y 443 en tu router hacia la IP local del servidor (por ejemplo, 192.168.1.50) y configura DNS dinámico en el subdominio si tu IP pública es dinámica. Para IPv6, basta con que el router anuncie la delegación y abras el firewall al host de Docker.

Comprueba que tu servidor resuelve el nombre externo antes de continuar:

getent hosts vault.dominio.com
curl -sI https://vault.dominio.com | head -1

Si el primer comando devuelve la IP de tu servidor y el segundo responde con HTTP/2, todo lo que viene después va a funcionar tal cual.

Despliegue con Docker Compose

Crea un directorio dedicado y dentro el archivo compose.yaml con la definición del servicio. La imagen vaultwarden/server:latest apunta a 1.37.2 a fecha de esta guía; si quieres fijar versión, usa el tag 1.37.2 explícito.

mkdir -p ~/vaultwarden/data && cd ~/vaultwarden
cat > compose.yaml <<'YAML'
services:
  vaultwarden:
    image: vaultwarden/server:1.37.2
    container_name: vaultwarden
    restart: unless-stopped
    environment:
      DOMAIN: "https://vault.dominio.com"
      SIGNUPS_ALLOWED: "true"
      INVITATIONS_ALLOWED: "true"
      SHOW_PASSWORD_HINT: "false"
      LOG_LEVEL: "warn"
      ROCKET_PORT: "80"
    volumes:
      - ./data:/data
    ports:
      - "127.0.0.1:8080:80"
YAML
docker compose up -d
docker compose logs --tail=20 vaultwarden

La variable SIGNUPS_ALLOWED queda en true solo durante la creación de tu primera cuenta. Inmediatamente después ciérrala: en la sección de hardening explicamos cómo. ROCKET_PORT deja el contenedor escuchando en 80 internamente; mapeamos a 127.0.0.1:8080 para que solo Caddy pueda alcanzarlo, nunca Internet directo.

El log de inicio debería terminar con una línea similar a:

Rocket has launched from http://0.0.0.0:80

Si en su lugar ves Failed to bind to 0.0.0.0:80, casi siempre hay otro contenedor ocupando el puerto. Ejecútalo en el host: sudo ss -tlnp | grep :80.

Reverse proxy con Caddy y HTTPS automático

Caddy es la opción más limpia para Vaultwarden en 2026 porque gestiona los certificados de Let’s Encrypt sin pasos extra. Su Caddyfile con un solo bloque basta:

sudo apt install -y caddy
sudo tee /etc/caddy/Caddyfile > /dev/null <<'CFG'
vault.dominio.com {
    encode gzip zstd
    reverse_proxy 127.0.0.1:8080
}
CFG
sudo systemctl reload caddy

En menos de treinta segundos Caddy solicita el certificado a Let’s Encrypt y lo renueva solo mientras el dominio siga apuntando a tu IP. Compruébalo con:

curl -vI https://vault.dominio.com 2>&1 | grep -E "subject:|expire date:|issuer:"
echo | openssl s_client -connect vault.dominio.com:443 -servername vault.dominio.com 2>/dev/null 
  | openssl x509 -noout -dates

La salida debe mostrar issuer=Let’s Encrypt y una fecha de expiración dentro de los siguientes 90 días. Si Caddy se queja de connection refused al recargar, lo más probable es que el firewall esté bloqueando 80 y 443; abre ambos con sudo ufw allow 80,443/tcp o el equivalente de tu distribución.

Para Nginx la traducción equivalente es trivial, pero vas a escribir a mano la renovación de certificados con certbot --nginx y un timer de systemd. Caddy lo evita y por eso es la recomendación para esta guía.

Hardening: cerrar registros, activar 2FA, proteger el panel admin

Una vez que tu primera cuenta existe y has verificado que el login funciona, toca reducir la superficie de ataque. La imagen trae decenas de variables de entorno para gobernarlo; las mínimas que debes cambiar son:

docker compose down
sed -i 's/SIGNUPS_ALLOWED: "true"/SIGNUPS_ALLOWED: "false"/' compose.yaml
sed -i 's/INVITATIONS_ALLOWED: "true"/INVITATIONS_ALLOWED: "false"/' compose.yaml
sed -i 's/SHOW_PASSWORD_HINT: "false"/SHOW_PASSWORD_HINT: "false"/' compose.yaml
cat >> compose.yaml <<'ENV'
      SIGNUPS_ALLOWED: "false"
      INVITATIONS_ALLOWED: "false"
      SHOW_PASSWORD_HINT: "false"
      LOG_LEVEL: "warn"
      LOGIN_RATELIMIT: "10"
      SIGNIN_ATTEMPTS: "5"
      SIGNIN_ATTEMPT_WINDOW: "60"
      ACCESS_TOKEN_TTL: "3600"
      REFRESH_TOKEN_TTL: "2592000"
      PASSWORD_ITERATIONS: "600000"
ENV
docker compose up -d

El ADMIN_TOKEN merece su propio archivo. Genera uno aleatorio y guárdalo fuera del compose:

ADMIN=$(openssl rand -base64 48 | tr -d '/+=' | head -c 64)
echo "ADMIN_TOKEN=$ADMIN" > ~/vaultwarden/.env
echo "    env_file: ./.env" >> compose.yaml
docker compose up -d

Con ese token puedes entrar a https://vault.dominio.com/admin y desde ahí invalidar sesiones, expulsar usuarios o consultar el diagnóstico. No abras ese path al público: añade un bloque extra en Caddy que restrinja por IP de origen.

Para cada cuenta de usuario, activa autenticación de dos factores TOTP desde la bóveda web: Ajustes → Seguridad → Autenticación de dos pasos → Authenticator app. Guarda el código de recuperación impreso en papel, no en una nota digital.

Si quieres registrar un CVE-2026-26012 que ya quedó parcheado: la vulnerabilidad afectaba a builds anteriores a 1.35.3 y permitía bypasear autenticación bajo configuraciones concretas de SMTP. Mantenerte en 1.37.2 te deja fuera de alcance.

Backups cifrados y automatización

El directorio ./data contiene toda la base SQLite con las cuentas, las carpetas y los metadatos de los adjuntos. Perderlo significa perder la bóveda, así que el backup es la pieza más crítica del despliegue. La estrategia más simple es copiar el directorio a un destino cifrado con age y rotar con cron.

sudo apt install -y age
mkdir -p ~/.age && age-keygen -o ~/.age/key.txt 2>~/.age/pub.txt
AGE_PUB=$(grep "public key:" ~/.age/pub.txt | awk '{print $3}')
echo "AGE recipient: $AGE_PUB"
cat > ~/vaultwarden/backup.sh <<'BASH'
#!/usr/bin/env bash
set -euo pipefail
cd "$(dirname "$0")"
TS=$(date -u +%Y%m%dT%H%M%SZ)
tar -C data -czf - . 
  | age -r "$(cat ~/.age/pub.txt | grep 'public key:' | awk '{print $3}')" 
  > "/tmp/vaultwarden-${TS}.age"
rclone copy "/tmp/vaultwarden-${TS}.age" remote:backups/vaultwarden/
rm -f "/tmp/vaultwarden-${TS}.age"
find ~/vaultwarden/data -name "db.sqlite*" -exec sqlite3 {} "PRAGMA wal_checkpoint(TRUNCATE);" ;
BASH
chmod +x ~/vaultwarden/backup.sh

Programa la ejecución diaria con un timer de systemd, no con cron clásico, porque systemd te da logs persistentes y captura mejor los errores:

cat > ~/.config/systemd/user/vaultwarden-backup.service <<'SVC'
[Unit]
Description=Backup cifrado de Vaultwarden
[Service]
Type=oneshot
ExecStart=/home/tu-usuario/vaultwarden/backup.sh
SVC
cat > ~/.config/systemd/user/vaultwarden-backup.timer <<'TMR'
[Unit]
Description=Backup diario de Vaultwarden a las 03:30
[Timer]
OnCalendar=*-*-* 03:30:00
Persistent=true
[Install]
WantedBy=timers.target
TMR
systemctl --user daemon-reload
systemctl --user enable --now vaultwarden-backup.timer
systemctl --user list-timers vaultwarden-backup.timer

Verifica el primer ciclo con systemctl --user status vaultwarden-backup.service tras las 03:30. Si el destino remote:backups/vaultwarden/ falla, revisa la configuración de rclone config; rclone lsd remote: lista lo que ve tu cuenta.

Actualizaciones y monitorización

Vaultwarden publica releases quincenales. La forma más cómoda de seguir el ritmo es fijar la versión en el compose y actualizar de forma manual tras leer las notas, porque cada release que cambia el formato de la base de datos exige una migración:

cd ~/vaultwarden
git init -q . 2>/dev/null || true
git add -A && git -c user.email=bak@local -c user.name=bak commit -q -m "pre-update $(date -I)"
docker compose pull vaultwarden
docker compose up -d
docker compose logs --tail=30 vaultwarden | grep -iE "migration|error"

Si la salida del log no contiene líneas error ni migration failed, el salto fue limpio. Para automatizar la detección de versiones nuevas sin actualizar a ciegas, añade este cron a tu usuario:

cat > ~/vaultwarden/check_update.sh <<'BASH'
#!/usr/bin/env bash
set -euo pipefail
CURRENT=$(docker inspect --format '{{ index .Config.Labels "org.opencontainers.image.version" }}' vaultwarden 2>/dev/null || 
          docker inspect --format '{{ index .Config.Env }}' vaultwarden | grep -oP 'VAULTWARDEN_VERSION=K[^s]*' || 
          echo unknown)
LATEST=$(curl -fsSL https://api.github.com/repos/dani-garcia/vaultwarden/releases/latest 
         | grep tag_name | head -1 | cut -d'"' -f4)
if [ "$CURRENT" != "$LATEST" ] && [ -n "$LATEST" ]; then
  echo "Update disponible: $CURRENT -> $LATEST" | mail -s "Vaultwarden update" root
fi
BASH
chmod +x ~/vaultwarden/check_update.sh
( crontab -l 2>/dev/null; echo "0 9 * * 1 /home/tu-usuario/vaultwarden/check_update.sh" ) | crontab -

Para saber si el contenedor se ha caído, un watchdog mínimo con un timer de systemd basta:

cat > ~/.config/systemd/user/vaultwarden-watchdog.service <<'SVC'
[Unit]
Description=Reinicia Vaultwarden si el healthcheck falla
[Service]
Type=oneshot
ExecStart=/usr/bin/docker compose -f /home/tu-usuario/vaultwarden/compose.yaml restart vaultwarden
SVC
cat > ~/.config/systemd/user/vaultwarden-watchdog.timer <<'TMR'
[Unit]
Description=Watchdog cada 5 minutos
[Timer]
OnCalendar=*:0/5
[Install]
WantedBy=timers.target
TMR
systemctl --user enable --now vaultwarden-watchdog.timer

Troubleshooting frecuente

Tres problemas aparecen semana tras semana en los hilos de Reddit y Discourse:

  • “Cannot start Rocket”: el puerto 80 dentro del contenedor está ocupado por otro proceso. Añade ROCKET_PORT: "8080" y mapea 8080:8080. Es casi siempre el caso cuando se despliega detrás de Caddy con network_mode: host.
  • Login falla con 401 después de cambiar de dominio: la cookie de sesión emitida por el dominio viejo no es válida para el nuevo. Limpia cookies del navegador y entra desde la URL nueva. Si la API rechaza el token, también ayuda docker compose restart vaultwarden para invalidar la caché interna.
  • Adjuntos no se descargan: los permisos del directorio data/attachments deben permitir lectura al UID 1000 que usa el contenedor. sudo chown -R 1000:1000 data lo corrige sin perder datos.
  • Errores de WebSocket al abrir la bóveda web: tu reverse proxy no está pasando Upgrade y Connection: Upgrade. En Caddy no requiere nada extra, pero en Nginx necesitas proxy_set_header Upgrade $http_upgrade; y proxy_set_header Connection "upgrade"; en el location del bloque.

Si la base queda corrupta tras un corte de energía, SQLite puede leer en modo WAL inconsistente. Recompón con:

docker compose stop vaultwarden
sqlite3 ~/vaultwarden/data/db.sqlite "PRAGMA integrity_check;"
sqlite3 ~/vaultwarden/data/db.sqlite ".recover" | sqlite3 ~/vaultwarden/data/db-recovered.sqlite
mv ~/vaultwarden/data/db-recovered.sqlite ~/vaultwarden/data/db.sqlite
docker compose start vaultwarden

Si PRAGMA integrity_check devuelve ok, no hace falta tocar nada más. Si devuelve errores, restaura desde el backup .age más reciente.

¿Y ahora qué?

Con Vaultwarden funcionando ya tienes el núcleo de tu propio ecosistema de identidad: contraseñas, tarjetas, notas seguras y 2FA TOTP. La siguiente pieza natural es un servidor Headscale para llegar a tus dispositivos sin abrir más puertos, y un Nextcloud si quieres sincronizar archivos cifrados extremo a extremo junto a la bóveda. Cualquier ampliación nueva debería pasar primero por un backup verificado del directorio data: el día que algo vaya mal, ese backup es la diferencia entre un susto y un desastre.

Preguntas frecuentes

¿Por qué Vaultwarden: tu propio Bitwarden self-hosted en 2026 es relevante para sysadmins Linux?

Forma parte del flujo diario de administración. Los conceptos explicados se aplican directamente a servidores en producción y resuelven problemas reales que enfrentan sysadmins hispanohablantes.

¿Qué necesito para empezar con Vaultwarden: tu propio Bitwarden self-hosted?

Distribución Linux actualizada (Debian, Ubuntu, RHEL, Fedora o Arch), acceso root, y conexión a internet. Para producción: entorno de staging separado antes de aplicar cambios.

¿Cuáles son los errores más comunes con Vaultwarden: tu propio Bitwarden self-hosted?

Los tres más frecuentes: (1) saltar la lectura de logs, (2) aplicar cambios directos en producción sin probar, (3) olvidar permisos y ownership. La documentación y un plan de rollback siempre.

¿Cuándo usar Vaultwarden: tu propio Bitwarden self-hosted y cuándo una alternativa?

Esta solución es ideal para el caso descrito. Alternativas como Docker, Ansible o configuración manual pueden ser mejores según contexto: portabilidad, escala y reproducibilidad son los factores clave.

¿Cómo migro desde una configuración previa a Vaultwarden: tu propio Bitwarden self-hosted?

Documentar el estado actual, probar la nueva configuración en staging, validar funcionalmente, y aplicar en producción con rollback plan listo. Los posts de SoyLinux incluyen troubleshooting para los problemas más comunes.

Lee también

Alfonso Moreno | Sysadmin Senior

Soy Alfonso, sysadmin senior con RHCE y más de 10 años administrando sistemas Linux para empresas y proyectos personales. Me especializo en infraestructura, redes, virtualización, monitoring, automation y self-hosting. Sobre SoyLinux.com SoyLinux.com es un blog técnico en español sobre Linux, software libre y self-hosting. Lo mantengo en mi tiempo libre. Áreas de expertise - Linux server administration: RHEL, CentOS, Rocky, Alma, Fedora, Debian, Ubuntu - Network services: Postfix, BIND, Samba, NFS, Squid - Virtualización y containers: KVM, Proxmox, Docker, Podman - Monitoring: Prometheus, Grafana, Zabbix - Automation: Ansible, Bash scripting - Self-hosting: Vaultwarden, Nextcloud, Jellyfin, Immich, Homelab Contacto - Email: [email protected]

One thought on “Vaultwarden: tu propio Bitwarden self-hosted en 2026”

Deja una respuesta

Tu dirección de correo electrónico no será publicada. Los campos obligatorios están marcados con *