Come installare Actual Budget su un VPS con Docker
Installa Actual Budget su un VPS con Docker Compose: volume dati, HTTPS obbligatorio nel browser, primo budget, importazioni bancarie e backup.
Cosa stai per realizzare
Actual Budget è un'app di budgeting a buste ospitata autonomamente. È la risposta abituale per chi cerca un'alternativa a YNAB che possa gestire in autonomia. Il server è costituito da un solo container, un solo volume dati e un solo nome HTTPS. Tutto ciò che serve a un normale budget funziona senza problemi sul VPS più piccolo disponibile, perché il server archivia principalmente file e li sincronizza.
È utile comprendere l'architettura prima di eseguire qualsiasi comando. Il budget è un database SQLite che risiede nel browser e in ogni app mobile. Il server che stai per installare è un endpoint di sincronizzazione: conserva l'elenco degli account, i file del budget e il registro delle modifiche che consente a uno smartphone e a un laptop di mantenere gli stessi dati. Per questo l'app continua a funzionare quando il server è inattivo. Per lo stesso motivo, la perdita del server non comporta la perdita del budget, purché almeno un client ne conservi ancora una copia.
Perché il server richiede HTTPS
Actual richiede HTTPS, e non è una formalità. Actual ha bisogno di una funzionalità del browser chiamata SharedArrayBuffer per funzionare. I browser la abilitano solo quando la pagina è isolata tra origini diverse in quello che la specifica definisce un contesto sicuro. Un contesto sicuro è https:// o http://localhost. Se si carica l'app da http://203.0.113.10:5006 in un browser su un altro computer, il browser non rende mai disponibile questa funzionalità alla pagina. Di conseguenza, il budget non viene caricato. I telefoni non fanno eccezione. La FAQ di Actual indica che un'app iOS ufficiale è in fase di beta testing e si connette al proprio server. Indica inoltre che non esiste ancora un'app Android ufficiale. Pertanto, sulla maggior parte dei telefoni si usa la stessa applicazione web, installata nella schermata iniziale come Progressive Web App, e si applica la stessa regola del contesto sicuro.
Esistono quindi 2 configurazioni praticabili. Si può installare un certificato reale su un nome reale davanti al container, come descritto in questa guida. In alternativa, si può fornire al server un certificato autofirmato con ACTUAL_HTTPS_KEY e ACTUAL_HTTPS_CERT, come documentato dal progetto, accettando un avviso del browser su ogni dispositivo. Un certificato gratuito di Let's Encrypt richiede cinque minuti. È quindi preferibile scegliere la prima opzione.
Installare Actual Budget con Docker Compose
Installare Docker per prima cosa se il server è nuovo. Se la sintassi del file Compose non è familiare, la guida Nozioni di base su Docker Compose per un VPS descrive i campi usati di seguito.
sudo install -d -m 755 /opt/actual
sudo install -d -m 700 /opt/actual/dataScrivere /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:/dataNel file ci sono tre dettagli importanti.
L'immagine è actualbudget/actual-server:latest, pubblicata dal progetto su Docker Hub e disponibile anche su ghcr.io/actualbudget/actual. Per i sistemi a bassa potenza è disponibile il tag latest-alpine.
Il container scrive tutti i dati in /data. Al suo interno sono presenti server-files, che contiene account.sqlite con le credenziali di accesso e i token di sessione, e user-files, che contiene i file dei budget. Montare quel percorso. In caso contrario, il successivo docker compose pull elimina il budget. ACTUAL_DATA_DIR consente di modificarlo, ma il valore predefinito va bene.
La porta viene pubblicata solo su 127.0.0.1. Un semplice 5006:5006 la pubblica su tutte le interfacce. Docker inserisce inoltre le proprie regole prima di ufw. Di conseguenza, l'applicazione sarebbe esposta a Internet anche con un firewall configurato per negare tutto. Il motivo è spiegato in perché le porte pubblicate da Docker bypassano ufw. Il binding sull'interfaccia di loopback consente di raggiungerla solo dal reverse proxy sullo stesso server.
Avviarlo:
cd /opt/actual
docker compose up --detach
docker compose logs -f actualIl log si stabilizza quando il server segnala di essere in ascolto sulla porta 5006. Verificare localmente prima di modificare il DNS:
curl -fsS -o /dev/null -w '%{http_code}\n' http://127.0.0.1:5006/Un 200 indica che l'applicazione sta rispondendo. curl: (7) Failed to connect indica che nessun processo ha accettato la connessione su 127.0.0.1:5006. Eseguire docker compose ps --all, perché il semplice docker compose ps elenca solo i container in esecuzione. Se il container risulta terminato, la causa più comune è un problema di permessi sul volume montato, visibile come una riga EACCES nel log.
Inserisci un certificato e un nome reale davanti all'applicazione
Punta un record A al VPS, budget.example.com, e attendi la propagazione DNS. Quindi installa nginx e rilascia il certificato. La guida Certbot su Ubuntu 24.04 con nginx descrive in dettaglio il rilascio e il timer per il rinnovo.
Il blocco 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 è la direttiva che viene più spesso dimenticata. La sincronizzazione quotidiana invia soltanto le modifiche, ma l'intero file del budget viene caricato quando registri per la prima volta un budget sul server e ogni volta che reimposti la sincronizzazione. Per impostazione predefinita, nginx limita il corpo della richiesta a 1 MB. Quando il file supera questo limite, questi caricamenti completi falliscono e nel log degli accessi di nginx compare 413 Request Entity Too Large, mentre l'applicazione mostra soltanto un errore di sincronizzazione generico. Il server applica limiti separati: ACTUAL_UPLOAD_FILE_SYNC_SIZE_LIMIT_MB è impostato per impostazione predefinita su 20 e ACTUAL_UPLOAD_SYNC_ENCRYPTED_FILE_SYNC_SIZE_LIMIT_MB su 50. Imposta quindi il limite di nginx a un valore superiore a quello applicabile al tuo caso.
Ricarica la configurazione ed esegui il test:
sudo nginx -t && sudo systemctl reload nginx
curl -fsS -o /dev/null -w '%{http_code}\n' https://budget.example.com/Prima esecuzione: la password e il primo file di budget
Apri https://budget.example.com in un browser. La prima schermata chiede di impostare una password per il server. Questa singola password protegge l’intero server, quindi generane una lunga e casuale e conservala in un luogo che potrai ritrovare, ad esempio in un gestore di password Vaultwarden self-hosted. Per impostazione predefinita non esistono account utente da creare: il server usa una sola password, quindi condividere un budget significa condividere quella password. Gli accessi separati sono possibili solo collegando un provider OpenID, che Actual supporta per le configurazioni multiutente.
Crea quindi un file di budget. Actual chiede se vuoi abilitare la crittografia end-to-end. Rispondi di sì: il server memorizzerà soltanto testo cifrato, una scelta corretta per i dati finanziari archiviati su una macchina a noleggio. Il costo è concreto: la password di crittografia non raggiunge mai il server, quindi se la perdi il file non è recuperabile e non esiste alcuna procedura di reimpostazione. Annotala prima di proseguire oltre quella schermata.
Imposta i saldi iniziali usando i valori aggiornati della tua banca invece di importare anni di cronologia. Il metodo del budget a buste procede dal denaro disponibile in questo momento, quindi non perdi nulla partendo senza uno storico.
Importare le transazioni
È qui che l’onestà conta più dell’entusiasmo, perché la gestione degli import è il motivo principale per cui molte persone abbandonano il budgeting self-hosted.
L’inserimento manuale è la base e funziona sempre. Con il metodo a buste è probabilmente proprio questo il punto, perché digitare un acquisto ti fa prestare attenzione alla spesa.
L’importazione da file gestisce la maggior parte delle transazioni. Actual legge file CSV, QIF, OFX, QFX e CAMT, e ogni banca esporta almeno uno di questi formati. Importa ogni conto dalla relativa schermata e mappa le colonne quando il file è in formato CSV.
È disponibile anche la sincronizzazione automatica con le banche, ma richiede un servizio di terze parti perché il server non può comunicare direttamente con le banche. Actual supporta SimpleFIN Bridge per le banche nordamericane, Enable Banking per l’Europa, Akahu per la Nuova Zelanda e Pluggy.ai per il Brasile. GoCardless è ancora supportato, ma non accetta nuovi account. Devi registrarti direttamente presso il provider, generare le credenziali e aggiungerle al server. SimpleFIN Bridge applica un costo di 15 dollari statunitensi all’anno più le imposte per un massimo di 25 istituti, secondo le tariffe di ottobre 2026. Gli altri provider applicano prezzi diversi.
Prima di affidarti a questa funzione, devi accettare due limitazioni. Le credenziali API risiedono sul server e non sono protette dalla crittografia end-to-end, perché il server deve utilizzarle. Inoltre, non avviene alcuna sincronizzazione mentre Actual è chiuso: la sincronizzazione automatica è disattivata per impostazione predefinita e, quando la attivi, funziona soltanto mentre l’applicazione è aperta. Il server quindi non interroga mai autonomamente le tue banche.
Backup, perché contiene solo file
Tutto ciò che ti interessa si trova in /opt/actual/data. Non è necessario esportare i dati né predisporre uno script per il dump del database.
L'unica insidia è SQLite. Se copi account.sqlite mentre il server vi sta scrivendo, potresti acquisire una transazione incompleta e accorgertene soltanto quando proverai a ripristinare il backup. Arresta il container per i pochi secondi necessari alla copia:
cd /opt/actual
docker compose stop
restic -r sftp:backup@backup.example.com:/srv/restic backup /opt/actual/data
docker compose startPianifica questa operazione seguendo l'approccio descritto in backup restic su un VPS, che tratta la configurazione del repository, la conservazione dei backup e la prova di ripristino. Esegui la prova di ripristino. Un backup che non hai mai ripristinato è solo un'ipotesi.
I backup automatici di Actual sono un'altra cosa e vale la pena conoscerli, ma sono disponibili soltanto nell'app desktop. L'app conserva fino a 10 copie recenti del file del budget, accessibili da File > Load Backup; questo consente di risolvere il caso in cui hai eliminato per errore una categoria senza intervenire sul server. L'app web nel browser non conserva copie di questo tipo. Se tutti i componenti della famiglia usano il browser, il backup del server descritto sopra è l'unico disponibile.
Aggiornamento del server
cd /opt/actual
docker compose pull
docker compose up --detachCompose ricrea il container dalla nuova immagine e ricollega lo stesso volume, pertanto i dati non vengono persi. Aggiornare anche i client. Un client più vecchio dei dati che riceve smette di applicare le modifiche e mostra un messaggio di aggiornamento richiesto (Update Required). Eseguire un backup prima di un salto di versione major, poiché le migrazioni vengono eseguite al primo avvio e non esiste una procedura di downgrade. Actual tollera l'uso di un tag latest variabile (floating) perché il suo stato è costituito da una directory di file, mentre un'applicazione che gestisce un database vero e proprio non lo permette; la guida self-hosting Chatwoot illustra l'uso di tag bloccati (pinned) e il dump pre-aggiornamento richiesto da questa buona pratica.
Cosa si rompe e cosa verrà visualizzato
L'applicazione si carica, ma la sincronizzazione non termina mai. Controllare il log degli accessi di nginx per 413. Questo indica che client_max_body_size è impostato su un valore troppo basso. Un 502 indica invece che nginx non ha ricevuto una risposta utilizzabile dall'upstream. Nella maggior parte dei casi il container è arrestato oppure non è in ascolto sulla porta 5006; controllare quindi docker compose ps --all e il log del container prima di procedere.
L'applicazione non si carica su un altro dispositivo oppure segnala che SharedArrayBuffer manca. La pagina non si trova in un contesto sicuro. La barra degli indirizzi mostrerà http:// con un indirizzo IP o un hostname diverso da localhost. Correggere il certificato invece di aggirare il problema.
Viene visualizzato un messaggio che indica che il file del budget non è compatibile con questa versione. Il file è stato aperto con una versione di Actual più recente rispetto al client attualmente in uso. Aggiornare il client almeno a quella versione oppure ripristinare un backup creato prima che la versione più recente modificasse il file, quindi ricaricarlo.
Il container si riavvia in un ciclo continuo. Leggere docker compose logs actual. Un errore di autorizzazione su /data indica che la directory montata non è scrivibile dall'utente del container. Un errore di indirizzo già in uso indica che un altro processo occupa già la porta 5006 sull'interfaccia loopback.
Il primo caricamento è lento. Quando si apre il file del budget, l'intero file viene scaricato nel browser. Si tratta di un unico trasferimento di grandi dimensioni, seguito da letture locali. Non è un problema di dimensionamento del server e aggiungere RAM non cambierà il comportamento.
FAQ
Actual Budget richiede HTTPS per funzionare?
Sì, in pratica. Actual richiede la funzionalità SharedArrayBuffer del browser e i browser la abilitano soltanto in un contesto sicuro, ovvero https:// o http://localhost. Tramite HTTP non cifrato da un altro computer, l'applicazione non viene caricata. Un'app iOS ufficiale è in fase di beta testing e non esiste ancora un'app Android ufficiale, quindi la maggior parte degli smartphone usa la stessa app web ed è soggetta alla stessa regola. (FAQ) Usa un certificato Let's Encrypt su un hostname reale oppure un certificato autofirmato con ACTUAL_HTTPS_KEY e ACTUAL_HTTPS_CERT, se puoi accettare un avviso sul certificato visualizzato su ogni dispositivo.
Actual può importare automaticamente le transazioni bancarie?
Solo tramite un servizio di terze parti a cui ti registri autonomamente: SimpleFIN Bridge in Nord America, Enable Banking in Europa, Akahu in Nuova Zelanda oppure Pluggy.ai in Brasile. GoCardless è supportato, ma non accetta nuovi account. Queste credenziali API risiedono sul tuo server e non sono coperte dalla crittografia end-to-end. La sincronizzazione automatica è disabilitata per impostazione predefinita e, una volta abilitata, viene eseguita soltanto mentre Actual è aperto, quindi sul server non viene eseguito alcun polling in background. Per importare CSV, QIF, OFX, QFX e CAMT non serve alcun servizio di terze parti.
Che cosa devo sottoporre esattamente a backup?
La directory dei dati montata, che in questa guida è /opt/actual/data. Contiene server-files/account.sqlite con gli accessi e le sessioni e user-files con i file del budget. Arresta il container prima di copiarla, perché la copia di un database SQLite in uso può includere una scrittura parziale. Sul server non ci sono altri dati persistenti.
Che cosa succede se perdo la password di crittografia?
Il file non può essere recuperato. La password non raggiunge mai il server, ed è proprio questo lo scopo della crittografia end-to-end, quindi non esiste una procedura di reimpostazione né un canale di supporto. Salvala in un password manager nel momento stesso in cui crei il file e conserva una copia in un luogo che non dipenda da questo stesso server.
Di quante risorse server ha bisogno Actual Budget?
Di pochissime. Il container distribuisce asset e file statici, mentre i calcoli del budget vengono eseguiti nel browser. Un vCPU condiviso con 1 GB di RAM è sufficiente per eseguirlo senza problemi e la directory dei dati di un budget familiare con diversi anni di cronologia rimane nell'ordine di alcune decine di megabyte. La pressione sul disco dipende dai tuoi backup e dagli altri container, non da Actual. Se devi dimensionare un server che deve eseguire anche un'applicazione più esigente, in genere è un photo server a determinare il requisito minimo; controlla quindi quanta RAM richiedono realmente PhotoPrism e Immich prima di scegliere un piano. Lo stesso ragionamento vale per uno stack multimediale: è il transcoding a determinare il piano, mentre un'interfaccia web come Halcyon, che trasforma una libreria Jellyfin in un videonoleggio anni 90 esplorabile richiede all'incirca le stesse poche risorse di Actual.