SSD Nodes Learn Hosting plans →
Guías Matt ConnorPor Matt Connor · Actualizado 2026-08-22

Gestores de secretos autoalojados para un VPS, comparados

Compara OpenBao, Infisical, SOPS con age, credenciales de systemd y un archivo env protegido para elegir secretos en un solo VPS y conocer su coste.

Qué hace un gestor de secretos autoalojado que no hace un gestor de contraseñas

Un gestor de secretos autoalojado entrega credenciales a los procesos. Un gestor de contraseñas entrega credenciales a las personas. Todo lo demás se deriva de esa diferencia. Una persona presente y atenta desbloquea un gestor de contraseñas. Un gestor de secretos tiene que entregar la contraseña de una base de datos a una aplicación a las 03:00, cuando nadie está despierto.

Los modos de fallo son distintos, y eso importa. Un gestor de contraseñas bloqueado es una molestia: se vuelve a introducir la contraseña maestra. Un gestor de secretos sellado provoca una interrupción: todos los servicios que se reinicien mientras está sellado arrancan sin credenciales y permanecen detenidos. Ejecutar Vaultwarden como gestor de contraseñas propio resuelve bien el problema de las personas. No resuelve el problema de las máquinas, y nunca se diseñó para hacerlo. Si también ejecuta uno junto con esto, los elementos que conviene reforzar son su token de administración y su archivo de copia de seguridad, no el contenido de la bóveda, que el cliente ya cifra. Una revisión de seguridad de Vaultwarden cubre ambos aspectos.

Las opciones realistas para un solo servidor se dividen en dos grupos. OpenBao e Infisical son servicios: una API, una base de datos, TLS (seguridad de la capa de transporte), un paso de inicio de sesión y un proceso que ahora debe mantener activo. SOPS con age, las credenciales de systemd y los secretos de Docker son archivos: se cifran en reposo, algo que ya está en ejecución los descifra y no hay nada adicional que monitorizar.

Esta es la respuesta directa. Para un solo equipo con una o dos personas, las opciones basadas en archivos suelen ser las adecuadas. Un OpenBao que nadie desella correctamente y cuyas credenciales nadie rota es peor que un archivo de entorno con permisos mode 600, porque añade un componente más y una copia de seguridad que probablemente se configurará mal, sin proporcionar ninguna rotación que no se estuviera haciendo manualmente.

¿Es suficiente un archivo de entorno con permisos 600?

A menudo, sí. La amenaza que evita es que otro usuario del servidor lea la contraseña de la base de datos. Los permisos de archivos de Unix lo impiden, incluso antes de que la red esté activa.

sudo install -d -m 750 -o root -g myapp /etc/myapp
sudo install -m 640 -o root -g myapp /dev/null /etc/myapp/env
sudoedit /etc/myapp/env

Compruébelo desde ambos lados:

sudo -u myapp cat /etc/myapp/env
sudo -u nobody cat /etc/myapp/env

El primer comando muestra el archivo. El segundo muestra cat: /etc/myapp/env: Permission denied, porque nobody no pertenece al grupo myapp y el archivo no tiene permisos para otros usuarios. Ese es todo el modelo de seguridad, y es un modelo real.

La filtración ocurre después. Una unidad de systemd con EnvironmentFile= copia esos valores en el entorno del proceso, y el entorno del proceso se puede leer.

[Service]
User=myapp
EnvironmentFile=/etc/myapp/env
ExecStart=/usr/local/bin/myapp
sudo cat /proc/$(pgrep -n myapp)/environ | tr '\0' '\n'

Ese comando muestra sus secretos en texto claro, porque /proc/<pid>/environ puede ser leído por root y por el usuario con el que se ejecuta el proceso. Un generador de informes de errores que adjunte el entorno a un informe verá lo mismo. También lo verá cualquier herramienta que se ejecute con la misma cuenta. Por eso mantener los secretos fuera de los agentes de IA empieza por sacarlos del entorno. Combine el archivo con un usuario de servicio dedicado con pocos privilegios para que «el usuario con el que se ejecuta el proceso» no sea root.

SOPS con age: secretos cifrados que puedes confirmar en git

