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 mapea8080:8080. Es casi siempre el caso cuando se despliega detrás de Caddy connetwork_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 vaultwardenpara invalidar la caché interna. - Adjuntos no se descargan: los permisos del directorio
data/attachmentsdeben permitir lectura al UID 1000 que usa el contenedor.sudo chown -R 1000:1000 datalo corrige sin perder datos. - Errores de WebSocket al abrir la bóveda web: tu reverse proxy no está pasando
UpgradeyConnection: Upgrade. En Caddy no requiere nada extra, pero en Nginx necesitasproxy_set_header Upgrade $http_upgrade;yproxy_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
- Guía Fedora 44: Novedades, Instalación y Upgrade para Sysadmins Linux
- Configura VLANs en Linux con nmcli: Guía paso a paso para sysadmins
- Protege tu SSH: Guía esencial para configurar Fail2ban en Linux
- Guía Definitiva: Configura Kubernetes con kubeadm en Ubuntu para Sysadmins Linux
- Configura Prometheus y Grafana: Guía DevOps para SysAdmins Linux

Pingback: Vaultwarden vs Passbolt vs Bitwarden: gestión de passwords en 2026 - Soy Linux