Vaultwarden es una implementación del servidor de Bitwarden escrita en Rust. Es compatible con las aplicaciones oficiales de Bitwarden (extensiones de navegador, escritorio, móvil y CLI) y consume muy pocos recursos, por lo que funciona bien en un VPS pequeño. En este tutorial desplegarás Vaultwarden con Docker Compose en Ubuntu 24.04, lo publicarás detrás de Nginx con un certificado de Let's Encrypt, protegerás el panel de administración y programarás copias de seguridad diarias.

Requisitos previos

Para seguir esta guía necesitas:

  • Un servidor con Ubuntu 24.04 LTS, por ejemplo un VPS de CubePath, con al menos 1 GB de RAM.
  • Un usuario no root con privilegios sudo.
  • Docker Engine y el plugin Docker Compose instalados desde el repositorio oficial de Docker.
  • Un subdominio (en esta guía, vault.your_domain) con un registro DNS A que apunte a la IP pública del servidor.
  • Los puertos 80 y 443 accesibles desde Internet.

Comprueba que Docker y Compose responden antes de empezar:

docker --version
docker compose version

Paso 1: Crear el directorio del proyecto

Guarda la configuración y los datos de Vaultwarden en /opt/vaultwarden. La base de datos SQLite, los adjuntos y las claves del servidor vivirán en el subdirectorio data:

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

Paso 2: Generar el token del panel de administración

El panel /admin permite gestionar usuarios y la configuración del servidor, así que debe estar protegido. Vaultwarden acepta un token en texto plano, pero lo recomendable es guardar solo su hash Argon2. Genera el hash con la herramienta incluida en la imagen; te pedirá la contraseña que usarás para entrar en el panel dos veces:

sudo docker run --rm -it vaultwarden/server:latest /vaultwarden hash
Generate an Argon2id PHC string using the 'bitwarden' preset:

Password:
Confirm Password:

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

Copia la cadena que empieza por $argon2id. Docker Compose interpreta el símbolo $ como el inicio de una variable, así que en el archivo de Compose tendrás que escribir cada $ como $$.

Paso 3: Crear el archivo de Docker Compose

Crea el archivo de Compose:

sudo nano /opt/vaultwarden/compose.yaml

Añade el siguiente contenido. Sustituye vault.your_domain por tu subdominio y el valor de ADMIN_TOKEN por tu hash con los $ duplicados:

services:
  vaultwarden:
    image: vaultwarden/server:latest
    container_name: vaultwarden
    restart: unless-stopped
    environment:
      DOMAIN: "https://vault.your_domain"
      SIGNUPS_ALLOWED: "true"
      ADMIN_TOKEN: "$$argon2id$$v=19$$m=65540,t=3,p=4$$..."
    volumes:
      - ./data:/data
    ports:
      - "127.0.0.1:8080:80"

Puntos importantes de esta configuración:

  • DOMAIN debe coincidir exactamente con la URL pública, incluido https://. Vaultwarden la usa para generar enlaces y para WebAuthn.
  • SIGNUPS_ALLOWED queda activado solo mientras creas tu cuenta; lo desactivarás en el paso 7.
  • El puerto se publica en 127.0.0.1, de modo que solo Nginx puede llegar al contenedor. Docker gestiona sus propias reglas de iptables y un puerto publicado en todas las interfaces quedaría expuesto aunque UFW lo bloquee.

Desde la versión 1.29, Vaultwarden sirve las notificaciones por WebSocket en el mismo puerto que la aplicación, así que no necesitas publicar el antiguo puerto 3012.

Paso 4: Arrancar Vaultwarden

Descarga la imagen y arranca el contenedor en segundo plano:

sudo docker compose up -d

Comprueba que el contenedor está en marcha:

sudo docker compose ps
NAME          IMAGE                       COMMAND       SERVICE       CREATED          STATUS                    PORTS
vaultwarden   vaultwarden/server:latest   "/start.sh"   vaultwarden   20 seconds ago   Up 19 seconds (healthy)   127.0.0.1:8080->80/tcp