SOPS (secrets operations) cifra los valores de un archivo YAML o JSON y deja las claves en texto claro. age es una herramienta de cifrado pequeña que proporciona un único par de claves y ningún servidor de claves. Juntos permiten confirmar secrets.enc.yaml junto al código, y git diff sigue mostrando qué configuración cambió sin revelar a quien lo lea en qué se convirtió.

age está incluido en Ubuntu 24.04. SOPS no lo está, así que descarga el .deb desde la página de versiones. La versión 3.13.3 era la actual en agosto de 2026.

sudo apt update && sudo apt install -y age
curl -LO https://github.com/getsops/sops/releases/download/v3.13.3/sops_3.13.3_amd64.deb
sudo apt install -y ./sops_3.13.3_amd64.deb
sops --version

Genera un par de claves. age-keygen escribe la clave privada en el archivo e imprime la clave pública, por lo que verás una línea que comienza por Public key: age1....

mkdir -p ~/.config/sops/age
age-keygen -o ~/.config/sops/age/keys.txt
chmod 600 ~/.config/sops/age/keys.txt
age-keygen -y ~/.config/sops/age/keys.txt

Coloca la clave pública en .sops.yaml en la raíz del repositorio, para no tener que recordar nunca el destinatario en la línea de comandos.

creation_rules:
  - age: age1s3cqcks5genc6ru8chl0hkkd04zmxvczsvdxq99ekffe4gmvjpzsedk23c
sops encrypt secrets.yaml > secrets.enc.yaml
sops decrypt secrets.enc.yaml

Una regla sin path_regex coincide con todo, que es lo que necesitas al principio. Si más adelante añades uno, escríbelo para que coincida con el archivo que pasas a sops, porque las reglas se comprueban contra la ruta de entrada y no contra el archivo al que rediriges la salida.

En tiempo de ejecución, entrega los valores a un solo proceso y a ningún otro:

sops exec-env secrets.enc.yaml './myapp'

sops exec-env descifra en memoria y establece los valores en el entorno del proceso hijo, por lo que no se escribe texto plano en el disco. La salvedad sobre el entorno de la sección anterior también se aplica a ese proceso hijo.

Aquí hay dos problemas habituales. El error Failed to get the data key required to decrypt the SOPS file con systemd casi siempre significa que SOPS buscó en el directorio de inicio equivocado, porque una unidad no hereda tu HOME. Establece la ruta explícitamente con Environment=SOPS_AGE_KEY_FILE=/etc/sops/age.txt en la unidad. Por separado, editar .sops.yaml no vuelve a cifrar nada de lo que ya existe: añadir la clave pública de un colega sólo afecta a los archivos nuevos, así que ejecuta sops updatekeys secrets.enc.yaml en cada archivo existente. Si tu configuración ya se ejecuta mediante Ansible, cifrar los mismos valores con Ansible Vault permite obtener el mismo resultado sin una segunda herramienta.

credenciales de systemd: secretos que nunca llegan al entorno

Ubuntu 24.04 incluye systemd 255, por lo que no es necesario instalar nada. systemd-creds cifra un secreto en el host y systemd lo descifra en un directorio privado que sólo puede leer ese servicio.

sudo systemd-creds setup
sudo install -d -m 700 /etc/myapp
echo -n 'hunter2' | sudo systemd-creds encrypt --name=db_password - /etc/myapp/db_password.cred
[Service]
User=myapp
LoadCredentialEncrypted=db_password:/etc/myapp/db_password.cred
ExecStart=/usr/local/bin/myapp

El servicio lee el valor desde un archivo llamado db_password dentro del directorio indicado por $CREDENTIALS_DIRECTORY. El valor no está en el entorno, por lo que /proc/<pid>/environ no muestra nada útil, y el texto en claro nunca se escribe en el sistema de archivos raíz.

Verifique que el archivo se descifra antes de configurar una unidad para usarlo:

sudo systemd-creds decrypt /etc/myapp/db_password.cred -

Debe saber qué clave lo cifró, porque eso determina si la copia de seguridad sirve de algo. El valor predeterminado --with-key=auto usa el chip TPM2 (módulo de plataforma segura, versión 2) cuando está presente y disponible, y la clave del host en caso contrario. La mayoría de las instancias VPS no tienen TPM2.

systemd-analyze has-tpm2

