Sauvegarder Vaultwarden dans le stockage objet avec restic
restic est un programme de sauvegarde qui chiffre et signe chaque octet sur la machine sauvegardée, avant que quoi que ce soit atteigne le stockage. Son guide Amazon S3 est une référence connue. Comme le stockage objet ZCP utilise l’API S3, la même configuration fonctionne ici avec un point de terminaison différent.
Ce tutoriel réalise toute la configuration de bout en bout. Terraform ou OpenTofu provisionne une VM de gestionnaire de mots de passe Vaultwarden, un réseau, une IP publique et un compartiment de stockage objet. Un script Bash sauvegarde ensuite la base de données SQLite dans ce compartiment selon une minuterie nocturne.
À la fin, vous disposez de :
- Vaultwarden exécuté sur une VM accessible en HTTPS
- Un compartiment de stockage objet contenant un dépôt restic
- Un script de sauvegarde sur une minuterie systemd, avec rétention et contrôles d’intégrité
- La preuve que les données déposées dans le compartiment sont chiffrées, ainsi qu’une restauration qui les remet dans un coffre fonctionnel
Prévoyez environ 45 minutes.
Avant de commencer
Section intitulée « Avant de commencer »- Un compte ZSoftly Public Cloud. Créez-en un si vous n’en avez pas.
- Terraform 1.0 ou version ultérieure, ou OpenTofu 1.6 ou version ultérieure.
- Le CLI
zcp, pour rechercher les slugs et inspecter le compartiment. - Une paire de clés SSH. Générez-en une avec
ssh-keygen -t ed25519si nécessaire.
Étape 1 : Exporter votre jeton d’API
Section intitulée « Étape 1 : Exporter votre jeton d’API »Dans la console, allez à Compte → Clés d’API, créez un jeton et exportez-le. Le fournisseur et la CLI lisent la même variable :
export ZCP_BEARER_TOKEN="<your-token>"Le jeton reste hors de votre configuration et de votre fichier d’état.
Étape 2 : Trouver les slugs de votre compte
Section intitulée « Étape 2 : Trouver les slugs de votre compte »Le calcul et le stockage objet se trouvent dans des régions distinctes. Les machines virtuelles
s’exécutent dans yul-1 (Montréal) ou yow-1 (Ottawa), et les stockages objet se trouvent dans
os-yul ou os-yow. Choisissez la paire dans la même ville pour réduire la latence et les frais de
transfert, ou l’autre ville pour conserver la sauvegarde hors du domaine de défaillance de la VM
qu’elle protège.
zcp region list # regions, including the os- oneszcp project list # your project slugzcp plan vm --region yul-1 # compute planszcp plan network --region yul-1 # network planszcp plan ip --region yul-1 # public IP planszcp storage-category list --region yul-1 # VM disk categorieszcp template list --region yul-1 | grep -i vaultwardenzcp plan object-storage --region os-yul # object storage sizeszcp storage-category list --region os-yul # object storage categoriesL’image Vaultwarden est une application Marketplace. Elle fournit Vaultwarden dans Docker Compose sur Ubuntu 24.04, génère un certificat autosigné et un jeton administrateur au premier démarrage, puis sert le coffre web sur le port 8000.
Étape 3 : Écrire la configuration
Section intitulée « Étape 3 : Écrire la configuration »Créez un répertoire vide avec un fichier, main.tf. Le stockage objet utilise sa propre source de
données de région, afin qu’aucune valeur ne soit codée en dur deux fois :
terraform { required_providers { zcp = { source = "zsoftly/zcp" version = "~> 0.2" } }}
provider "zcp" { default_project = "default-9"}
variable "admin_cidr" { description = "CIDR allowed to reach SSH and the web vault. Deliberately has no default." type = string}
data "zcp_region" "compute" { slug = "yul-1"}
data "zcp_region" "objects" { slug = "os-yul"}
resource "zcp_ssh_key" "vault" { name = "vault-key" region = data.zcp_region.compute.slug public_key = file("~/.ssh/id_ed25519.pub")}
resource "zcp_network" "vault" { name = "vault-network" cloud_provider = data.zcp_region.compute.cloud_provider region = data.zcp_region.compute.slug network_plan = "pnet-yul" billing_cycle = "hourly"}
resource "zcp_instance" "vault" { name = "vault-01" cloud_provider = data.zcp_region.compute.cloud_provider region = data.zcp_region.compute.slug template = "zmi-vaultwarden-1360-ubuntu2404-100-1" plan = "ca2sm" billing_cycle = "hourly" network = zcp_network.vault.id storage_category = "premium-ssd" ssh_key = zcp_ssh_key.vault.name assign_public_ip = false}
resource "zcp_ip_address" "vault" { plan = "ipv4-yul" billing_cycle = "hourly" network = zcp_network.vault.id}
resource "zcp_egress_rule" "https_out" { network = zcp_network.vault.id protocol = "tcp" cidr = "0.0.0.0/0" start_port = "443" end_port = "443"}
resource "zcp_egress_rule" "http_out" { network = zcp_network.vault.id protocol = "tcp" cidr = "0.0.0.0/0" start_port = "80" end_port = "80"}
resource "zcp_egress_rule" "dns_out" { network = zcp_network.vault.id protocol = "udp" cidr = "0.0.0.0/0" start_port = "53" end_port = "53"}
resource "zcp_firewall_rule" "ssh" { ip_address = zcp_ip_address.vault.id protocol = "tcp" cidr_list = var.admin_cidr start_port = "22" end_port = "22"}
resource "zcp_firewall_rule" "vault_web" { ip_address = zcp_ip_address.vault.id protocol = "tcp" cidr_list = var.admin_cidr start_port = "8000" end_port = "8000"}
resource "zcp_port_forward" "ssh" { ip_address = zcp_ip_address.vault.id protocol = "tcp" public_start_port = "22" public_end_port = "22" private_start_port = "22" private_end_port = "22" virtual_machine = zcp_instance.vault.id}
resource "zcp_port_forward" "vault_web" { ip_address = zcp_ip_address.vault.id protocol = "tcp" public_start_port = "8000" public_end_port = "8000" private_start_port = "8000" private_end_port = "8000" virtual_machine = zcp_instance.vault.id}
resource "zcp_object_storage" "backups" { name = "vault-backups" cloud_provider = data.zcp_region.objects.cloud_provider region = data.zcp_region.objects.slug billing_cycle = "hourly" storage_category = "ssd-storage" plan = "o210g"}
resource "zcp_object_storage_bucket" "restic" { object_storage = zcp_object_storage.backups.id name = "vaultwarden-restic"}
output "vault_ip" { value = zcp_ip_address.vault.ip_address}
output "object_storage_slug" { value = zcp_object_storage.backups.id}
# The platform assigns the bucket a suffixed identifier when it is created, and that# value is both the bucket slug and the S3 bucket name. The `name` argument above is# only the name requested, so never use it in an endpoint URL.output "bucket_name" { value = zcp_object_storage_bucket.restic.id}
output "s3_access_key" { value = zcp_object_storage.backups.api_key sensitive = true}
output "s3_secret_key" { value = zcp_object_storage.backups.api_secret sensitive = true}Plusieurs choix de ce fichier ne sont pas évidents. Une erreur peut vous laisser avec une VM inaccessible ou impossible à sauvegarder.
Les règles de sortie sont obligatoires ici. Un réseau déclaré comme ressource distincte commence sans règle de sortie et refuse tout trafic sortant. Sans ces trois règles, la VM ne peut atteindre ni le dépôt de paquets, ni GitHub, ni le point de terminaison du stockage objet. Chaque commande des étapes suivantes expire. Les règles ci-dessus autorisent le trafic HTTPS, HTTP et DNS vers toute destination. Restreignez les CIDR de destination lorsque vous connaissez les hôtes nécessaires.
Cela concerne la gestion directe de votre réseau. Un réseau créé automatiquement par la VM, via
network_plan ou l’option --network-plan de la CLI, est provisionné avec un accès sortant
fonctionnel. Prendre la responsabilité du réseau pour un destroy propre implique aussi de prendre
la responsabilité de ses règles de sortie.
Le trafic entrant passe par des redirections de port, pas par un NAT statique. La première IP
publique allouée à un nouveau réseau devient son adresse source-NAT et la plateforme refuse d’y
placer un NAT statique. Un zcp_ip_association sur cette IP indique une réussite sans rien changer,
donc le trafic n’arrive jamais. Utilisez plutôt zcp_port_forward. Chaque redirection nécessite une
règle de pare-feu sur la même IP pour laisser entrer le trafic.
assign_public_ip = false sur l’instance empêche la plateforme d’allouer une deuxième adresse. La
ressource zcp_ip_address fournit l’unique adresse utilisée par le réseau.
zcp_ssh_key accepte un argument region. La documentation du registre pour cette ressource
l’omet. L’API en a besoin pour déterminer le fournisseur cloud, donc son absence fait échouer la
validation.
Donnez son propre compartiment au dépôt restic. Un dépôt possède exclusivement les chemins dans lesquels il écrit, donc ne pointez pas une deuxième charge de travail vers le même compartiment.
Étape 4 : Appliquer
Section intitulée « Étape 4 : Appliquer »tofu inittofu plantofu applyterraform initterraform planterraform applyL’application attend que la VM indique Running. La VM est l’élément lent, avec environ deux minutes et demie, et toute la pile est prête en trois à quatre minutes. Lisez les sorties :
tofu output vault_iptofu output object_storage_slugtofu output bucket_nametofu output -raw s3_access_keytofu output -raw s3_secret_keyobject_storage_slug est le slug du stockage, demandé comme premier argument par chaque commande
zcp object-storage. bucket_name provient de l’id de la ressource de compartiment, que la
plateforme définit à la valeur suffixée qu’elle a attribuée. Cette valeur suffixée est la chaîne à
laquelle répond le point de terminaison S3. Confirmez-la dans la plateforme avant de vous y fier,
car l’argument name de la ressource contient toujours le nom sans suffixe que vous avez saisi :
zcp object-storage bucket list <object_storage_slug> --region os-yul --project default-9Terraform marque les deux sorties de clé comme sensibles, donc l’option -raw est nécessaire pour
les afficher. Il les écrit tout de même en clair dans l’état. Tout fournisseur qui renvoie un
identifiant se comporte de la même façon. Conservez le fichier d’état à un endroit où vous seriez
prêt à conserver l’identifiant lui-même.
Étape 5 : Ouvrir le coffre et y ajouter un élément
Section intitulée « Étape 5 : Ouvrir le coffre et y ajouter un élément »Le premier démarrage prend moins d’une minute. Connectez-vous et attendez la fin :
ssh ubuntu@<vault_ip>journalctl -u vaultwarden-first-boot.service -fLisez le jeton administrateur généré :
sudo cat /root/.credentials/vaultwarden.txtOuvrez https://<vault_ip>:8000 dans un navigateur. Le certificat autosigné déclenche un
avertissement, acceptez donc l’exception. L’auto-inscription est désactivée par défaut. Invitez-vous
depuis le panneau d’administration à https://<vault_ip>:8000/admin, sous Users, connectez-vous
puis enregistrez un mot de passe. Vous supprimerez et restaurerez cet élément plus tard. Il doit
donc exister avant la première sauvegarde.
Les données du coffre se trouvent dans /data/vaultwarden/data : db.sqlite3, les répertoires des
pièces jointes et des envois, config.json et la paire de clés RSA qui signe les jetons clients. Le
fichier Compose et le fichier d’environnement se trouvent un niveau plus haut dans
/data/vaultwarden.
Étape 6 : Installer restic sur la VM
Section intitulée « Étape 6 : Installer restic sur la VM »Ubuntu fournit restic dans ses paquets, mais son archive accuse plusieurs versions de retard. Installez le binaire actuel et vérifiez-le avec les sommes de contrôle publiées :
sudo apt-get updatesudo apt-get install -y bzip2 rsync sqlite3
RESTIC_VERSION=0.19.1cd /tmpcurl -fsSLO "https://github.com/restic/restic/releases/download/v${RESTIC_VERSION}/restic_${RESTIC_VERSION}_linux_amd64.bz2"curl -fsSLO "https://github.com/restic/restic/releases/download/v${RESTIC_VERSION}/SHA256SUMS"sha256sum --ignore-missing -c SHA256SUMS
bunzip2 "restic_${RESTIC_VERSION}_linux_amd64.bz2"sudo install -m 755 "restic_${RESTIC_VERSION}_linux_amd64" /usr/local/bin/resticrestic versionsha256sum -c doit afficher OK pour l’archive. Si ce n’est pas le cas, arrêtez-vous et
téléchargez-la de nouveau.
Étape 7 : Créer le dépôt
Section intitulée « Étape 7 : Créer le dépôt »restic lit sa configuration dans l’environnement. Placez-la dans un fichier réservé à root plutôt que dans un profil shell afin que la minuterie systemd et votre shell interactif partagent une seule source de vérité.
Générez d’abord un mot de passe de dépôt. Toute personne qui obtient une copie du compartiment en a encore besoin pour lire votre coffre :
sudo install -d -m 700 /etc/resticopenssl rand -base64 32 | sudo tee /etc/restic/repo-password > /dev/nullsudo chmod 600 /etc/restic/repo-passwordsudo cat /etc/restic/repo-passwordÉcrivez maintenant le fichier d’environnement en remplaçant le point de terminaison par celui de votre région, puis le nom du compartiment et les deux clés par les sorties Terraform :
sudo install -d -m 700 /var/cache/resticsudo tee /etc/restic/vaultwarden.env > /dev/null <<'EOF'RESTIC_REPOSITORY=s3:https://objects.yul.zcp.zsoftly.ca/<your-bucket-name>RESTIC_PASSWORD_FILE=/etc/restic/repo-passwordRESTIC_CACHE_DIR=/var/cache/resticAWS_ACCESS_KEY_ID=<access-key>AWS_SECRET_ACCESS_KEY=<secret-key>EOFsudo chmod 600 /etc/restic/vaultwarden.envRESTIC_CACHE_DIR semble mineur, mais son absence casse la minuterie. systemd exécute les services
sans $HOME, et restic refuse de démarrer lorsqu’il ne peut localiser un répertoire de cache. Sans
cette variable, la minuterie nocturne de l’étape 9 échoue avec
unable to locate cache directory: neither $XDG_CACHE_HOME nor $HOME are defined. La placer dans le
fichier d’environnement partagé corrige à la fois la minuterie et votre shell interactif.
Les points de terminaison sont https://objects.yul.zcp.zsoftly.ca pour os-yul et
https://objects.yow.zcp.zsoftly.ca pour os-yow. Le chemin après l’hôte est le nom du
compartiment, qui correspond à la forme s3:https://server/bucket utilisée par restic pour tout
serveur compatible S3.
Le fichier est réservé à root. Chargez-le donc dans un shell root plutôt que dans le vôtre. Chaque
commande exécutée sur la VM d’ici à l’étape 11 utilise ce shell root et ne porte donc pas sudo.
Les commandes zcp de l’étape 10 sont l’exception et s’exécutent sur votre poste de travail :
sudo -iset -a; . /etc/restic/vaultwarden.env; set +arestic initcreated restic repository db62f25025 at s3:https://objects.yul.zcp.zsoftly.ca/vaultwarden-restic-001024
Please note that knowledge of your password is required to accessthe repository. Losing your password means that your data isirrecoverably lost.Étape 8 : Écrire le script de sauvegarde
Section intitulée « Étape 8 : Écrire le script de sauvegarde »Vaultwarden conserve ses données dans SQLite. Copier un fichier SQLite actif avec cp peut capturer
une transaction incomplète. La commande .backup du client sqlite3 crée un instantané cohérent
pendant que le conteneur continue de répondre. Aucune interruption n’est nécessaire.
Toujours dans le shell root, écrivez le script dans /root/vaultwarden-backup.sh :
#!/usr/bin/env bash# Back up Vaultwarden to ZCP object storage with restic.set -euo pipefail
VW_DIR=/data/vaultwardenDATA_DIR="${VW_DIR}/data"STAGE_ROOT=/var/backups/vaultwardenSTAGE_DIR="${STAGE_ROOT}/stage"ENV_FILE=/etc/restic/vaultwarden.env
set -a# shellcheck disable=SC1090. "${ENV_FILE}"set +a
install -d -m 700 "${STAGE_ROOT}"install -d -m 700 "${STAGE_DIR}"trap 'rm -rf "${STAGE_DIR:?}"' EXIT
# Copy the file-based data, skipping the live database and anything regenerable.rsync -a --delete \ --exclude 'db.sqlite3*' \ --exclude 'icon_cache/' \ --exclude 'tmp/' \ --exclude 'ssl/' \ "${DATA_DIR}/" "${STAGE_DIR}/data/"
# Snapshot the running database without stopping the container.sqlite3 "${DATA_DIR}/db.sqlite3" ".backup '${STAGE_DIR}/data/db.sqlite3'"
# Refuse to upload a copy that is already damaged.integrity=$(sqlite3 "${STAGE_DIR}/data/db.sqlite3" 'PRAGMA integrity_check;')if [ "${integrity}" != "ok" ]; then echo "[ERROR] integrity check on the staged database returned: ${integrity}" >&2 exit 1fi
# The env file carries the admin token hash, so a restore needs it too.install -m 600 "${VW_DIR}/vaultwarden.env" "${STAGE_DIR}/vaultwarden.env"
restic backup --tag vaultwarden "${STAGE_DIR}"
restic forget --tag vaultwarden \ --keep-daily 7 --keep-weekly 4 --keep-monthly 6 \ --prune
restic check
echo "[OK] vaultwarden backup finished"Installez-le et exécutez-le une première fois manuellement :
install -m 700 /root/vaultwarden-backup.sh /usr/local/sbin/vaultwarden-backup.sh/usr/local/sbin/vaultwarden-backup.shLa première exécution téléverse tout. Les suivantes n’envoient que les blocs modifiés. Un coffre qui ajoute quelques mots de passe par jour ne consomme donc que quelques kilooctets par nuit. Un nouveau coffre produit un premier instantané très petit :
Files: 3 new, 0 changed, 0 unmodifiedDirs: 5 new, 0 changed, 0 unmodifiedAdded to the repository: 276.673 KiB (9.721 KiB stored)
processed 3 files, 274.046 KiB in 0:00snapshot f5eb9b2f savedVérifiez ce qui a été envoyé :
restic snapshotsID Time Host Tags Paths Sizef5eb9b2f 2026-09-13 21:39:33 vwtest-01 vaultwarden /var/backups/vaultwarden/stage 274.046 KiBLa ligne de rétention conserve 7 instantanés quotidiens, 4 hebdomadaires et 6 mensuels, puis
supprime ce qui ne relève d’aucune de ces trois catégories. restic check vérifie que tous les
paquets et index référencés existent. Sur un petit dépôt, cela prend quelques secondes. Lorsque le
dépôt dépasse quelques dizaines de gigaoctets, placez cette vérification dans un minuteur
hebdomadaire séparé et ajoutez-y --read-data-subset=1/7, qui lit et vérifie un septième des
données de paquets à chaque exécution.
Étape 9 : L’exécuter chaque nuit
Section intitulée « Étape 9 : L’exécuter chaque nuit »Créez l’unité de service dans /etc/systemd/system/vaultwarden-backup.service :
[Unit]Description=Back up Vaultwarden to ZCP object storage with resticAfter=network-online.target docker.serviceWants=network-online.target
[Service]Type=oneshotExecStart=/usr/local/sbin/vaultwarden-backup.shNice=10IOSchedulingClass=idlePuis le minuteur dans /etc/systemd/system/vaultwarden-backup.timer :
[Unit]Description=Daily Vaultwarden restic backup
[Timer]OnCalendar=*-*-* 02:30:00RandomizedDelaySec=20mPersistent=true
[Install]WantedBy=timers.targetActivez-le :
systemctl daemon-reloadsystemctl enable --now vaultwarden-backup.timersystemctl list-timers vaultwarden-backup.timerConfirmez son exécution sous systemd sans attendre le lendemain :
systemctl start vaultwarden-backup.servicesystemctl show vaultwarden-backup.service -p Result -p ExecMainStatusResult=successExecMainStatus=0OnCalendar suit l’horloge de la VM, en UTC sauf modification de votre part. Persistent=true
exécute au prochain démarrage une sauvegarde manquée. Une VM éteinte pendant la nuit rattrape donc
son retard. Consultez la dernière exécution avec journalctl -u vaultwarden-backup.service -n 50.
Étape 10 : Confirmer que le compartiment contient du texte chiffré
Section intitulée « Étape 10 : Confirmer que le compartiment contient du texte chiffré »Cette étape répond à la question laissée ouverte par l’absence de chiffrement côté serveur. Chaque
dépôt restic contient à sa racine un petit objet nommé config. Sur la VM, restic le déchiffre :
restic cat config{ "version": 2, "id": "db62f2502541f42cc6af484cc6b9fc04b24498e3dc6430441a4156bcea6553de", "chunker_polynomial": "395c3db449e5db"}Récupérez maintenant le même objet directement du compartiment. Utilisez les identifiants de gestion de votre poste de travail plutôt que restic :
zcp object-storage object list <object_storage_slug> <your-bucket-name> \ --region os-yul --project default-9
zcp object-storage object download <object_storage_slug> <your-bucket-name> config \ --dest /tmp/restic-config --region os-yul --project default-9
file /tmp/restic-confighead -c 32 /tmp/restic-config | xxdLa liste présente la structure standard du dépôt :
KEY SIZEconfig 155data/00/00d100ce161bbecdd894e17051b75e431d81d8178c3f25c665e27373e1b167ff 1945data/a3/a31a963e4c25da5fc0a8446cb32840830b6ecf1aa70f26e5145863d6ed13d91c 8081index/d17710e043e7ba09fa77ab93681d82dc4ca1bfe7a7ad915f0e45c0046987e07e 638keys/78fd2a6413aa925c0f0fe801933cc68f18568ae82cb7dcede38121e288f25bb4 446snapshots/f5eb9b2f32fd48703d1f1b4487d33195099250bdeffe625a94149653c66a46bf 379Le même objet config, extrait directement du compartiment, est illisible :
/tmp/restic-config: data00000000: 6498 2898 76cb 76fd 3a6a e81f 1dd5 b9db d.(.v.v.:j......00000010: eac5 6875 a1b5 b406 59c3 7b4c b264 c344 ..hu....Y.{L.d.Dfile indique data et le vidage hexadécimal est du bruit. Il en va de même pour les paquets sous
data/, qui contiennent le coffre. Rien dans le compartiment n’est lisible sans le mot de passe du
dépôt. Ce mot de passe ne quitte jamais la VM ni votre copie hors ligne. restic chiffre avec AES-256
en mode compteur et authentifie avec Poly1305-AES. Un objet modifié échoue donc à la vérification
MAC au lieu de se déchiffrer en données plausibles.
Étape 11 : Restaurer
Section intitulée « Étape 11 : Restaurer »Une sauvegarde que vous n’avez jamais restaurée reste une hypothèse. Vérifiez-la maintenant, quand la perte des données n’a pas de conséquence.
Supprimez du coffre web l’élément de mot de passe enregistré à l’étape 5. Restaurez ensuite l’instantané :
restic snapshots --tag vaultwardenrestic restore latest --target /tmp/vault-restorerestoring snapshot f5eb9b2f of [/var/backups/vaultwarden/stage] to /tmp/vault-restoreSummary: Restored 8 files/dirs (274.046 KiB) in 0:00restore écrit l’arborescence dans /tmp/vault-restore/var/backups/vaultwarden/stage. Remettez la
base de données avec le conteneur arrêté afin qu’aucune écriture ne survienne pendant l’opération :
cd /data/vaultwardendocker compose stoprsync -a --delete \ --exclude 'ssl/' \ --exclude 'icon_cache/' \ --exclude 'tmp/' \ /tmp/vault-restore/var/backups/vaultwarden/stage/data/ ./data/chown -R root:root ./datadocker compose start--delete en fait une restauration ponctuelle plutôt qu’une fusion. Les pièces jointes, envois et
config.json reviennent donc à leur état au moment de l’instantané, et tout élément créé depuis est
supprimé. Copier seulement la base de données les désynchroniserait.
Les trois exclusions reprennent celles du script de sauvegarde et rendent ici --delete sûr. rsync
ne supprime pas un chemin exclu sauf avec --delete-excluded, donc le répertoire actif ssl/
survit. C’est essentiel : ROCKET_TLS dans vaultwarden.env pointe sur /data/ssl/cert.pem, le
certificat est généré sur la VM et n’est jamais préparé, et sa suppression empêche le conteneur de
démarrer. Vaultwarden régénère icon_cache/ et tmp/.
vaultwarden.env figure aussi dans l’instantané, mais laissez-le intact lors d’un retour en arrière
sur la même VM. Une restauration de base ne le modifie pas et l’écraser supprimerait toute
modification ultérieure de DOMAIN ou du jeton administrateur. Restaurez-le seulement lors d’une
reconstruction sur une autre VM, comme ci-dessous.
Rechargez le coffre web et connectez-vous. L’élément supprimé est revenu. Pour vérifier sans navigateur, lisez directement la base avant et après :
sqlite3 /data/vaultwarden/data/db.sqlite3 'select email from users;'Pour reconstruire plutôt sur une nouvelle VM, déployez la même image Marketplace et installez
restic. Créez le même /etc/restic/vaultwarden.env et le même mot de passe de dépôt, puis restaurez
depuis le dépôt existant. Copiez aussi rsa_key.pem et config.json. La clé RSA signe les jetons
clients et son remplacement déconnecte tous les clients.
restic restore latest --target /tmp/vault-restorecp /tmp/vault-restore/var/backups/vaultwarden/stage/vaultwarden.env \ /data/vaultwarden/vaultwarden.envNettoyer
Section intitulée « Nettoyer »Détruisez toute la pile après les essais :
tofu destroyterraform destroyLa destruction ne se termine pas en une passe. Attendez-vous aux deux échecs suivants.
Videz d’abord le compartiment. Un compartiment qui contient un dépôt restic refuse d’être
supprimé, et la destruction s’arrête avec API error 403 sur zcp_object_storage_bucket. Videz-le
puis relancez :
zcp object-storage bucket empty <object_storage_slug> <your-bucket-name> \ --region os-yul --project default-9Removed 9 object/version entries from bucket "vaultwarden-restic-001024".L’IP publique est supprimée avec son réseau, pas seule. Comme l’adresse est l’IP source-NAT du
réseau, sa libération directe échoue avec
API error 403: Source Nat IP cannot be deleted from the associated network. Laissez la destruction
du réseau s’en charger :
tofu state rm zcp_ip_address.vaulttofu destroySupprimer le réseau libère son adresse. Confirmez qu’il ne reste rien de facturable :
zcp instance list --region yul-1 --project default-9zcp network list --region yul-1 --project default-9zcp ip list --region yul-1 --project default-9zcp object-storage list --region os-yul --project default-9Dépannage
Section intitulée « Dépannage »Tout sur la VM expire, y compris apt-get update. Le réseau ne possède aucune règle de sortie.
La VM ne peut donc joindre ni l’archive de paquets, ni GitHub, ni le point de terminaison du
stockage d’objets. Vérifiez avec zcp egress list --network <network-slug> --region yul-1. Une
liste vide est le problème. Appliquez les trois ressources zcp_egress_rule de l’étape 3. Une VM
sur un réseau créé automatiquement ne rencontre pas ce cas, d’où l’absence de sortie dans les
tutoriels CLI.
SSH expire juste après l’application. Les règles de pare-feu et de redirection s’activent de
façon asynchrone et prennent environ trente secondes. Confirmez qu’elles sont toutes deux Active :
zcp firewall list --ip <ip-slug> --region yul-1 --project default-9zcp portforward list --ip <ip-slug> --region yul-1 --project default-9Une liste de pare-feu vide juste après la création d’une règle signifie généralement qu’elle n’est pas encore active. Cela ne signifie pas forcément qu’elle a échoué.
SSH expire toujours et les règles sont Active. Vérifiez les redirections de ports. Le NAT
statique sur l’IP source-NAT du réseau ne fait silencieusement rien. Une configuration qui utilise
zcp_ip_association au lieu de zcp_port_forward laisse la VM inaccessible tout en indiquant un
succès.
API error 500: Private end port is required. Il manque public_end_port ou private_end_port
à un zcp_port_forward. Définissez-les à la même valeur que les ports de début.
Le minuteur nocturne échoue mais le script fonctionne manuellement. RESTIC_CACHE_DIR manque
dans /etc/restic/vaultwarden.env. systemd exécute les services sans $HOME.
Fatal: unable to open config file ou une erreur de compartiment introuvable. L’URL du dépôt
désigne un compartiment inexistant, souvent parce que le suffixe numérique de la plateforme a été
omis. Listez les noms réels avec
zcp object-storage bucket list <object_storage_slug> --region os-yul.
wrong password or no key found. Le mot de passe du dépôt ne correspond pas. Vérifiez que
RESTIC_PASSWORD_FILE pointe vers le fichier généré et que ce fichier n’a pas été modifié depuis.
Les téléversements échouent avec InvalidArgument. Le chiffrement par défaut est activé sur le
compartiment. Désactivez-le avec
zcp object-storage bucket encryption disable <object_storage_slug> <your-bucket-name> et vérifiez
le réglage actuel avec encryption status.
Étapes suivantes
Section intitulée « Étapes suivantes »- Clés d’accès au stockage d’objets et Utilisation de l’API S3
- Gérer ZCP avec Terraform ou OpenTofu pour le cycle de vie du fournisseur de façon isolée
- Vaultwarden sur le Marketplace pour la configuration de l’image et les notes de renforcement pour la production
- Sauvegardes et instantanés d’instances pour la récupération d’une VM entière en complément de cette approche au niveau des fichiers
- Documentation restic pour les politiques, les dépôts distants et le montage d’un dépôt comme système de fichiers