Vaultwarden expone el endpoint /alive para comprobaciones de salud. Consúltalo desde el propio servidor:

curl -s http://127.0.0.1:8080/alive

La respuesta es una marca de tiempo entre comillas. Si no obtienes respuesta, revisa los registros con sudo docker compose logs vaultwarden.

Paso 5: Configurar Nginx como proxy inverso

Instala Nginx y el plugin de Certbot para Nginx:

sudo apt update
sudo apt install nginx certbot python3-certbot-nginx

Crea un bloque de servidor para el subdominio:

sudo nano /etc/nginx/sites-available/vaultwarden

Añade esta configuración. Las cabeceras Upgrade y Connection permiten que las notificaciones en tiempo real funcionen por WebSocket, y client_max_body_size fija el tamaño máximo de los adjuntos:

server {
    listen 80;
    listen [::]:80;
    server_name vault.your_domain;

    client_max_body_size 128M;

    location / {
        proxy_pass http://127.0.0.1:8080;
        proxy_http_version 1.1;
        proxy_set_header Upgrade $http_upgrade;
        proxy_set_header Connection "upgrade";
        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;
    }
}

Activa el sitio, comprueba la sintaxis y recarga Nginx:

sudo ln -s /etc/nginx/sites-available/vaultwarden /etc/nginx/sites-enabled/
sudo nginx -t
sudo systemctl reload nginx
nginx: the configuration file /etc/nginx/nginx.conf syntax is ok
nginx: configuration file /etc/nginx/nginx.conf test is successful

Si usas UFW, permite SSH y el tráfico web:

sudo ufw allow OpenSSH
sudo ufw allow 'Nginx Full'
sudo ufw enable

Paso 6: Obtener el certificado TLS

Certbot obtiene el certificado de Let's Encrypt, añade la configuración TLS al bloque de servidor y crea la redirección de HTTP a HTTPS:

sudo certbot --nginx -d vault.your_domain

Cuando termine, verifica que la renovación automática funciona. El paquete de Ubuntu instala un temporizador de systemd que la ejecuta dos veces al día:

sudo certbot renew --dry-run
Congratulations, all simulated renewals succeeded:
  /etc/letsencrypt/live/vault.your_domain/fullchain.pem (success)

Abre https://vault.your_domain en el navegador. Deberías ver la pantalla de inicio de sesión de la bóveda web.

Paso 7: Crear tu cuenta y cerrar los registros

Pulsa Crear cuenta en la bóveda web y registra tu usuario con una contraseña maestra robusta. Vaultwarden no puede recuperar la contraseña maestra: si la pierdes, pierdes el acceso a la bóveda.

Una vez creadas las cuentas necesarias, desactiva los registros abiertos para que nadie más pueda darse de alta. Edita el archivo de Compose:

sudo nano /opt/vaultwarden/compose.yaml

Cambia el valor de la variable:

      SIGNUPS_ALLOWED: "false"

Recrea el contenedor para aplicar el cambio:

cd /opt/vaultwarden
sudo docker compose up -d

Para comprobarlo, abre la bóveda en una ventana privada e intenta crear una cuenta: Vaultwarden responderá que el registro no está permitido.

A partir de ahora puedes dar de alta usuarios nuevos desde https://vault.your_domain/admin, en la sección Users, con Invite User. Entra al panel con la contraseña que usaste para generar el hash en el paso 2. Para que las invitaciones lleguen por correo necesitas configurar SMTP con las variables SMTP_HOST, SMTP_FROM, SMTP_PORT, SMTP_SECURITY, SMTP_USERNAME y SMTP_PASSWORD; sin SMTP, el usuario invitado puede registrarse igualmente con ese correo desde la bóveda web.

Paso 8: Programar copias de seguridad