no indica que se usó la clave del host, que se encuentra en /var/lib/systemd/credential.secret y sólo puede leerla root. Si restaura db_password.cred en un VPS nuevo sin ese archivo, nada podrá descifrarlo. Incluya credential.secret en la misma copia de seguridad o conserve el texto en claro en una ubicación a la que todavía pueda acceder.

Secretos de Docker: archivos en /run/secrets

Compose lee un archivo del host y lo monta en el contenedor en /run/secrets/<name>.

services:
  app:
    image: myapp:latest
    environment:
      DB_PASSWORD_FILE: /run/secrets/db_password
    secrets:
      - db_password
secrets:
  db_password:
    file: ./db_password.txt
docker compose exec app cat /run/secrets/db_password
docker compose exec app env | grep -i password

El primero muestra el secreto. El segundo muestra sólo DB_PASSWORD_FILE=/run/secrets/db_password, que es el objetivo: el valor nunca está en el entorno del contenedor, por lo que no aparece en la salida de docker inspect. Muchas imágenes oficiales ya esperan esta estructura, y la imagen de Postgres lee POSTGRES_PASSWORD_FILE exactamente de esta forma.

Debe tener claro qué ofrece este mecanismo. Fuera del modo Swarm no hay cifrado en ninguna capa: ./db_password.txt es un archivo de texto plano en el host, y su única protección son sus permisos y su propietario. Configure ambos manualmente, porque Compose montará sin quejarse un archivo legible por todos. El conjunto más amplio de ventajas y limitaciones frente al atajo de env_file se explica en la guía sobre archivos env y secretos de Compose.

Qué coste real tienen OpenBao y Vault

OpenBao es el fork de HashiCorp Vault mantenido por Linux Foundation. Se inició después de que HashiCorp relicenciara Vault con la Business Source License en 2023. OpenBao continúa bajo MPL 2.0 (Mozilla Public License). La versión 2.6.2 era la actual en agosto de 2026. Casi todo lo siguiente también se aplica a Vault, porque el fork conservó la misma interfaz de comandos.

docker pull docker.io/openbao/openbao

Los paquetes para Debian y Ubuntu están disponibles en la página de descargas de OpenBao si prefiere que apt gestione las actualizaciones. El servidor necesita un archivo de configuración que contenga un listener y un backend de almacenamiento:

listener "tcp" {
  address       = "127.0.0.1:8200"
  tls_cert_file = "/path/to/full-chain.pem"
  tls_key_file  = "/path/to/private-key.pem"
}

storage "raft" {
  path = "/path/to/raft/data"
  node_id = "raft_node_1"
}

Después, inícielo una vez:

bao operator init

De forma predeterminada, esto divide la clave root en 5 partes y requiere 3 de ellas para desprecintar el servidor. Esas son las opciones -key-shares y -key-threshold. OpenBao muestra las partes y el token root inicial una sola vez y no vuelve a mostrarlos.

Ahora viene la parte que omiten casi todas las comparativas. Un servidor reiniciado es un servidor precintado. OpenBao mantiene la clave root únicamente en memoria. Por eso, después de un reinicio, no puede descifrar su propio almacenamiento hasta que alguien proporciona el umbral de partes necesario. Una actualización del kernel o una terminación por falta de memoria deja el servidor precintado y las aplicaciones no pueden iniciar sesión.

En un VPS de una sola persona, la división de Shamir no protege nada, porque las cinco partes terminan en el mismo gestor de contraseñas de la misma persona. El desprecintado automático mueve la clave a un dispositivo o servicio de confianza. En una nube grande, esto significa normalmente un servicio de gestión de claves. En un VPS, suele significar un archivo de claves almacenado en el mismo disco que los datos que protege. Esto reduce realmente la seguridad, a cambio de que el servidor vuelva a funcionar por sí solo después de un reinicio. Tome esta decisión de forma consciente y deje registrado qué opción eligió.

Infisical: una interfaz web, una base de datos y una clave maestra que seguirá bajo tu control

Infisical es una plataforma de secretos con interfaz web, proyectos, entornos y control de acceso por usuario. El autoalojamiento con Compose es breve:

curl -o docker-compose.prod.yml https://raw.githubusercontent.com/Infisical/infisical/main/docker-compose.prod.yml
curl -o .env https://raw.githubusercontent.com/Infisical/infisical/main/.env.example
docker compose -f docker-compose.prod.yml up -d

