Installer Actual Budget sur un VPS avec Docker
Installez Actual Budget avec Docker Compose sur un VPS : volume de données, HTTPS obligatoire dans le navigateur, premier budget, imports bancaires et sauvegardes.
Ce que vous allez mettre en place
Actual Budget est une application de gestion budgétaire par enveloppes que vous hébergez vous-même. C’est généralement la solution choisie par les personnes qui cherchent une alternative à YNAB pouvant être hébergée sur leur propre serveur. Le serveur se compose d’un conteneur, d’un volume de données et d’un nom HTTPS. Toutes les fonctions nécessaires à un budget classique fonctionnent sans problème sur le plus petit VPS que vous pouvez louer, car le serveur stocke essentiellement des fichiers et les synchronise.
Vous devez comprendre l’architecture avant de saisir la moindre commande. Le budget lui-même est une base de données SQLite présente dans votre navigateur et dans chaque application mobile. Le serveur que vous allez installer est un endpoint de synchronisation : il contient la liste des comptes, les fichiers de budget et le journal des modifications qui permet au téléphone et à l’ordinateur portable de conserver les mêmes données. C’est pourquoi l’application continue de fonctionner lorsque le serveur est indisponible. C’est aussi pourquoi la perte du serveur n’entraîne pas la perte de votre budget, tant qu’un client en conserve encore une copie.
Pourquoi le serveur a besoin de HTTPS
Actual exige HTTPS, et ce n’est pas une formalité. Les navigateurs n’exposent la Web Crypto API, l’interface qu’Actual utilise pour son chiffrement de bout en bout, que dans ce que la spécification appelle un contexte sécurisé. Un contexte sécurisé est https:// ou http://localhost. Si vous ouvrez l’application depuis http://203.0.113.10:5006 dans un navigateur sur une autre machine, ces fonctionnalités ne sont tout simplement pas disponibles, car le navigateur ne les a jamais transmises à la page. Les versions mobiles officielles refusent également une URL de serveur en http:// non chiffrée.
Deux configurations sont donc possibles. Placez un certificat valide associé à un vrai nom de domaine devant le conteneur, comme dans ce guide. Vous pouvez aussi fournir au serveur un certificat auto-signé avec ACTUAL_HTTPS_KEY et ACTUAL_HTTPS_CERT, comme l’indique la documentation du projet, puis accepter un avertissement du navigateur sur chaque appareil. Un certificat gratuit de Let's Encrypt s’obtient en cinq minutes : choisissez donc la première option.
Installer Actual Budget avec Docker Compose
Installez d’abord Docker si le serveur est vierge. Si la syntaxe des fichiers Compose ne vous est pas familière, le guide Bases de Docker Compose pour un VPS présente les champs utilisés ci-dessous.
sudo install -d -m 755 /opt/actual
sudo install -d -m 700 /opt/actual/dataÉcrivez /opt/actual/docker-compose.yml :
services:
actual:
image: actualbudget/actual-server:latest
container_name: actual
restart: unless-stopped
ports:
- '127.0.0.1:5006:5006'
volumes:
- ./data:/dataTrois éléments de ce fichier sont importants.
L’image est actualbudget/actual-server:latest, publiée par le projet sur Docker Hub et disponible en miroir à ghcr.io/actualbudget/actual. Il existe une balise latest-alpine pour les machines peu puissantes.
Le conteneur écrit toutes ses données sous /data. Vous y trouverez server-files, qui contient account.sqlite avec vos identifiants et vos jetons de session, ainsi que user-files, qui contient les fichiers de budget eux-mêmes. Montez ce chemin, sinon l’instruction docker compose pull suivante supprime votre budget. ACTUAL_DATA_DIR permet de le déplacer, mais la valeur par défaut convient.
Le port est publié uniquement sur 127.0.0.1. Une valeur 5006:5006 seule le publie sur toutes les interfaces. Docker ajoute ses propres règles avant celles d’ufw. L’application serait donc accessible depuis Internet même avec un pare-feu qui refuse tout le trafic. Cette particularité est expliquée dans pourquoi les ports publiés par Docker contournent ufw. Une liaison sur l’interface loopback signifie que seul le reverse proxy du même serveur peut l’atteindre.
Démarrez le conteneur :
cd /opt/actual
docker compose up --detach
docker compose logs -f actualLe journal se stabilise lorsque le serveur indique qu’il écoute sur le port 5006. Vérifiez d’abord le service localement avant de modifier le DNS :
curl -fsS -o /dev/null -w '%{http_code}\n' http://127.0.0.1:5006/Un 200 signifie que l’application répond. Un curl: (7) Failed to connect signifie que le conteneur n’est pas en cours d’exécution, et docker compose ps affichera qu’il s’est arrêté. La cause habituelle est un problème de permissions sur le volume monté, visible sous la forme d’une ligne EACCES dans le journal.
Placez un certificat et un nom réel devant le service
Pointez un enregistrement A vers le VPS, budget.example.com, puis attendez sa résolution. Installez ensuite nginx et délivrez le certificat. Le guide Certbot sur Ubuntu 24.04 avec nginx explique en détail la délivrance du certificat et le timer de renouvellement.
Le bloc proxy :
server {
listen 443 ssl;
http2 on;
server_name budget.example.com;
ssl_certificate /etc/letsencrypt/live/budget.example.com/fullchain.pem;
ssl_certificate_key /etc/letsencrypt/live/budget.example.com/privkey.pem;
client_max_body_size 100m;
location / {
proxy_pass http://127.0.0.1:5006;
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;
}
}client_max_body_size est la ligne que l’on oublie souvent. Le fichier de budget est envoyé en entier lors d’une synchronisation complète. Par défaut, nginx limite le corps des requêtes à 1 MB. Une fois cette taille dépassée, la synchronisation échoue avec 413 Request Entity Too Large dans l’access log de nginx, tandis que l’application n’affiche qu’une erreur de synchronisation générique. Le serveur applique ses propres limites : ACTUAL_UPLOAD_FILE_SYNC_SIZE_LIMIT_MB vaut 20 par défaut et ACTUAL_UPLOAD_SYNC_ENCRYPTED_FILE_SYNC_SIZE_LIMIT_MB vaut 50. Définissez donc la limite nginx au-dessus de celle qui s’applique à votre configuration.
Rechargez nginx et testez :
sudo nginx -t && sudo systemctl reload nginx
curl -fsS -o /dev/null -w '%{http_code}\n' https://budget.example.com/Première exécution : le mot de passe et votre premier fichier de budget
Ouvrez https://budget.example.com dans un navigateur. Le premier écran vous demande de définir un mot de passe pour le serveur. Ce mot de passe protège l’ensemble du serveur : générez-en un long et aléatoire, puis conservez-le dans un endroit où vous pourrez le retrouver, par exemple dans un gestionnaire de mots de passe Vaultwarden auto-hébergé. Il n’y a aucun compte utilisateur à créer. Le serveur Actual utilise volontairement un seul mot de passe. Partager un budget revient donc à partager ce mot de passe.
Créez ensuite un fichier de budget. Actual vous demande si vous souhaitez activer le chiffrement de bout en bout. Répondez oui : le serveur ne stockera alors que des données chiffrées, ce qui est préférable pour des données financières sur une machine louée. Cette protection a une contrepartie réelle : le mot de passe de chiffrement n’est jamais transmis au serveur. Si vous le perdez, le fichier est perdu et aucune réinitialisation n’est possible. Notez-le avant de quitter cet écran.
Définissez vos soldes de départ à partir des montants actuels indiqués par votre banque, plutôt que d’importer des années d’historique. La méthode du budget par enveloppes part de l’argent dont vous disposez maintenant. Vous ne perdez donc rien en laissant l’historique vide.
Importer des transactions
C’est ici que l’honnêteté compte plus que l’enthousiasme, car l’import est la principale raison pour laquelle les utilisateurs abandonnent la gestion budgétaire auto-hébergée.
La saisie manuelle constitue la base et fonctionne toujours. Avec la méthode des enveloppes, c’est même probablement l’objectif : saisir un achat vous oblige à en prendre conscience.
L’import de fichiers prend en charge la majorité des opérations. Actual lit les formats CSV, QIF, OFX et QFX, et toutes les banques proposent au moins l’un de ces formats à l’export. Importez les données de chaque compte depuis l’écran du compte, associez les colonnes une seule fois, puis Actual mémorise cette disposition pour le compte.
La synchronisation automatique avec les banques existe, mais elle nécessite un service tiers, car le serveur ne peut pas communiquer directement avec les banques. Actual prend en charge SimpleFIN Bridge pour les banques nord-américaines, Enable Banking pour l’Europe, Akahu pour la Nouvelle-Zélande et Pluggy.ai pour le Brésil. GoCardless reste pris en charge, mais n’accepte plus de nouveaux comptes. Vous devez créer vous-même un compte auprès du fournisseur, générer les identifiants, puis les ajouter au serveur. En juillet 2026, SimpleFIN Bridge facture 15 dollars US par an pour un maximum de 25 établissements. Les autres fournisseurs appliquent des tarifs différents.
Vous devez accepter deux limites avant de vous appuyer sur cette fonction. Les identifiants API sont stockés sur le serveur et ne sont pas couverts par le chiffrement de bout en bout, car le serveur doit les utiliser. De plus, Actual n’interroge pas automatiquement les banques : la synchronisation se lance avec un bouton et non comme une tâche en arrière-plan.
Sauvegardes : ce ne sont que des fichiers
Tout ce qui compte se trouve sous /opt/actual/data. Il n’y a pas d’étape d’export ni de dump de base de données à automatiser.
Le seul piège concerne SQLite. Copier account.sqlite pendant que le serveur y écrit peut capturer une transaction incomplète. Vous ne le découvrirez qu’au moment de la restauration. Arrêtez le conteneur pendant les quelques secondes nécessaires à la copie :
cd /opt/actual
docker compose stop
restic -r sftp:backup@backup.example.com:/srv/restic backup /opt/actual/data
docker compose startPlanifiez cette opération en suivant la méthode décrite dans sauvegardes restic sur un VPS, qui couvre la configuration du dépôt, la conservation et le test de restauration. Effectuez ce test. Une sauvegarde que vous n’avez jamais restaurée n’est qu’une supposition.
Les sauvegardes côté client d’Actual constituent un mécanisme distinct qu’il est utile de connaître. Le navigateur conserve des copies récentes du fichier de budget, accessibles depuis le menu des fichiers. Cela permet de gérer le cas « j’ai supprimé une catégorie par erreur » sans toucher au serveur.
Mise à jour du serveur
cd /opt/actual
docker compose pull
docker compose up --detachCompose recrée le conteneur à partir de la nouvelle image et rattache le même volume : les données sont donc conservées. Mettez également les clients à jour. Les versions du serveur et de l’application doivent rester proches. Un client beaucoup plus ancien que le serveur peut refuser la synchronisation et afficher un message d’incompatibilité de version. Effectuez une sauvegarde avant un changement majeur de version, car les migrations s’exécutent au premier démarrage et il n’existe pas de procédure de rétrogradation. Actual est tolérant avec un tag latest flottant, car son état se trouve dans un répertoire de fichiers. Ce n’est pas le cas d’une application qui utilise une véritable base de données. Le guide auto-hébergement de Chatwoot décrit les tags épinglés et le dump préalable à la mise à niveau que cette pratique impose.
Ce qui peut échouer et ce que vous verrez
L’application se charge, mais la synchronisation ne se termine jamais. Consultez le journal d’accès nginx pour 413. Cela signifie que client_max_body_size est réglé trop bas. En revanche, un 502 signifie que nginx fonctionne, mais que le conteneur ne fonctionne pas.
Les options de chiffrement sont absentes ou l’application mobile refuse l’URL. La page n’est pas dans un contexte sécurisé. La barre d’adresse affiche http:// avec une adresse IP ou un nom d’hôte qui n’est pas localhost. Corrigez le certificat au lieu de contourner le problème.
Un message indique que le fichier de budget n’est pas compatible avec cette version. Les versions du client et du serveur ont divergé. Mettez les deux à jour vers la même release, puis rechargez la page.
Le conteneur redémarre en boucle. Consultez docker compose logs actual. Une erreur de permissions sur /data signifie que le répertoire monté n’est pas accessible en écriture pour l’utilisateur du conteneur. Une erreur indiquant que l’adresse est déjà utilisée signifie qu’un autre processus utilise déjà 5006 sur l’interface loopback.
Le premier chargement semble lent. L’intégralité du fichier de budget est téléchargée dans le navigateur lorsque vous l’ouvrez. Il s’agit d’un transfert important, suivi de lectures locales. Ce n’est pas un problème de dimensionnement du serveur, et ajouter de la RAM ne changera rien.
FAQ
Actual Budget a-t-il besoin de HTTPS pour fonctionner ?
Oui, en pratique. Le chiffrement de bout en bout d’Actual utilise l’API Web Crypto du navigateur, que les navigateurs n’exposent que dans un contexte sécurisé, c’est-à-dire https:// ou http://localhost. En HTTP simple depuis une autre machine, ces fonctionnalités sont indisponibles, et les applications mobiles officielles refusent l’URL d’un serveur HTTP simple. Utilisez un certificat Let's Encrypt sur un vrai nom d’hôte, ou un certificat autosigné avec ACTUAL_HTTPS_KEY et ACTUAL_HTTPS_CERT si vous utilisez uniquement un navigateur desktop.
Actual peut-il importer automatiquement mes opérations bancaires ?
Uniquement par l’intermédiaire d’un service tiers auquel vous vous inscrivez vous-même : SimpleFIN Bridge en Amérique du Nord, Enable Banking en Europe, Akahu en Nouvelle-Zélande ou Pluggy.ai au Brésil. GoCardless est pris en charge, mais n’accepte plus de nouveaux comptes. Ces identifiants d’API sont stockés sur votre serveur et ne sont pas couverts par le chiffrement de bout en bout. La synchronisation est également manuelle : vous appuyez sur un bouton et aucun polling ne s’exécute en arrière-plan. L’importation CSV, QIF, OFX et QFX ne nécessite aucun tiers.
Que dois-je sauvegarder exactement ?
Le répertoire de données monté, qui est /opt/actual/data dans ce guide. Il contient server-files/account.sqlite avec les identifiants et les sessions, ainsi que user-files avec les fichiers de budget. Arrêtez le conteneur avant la copie, car copier une base SQLite active peut capturer une écriture partielle. Aucun autre élément du serveur ne contient d’état.
Que se passe-t-il si je perds le mot de passe de chiffrement ?
Le fichier ne peut pas être récupéré. Le mot de passe n’atteint jamais le serveur, ce qui est précisément le principe du chiffrement de bout en bout. Il n’existe donc ni procédure de réinitialisation ni solution via le support. Enregistrez-le dans un gestionnaire de mots de passe dès que vous créez le fichier, et conservez une copie à un autre endroit qui ne dépend pas de ce même serveur.
De quelles ressources serveur Actual Budget a-t-il besoin ?
De très peu. Le conteneur sert des ressources et des fichiers statiques, tandis que les calculs du budget sont effectués dans le navigateur. Un vCPU partagé avec 1 GB de RAM suffit à l’exécuter sans problème, et le répertoire de données d’un budget familial contenant plusieurs années d’historique reste de l’ordre de quelques dizaines de mégaoctets. La pression sur le disque vient de vos sauvegardes et de vos autres conteneurs, pas d’Actual. Si vous dimensionnez une machine qui doit aussi exécuter un service plus exigeant, un serveur photo détermine généralement le minimum requis. Consultez donc la quantité de RAM réellement nécessaire à PhotoPrism et Immich avant de choisir une offre. La même logique s’applique à une stack multimédia : c’est le transcodage qui détermine le dimensionnement, tandis qu’une interface web comme Halcyon, qui transforme une bibliothèque Jellyfin en vidéoclub des années 90 où l’on peut se promener consomme à peu près autant de ressources qu’Actual.