Lo que hay que copiar está en /opt/vaultwarden/data: la base de datos db.sqlite3, las claves rsa_key*, config.json y los directorios attachments y sends. Copiar la base de datos con cp mientras Vaultwarden escribe puede dar un archivo inconsistente, así que usa el comando .backup de SQLite, que hace una copia coherente en caliente.

Instala el cliente de SQLite:

sudo apt install sqlite3

Crea el script de copia:

sudo nano /usr/local/bin/vaultwarden-backup

Añade el siguiente contenido:

#!/usr/bin/env bash
set -euo pipefail

DATA_DIR="/opt/vaultwarden/data"
BACKUP_DIR="/var/backups/vaultwarden"
STAMP="$(date +%Y%m%d-%H%M%S)"
WORK_DIR="$(mktemp -d)"
trap 'rm -rf "$WORK_DIR"' EXIT

mkdir -p "$BACKUP_DIR"
chmod 700 "$BACKUP_DIR"

# Copia coherente de la base de datos
sqlite3 "$DATA_DIR/db.sqlite3" ".backup '$WORK_DIR/db.sqlite3'"

# Claves, configuración, adjuntos y Sends (los que existan)
cd "$DATA_DIR"
for item in rsa_key.pem rsa_key.pub.pem config.json attachments sends; do
    if [ -e "$item" ]; then
        cp -a "$item" "$WORK_DIR/"
    fi
done

tar -czf "$BACKUP_DIR/vaultwarden-$STAMP.tar.gz" -C "$WORK_DIR" .

# Conserva las copias de los últimos 30 días
find "$BACKUP_DIR" -name 'vaultwarden-*.tar.gz' -mtime +30 -delete

Hazlo ejecutable y pruébalo:

sudo chmod 750 /usr/local/bin/vaultwarden-backup
sudo /usr/local/bin/vaultwarden-backup
sudo tar -tzf /var/backups/vaultwarden/vaultwarden-*.tar.gz
./
./db.sqlite3
./rsa_key.pem
./attachments/

Programa la copia todos los días a las 03:15 con un archivo en /etc/cron.d:

echo '15 3 * * * root /usr/local/bin/vaultwarden-backup' | sudo tee /etc/cron.d/vaultwarden-backup

Paso 9: Actualizar Vaultwarden

Vaultwarden publica versiones con frecuencia. Para actualizar, haz antes una copia de seguridad, descarga la imagen nueva y recrea el contenedor:

sudo /usr/local/bin/vaultwarden-backup
cd /opt/vaultwarden
sudo docker compose pull
sudo docker compose up -d
sudo docker image prune -f

Los datos en ./data se conservan entre versiones y las migraciones de base de datos se aplican solas al arrancar.

Solución de problemas

  • 502 Bad Gateway en Nginx: el contenedor no está en marcha o no escucha en 127.0.0.1:8080. Comprueba sudo docker compose ps y curl http://127.0.0.1:8080/alive.
  • El panel /admin rechaza la contraseña: el hash se guardó sin duplicar los $. Revisa ADMIN_TOKEN en compose.yaml y recrea el contenedor. Los registros muestran un aviso si el token no es un hash válido.
  • Los clientes no se sincronizan al instante: faltan las cabeceras Upgrade y Connection en Nginx, por lo que el WebSocket no se establece. Los cambios se ven igualmente al sincronizar a mano.
  • Error Registration not allowed: es lo esperado con SIGNUPS_ALLOWED en false. Invita al usuario desde el panel de administración.

Conclusión

Ya tienes Vaultwarden funcionando con HTTPS, con los registros cerrados, el panel de administración protegido por un hash Argon2 y copias de seguridad diarias. Como siguientes pasos, conecta las extensiones y aplicaciones de Bitwarden indicando https://vault.your_domain como servidor autoalojado, activa la autenticación en dos pasos en tu cuenta y configura SMTP para enviar invitaciones y avisos por correo.