Edita .env antes de ejecutar ese último comando. Dos valores deben ser propios y uno de ellos no debe cambiar nunca después:

openssl rand -hex 16
openssl rand -base64 32

El primero es ENCRYPTION_KEY, una cadena hexadecimal de 16 bytes. Es la clave con la que se cifran tus secretos dentro de PostgreSQL. Si la pierdes, una copia de seguridad perfecta de la base de datos se convierte en un conjunto de textos cifrados. Si la cambias en una instancia en ejecución, los secretos existentes dejan de descifrarse. El segundo es AUTH_SECRET, una cadena base64 de 32 bytes que se usa para las sesiones. SITE_URL debe ser la URL absoluta a la que realmente accederás, incluido el protocolo, o la redirección del inicio de sesión fallará.

Infisical encaja mejor que OpenBao cuando lo que realmente necesitas son usuarios: una interfaz web para un equipo pequeño y separación entre entornos, en lugar de credenciales de base de datos que caducan por sí solas. A cambio, debes mantener PostgreSQL, Redis y un certificado TLS, y aplicarles parches y copias de seguridad.

Qué ocurre cuando el servicio de secretos está caído y la aplicación se reinicia

Esta pregunta determina si un servicio de secretos debe ejecutarse en un único servidor. Los archivos se pueden leer antes de que se inicie la red. Un servicio no.

Reinicie el servidor y la aplicación y OpenBao se iniciarán al mismo tiempo. La aplicación solicita la contraseña de la base de datos, OpenBao todavía está sellado, la solicitud falla y systemd reinicia la aplicación en un bucle hasta que una persona introduce las partes necesarias para desellarlo. No hay ningún componente roto. Tampoco hay ninguno disponible.

Hay dos formas correctas de gestionarlo. Ordene las unidades y permita que la aplicación vuelva a intentarlo: After= el servicio de secretos, además de Restart=on-failure y un RestartSec= suficientemente largo para no sobrecargar la API. O recupere el secreto durante el despliegue en lugar de hacerlo durante el arranque: genere el secreto en un archivo con permisos 600 o en una credencial de systemd, de modo que el sistema en ejecución dependa de un archivo y no de una API.

La caducidad del token es el mismo problema, pero en una escala de tiempo más lenta. Los tokens y las concesiones de OpenBao tienen un tiempo de vida, por lo que un proceso de larga duración que nunca los renueva pierde el acceso en un momento que no está relacionado con ningún despliegue. Este fallo resulta confuso precisamente porque ese día no cambió nada.

Hacer una copia de seguridad del almacén

Todas las opciones de esta sección tienen una clave, y una copia de seguridad sin esa clave no sirve. Anote dónde se encuentra la suya.

En un archivo de entorno, el propio archivo es el secreto, por lo que la copia de seguridad debe estar cifrada. Con SOPS, el archivo cifrado puede almacenarse en cualquier ubicación pública, y la clave privada de age en ~/.config/sops/age/keys.txt es lo que no debe perder. Para las credenciales de systemd, haga una copia de seguridad de /var/lib/systemd/credential.secret junto con los archivos .cred. Para Infisical, cree un volcado de PostgreSQL y almacene ENCRYPTION_KEY en una ubicación separada.

OpenBao con almacenamiento raft crea su propia instantánea:

bao operator raft snapshot save backup.snap
bao operator raft snapshot restore backup.snap

La instantánea contiene el almacenamiento cifrado, por lo que restaurarla en un servidor nuevo todavía requiere las partes de desbloqueo de bao operator init. Un trabajo nocturno que copie instantáneas al almacenamiento de objetos mientras las partes se guardan en ninguna ubicación no es una copia de seguridad de nada. Pruebe la restauración en un VPS desechable antes de depender de ella.

Registro de auditoría: quién leyó cada secreto

Los archivos no proporcionan un registro de auditoría. El modo y el propietario indican quién podría leer el secreto. Nunca indican quién lo hizo. auditd con una supervisión de la ruta es el sustituto más cercano, pero informa de que se abrió un archivo, no de qué valor se utilizó.

OpenBao registra cada solicitud en un dispositivo de auditoría que debe habilitar explícitamente:

bao audit enable file file_path=/var/log/openbao_audit.log

Hay dos aspectos de ese registro que cambian la forma de administrar el servidor. La mayoría de las cadenas de las solicitudes y respuestas se someten a un hash HMAC-SHA256 con una sal. Así puede comparar con el registro un valor que ya conoce sin que el propio registro contenga el texto sin cifrar. Los enteros y los booleanos se escriben sin cifrar, por lo que ese hash no protege un secreto numérico.

Después aparece el problema operativo: OpenBao no responde a las solicitudes cuando ningún dispositivo de auditoría habilitado puede registrarlas. Además, un dispositivo que falla de forma bloqueante hace que las solicitudes queden bloqueadas hasta que alguien lo repare. Un disco lleno en /var/log deja fuera de servicio la API de secretos por diseño. Reserve espacio propio para el registro de auditoría y configure una regla de logrotate desde el primer día, no después de la primera interrupción.

¿Qué gestor de secretos autoalojado debería ejecutar?

Cuente las máquinas y las personas. Después, elija.

  1. Una máquina y una persona: un archivo de entorno con permisos 600, propiedad de root y legible por un usuario de servicio. Añada las credenciales de systemd cuando quiera evitar que el valor quede en el entorno del proceso.
  2. Una máquina y entre dos y cinco personas, con la configuración ya almacenada en git: SOPS con age. Cada persona obtiene un par de claves, y .sops.yaml enumera todas las claves públicas autorizadas para descifrar.
  3. Varias máquinas, un repositorio de configuración y sin necesidad de credenciales que caduquen: siga usando SOPS con age, con una clave receptora por host, de modo que una clave de host robada sólo permita descifrar los archivos de ese host.
  4. Varias máquinas y varios equipos que realmente necesitan credenciales de base de datos con una vigencia definida, además de un registro de auditoría que alguien revise: OpenBao. Reserve una hora al mes de trabajo operativo para las pruebas de desbloqueo y restauración.

La regla común a los cuatro casos es la misma. Ejecute la opción más pequeña que cumpla un requisito que pueda expresar claramente, porque un gestor de secretos que está caído no se distingue de un gestor de secretos vacío.

FAQ

¿Merece la pena usar un gestor de secretos autohospedado en un único VPS?

Normalmente no, si se refiere a un servicio como OpenBao o Infisical. En un solo equipo con una o dos personas, un archivo de entorno con permisos mode 600 o una credencial cifrada de systemd ofrecen la misma protección frente a otro usuario local, sin un proceso de desbloqueo y sin otro servicio que actualizar. Un servicio de secretos empieza a compensar cuando tiene varias máquinas y varias personas, o cuando necesita credenciales que caduquen sin que nadie tenga que rotarlas manualmente.

¿Cuál es la diferencia entre un gestor de contraseñas y un gestor de secretos?

Un gestor de contraseñas almacena credenciales que introduce una persona, y un usuario lo desbloquea mientras está presente. Un gestor de secretos entrega credenciales a procesos, por lo que debe funcionar a las 03:00 sin que nadie lo supervise. Esa es la diferencia fundamental: un gestor de contraseñas bloqueado le obliga a volver a escribir una contraseña maestra, mientras que un gestor de secretos sellado detiene todos los servicios que se reinicien mientras permanezca sellado.

¿Qué ocurre con mis aplicaciones si OpenBao queda sellado después de un reinicio?

No pueden obtener sus secretos, por lo que no se inician, y systemd las reinicia en un bucle hasta que alguien proporciona el umbral de desbloqueo, que de forma predeterminada es 3 de 5 fragmentos. OpenBao mantiene la clave root sólo en memoria, por lo que cada reinicio vuelve a sellarlo. Puede activar el desbloqueo automático, aceptando que en un único VPS la clave de desbloqueo termina en el mismo disco que los datos, o generar los secretos en un archivo durante el despliegue para que el arranque nunca dependa de la API.

¿Puedo confirmar archivos cifrados con SOPS en un repositorio público?

Los valores están cifrados, por lo que están protegidos frente a cualquiera que no tenga la clave privada de age. Las claves no están cifradas: un lector puede ver que conserva STRIPE_SECRET_KEY y SMTP_PASSWORD, y con qué frecuencia cambia cada una. Esos metadatos son aceptables para la mayoría de los proyectos y no lo son para algunos. Mantenga la clave privada de age fuera del repositorio y ejecute sops updatekeys en todos los archivos existentes cada vez que añada o elimine un destinatario.