SSD Nodes Learn Hosting plans →
Gidsen Matt ConnorDoor Matt Connor · Bijgewerkt 2026-10-30

Actual Budget zelf hosten op een VPS met Docker

Leer hoe u Actual Budget installeert op een VPS met Docker Compose. Wij behandelen de noodzakelijke HTTPS-configuratie, het databeheer, bankimport en het inrichten van backups.

Wat u gaat bouwen

Actual Budget is een zelfgehoste applicatie voor budgetbeheer op basis van het enveloppesysteem. Het is het standaardantwoord wanneer mensen op zoek zijn naar een YNAB-alternatief dat zij zelf kunnen hosten. De server bestaat uit één container, één datavolume en één HTTPS-naam. Alles wat een normaal budget nodig heeft, draait probleemloos op de kleinste VPS die u kunt huren, omdat de server hoofdzakelijk bestanden opslaat en synchroniseert.

Het is nuttig om de architectuur te begrijpen voordat u begint met typen. Het budget zelf is een SQLite-database die zich in uw browser en in elke mobiele app bevindt. De server die u gaat installeren is een synchronisatie-eindpunt: deze bevat de lijst met rekeningen, de budgetbestanden en het wijzigingslogboek waarmee een telefoon en een laptop met elkaar kunnen synchroniseren. Daarom blijft de app werken als de server offline is, en daarom verliest u uw budget niet als de server verloren gaat, zolang ten minste één client nog een kopie heeft.

Waarom de server HTTPS nodig heeft

Actual vereist HTTPS, en dit is geen formaliteit. Actual heeft een browserfunctie genaamd SharedArrayBuffer nodig om überhaupt te kunnen draaien, en browsers schakelen deze alleen in wanneer de pagina cross-origin geïsoleerd is in wat de specificatie een veilige context noemt. Een veilige context is https:// of http://localhost. Laad de applicatie vanaf http://203.0.113.10:5006 in een browser op een andere machine en de browser stelt die functie nooit beschikbaar aan de pagina, waardoor het budget niet laadt. Voor telefoons geldt geen uitzondering. De FAQ van Actual vermeldt dat er een officiële iOS-app in bètafase is die verbinding maakt met uw eigen server, en dat er nog geen officiële Android-app is. Op de meeste telefoons gebruikt u daarom dezelfde webapplicatie, geïnstalleerd op het startscherm als een Progressive Web App, en daarvoor geldt dezelfde regel voor een veilige context.

Er zijn dus twee werkbare configuraties. Plaats een geldig certificaat op een echte domeinnaam voor de container, wat deze handleiding beschrijft. Of voorzie de server van een zelfondertekend certificaat met ACTUAL_HTTPS_KEY en ACTUAL_HTTPS_CERT, zoals gedocumenteerd door het project, en accepteer op elk apparaat een browserwaarschuwing. Een gratis certificaat van Let's Encrypt kost vijf minuten tijd, kies daarom voor de eerste optie.

Actual Budget installeren met Docker Compose

Installeer eerst Docker als de server nieuw is. Als de syntax van het Compose-bestand nieuw voor u is, behandelt de gids Docker Compose-basisprincipes voor een VPS de velden die hieronder worden gebruikt.

sudo install -d -m 755 /opt/actual
sudo install -d -m 700 /opt/actual/data

Schrijf /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:/data

Drie details in dat bestand zijn van belang.

De image is actualbudget/actual-server:latest, gepubliceerd door het project op Docker Hub en gespiegeld op ghcr.io/actualbudget/actual. Er is een latest-alpine-tag voor apparaten met een laag vermogen.

De container schrijft alles onder /data. Binnenin vindt u server-files, dat account.sqlite bevat met uw inloggegevens en sessietokens, en user-files, dat de budgetbestanden zelf bevat. Mount dat pad, anders gooit de volgende docker compose pull uw budget weg. ACTUAL_DATA_DIR kan het verplaatsen, maar de standaardinstelling is prima.

De poort wordt alleen gepubliceerd op 127.0.0.1. Een kale 5006:5006 publiceert op elke interface, en Docker schrijft zijn eigen regels vóór ufw, waardoor de applicatie open zou staan voor het internet, zelfs met een deny-all firewall. Die verrassing wordt uitgelegd in waarom Docker-gepubliceerde poorten ufw omzeilen. Binden aan loopback betekent dat alleen de reverse proxy op dezelfde server deze kan bereiken.

Start het:

cd /opt/actual
docker compose up --detach
docker compose logs -f actual

Het logbestand stabiliseert zodra de server meldt dat deze luistert op poort 5006. Controleer het lokaal voordat u DNS aanpast:

curl -fsS -o /dev/null -w '%{http_code}\n' http://127.0.0.1:5006/

Een 200 betekent dat de applicatie bereikbaar is. curl: (7) Failed to connect betekent dat niets de verbinding op 127.0.0.1:5006 accepteerde. Voer docker compose ps --all uit, omdat een standaard docker compose ps alleen actieve containers toont. Als de container als afgesloten (exited) wordt weergegeven, is de gebruikelijke oorzaak een rechtenprobleem op de gemounte volume, zichtbaar als een EACCES-regel in het logbestand.

Een certificaat en een domeinnaam instellen

Wijs een A-record naar de VPS, budget.example.com, en wacht tot dit is opgelost. Installeer vervolgens nginx en vraag het certificaat aan. De handleiding Certbot op Ubuntu 24.04 met nginx beschrijft de uitgifte en de vernieuwingstimer volledig.

Het proxy-blok:

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 is de regel die vaak wordt vergeten. Bij dagelijkse synchronisatie worden alleen uw wijzigingen verzonden, maar het volledige budgetbestand wordt geüpload wanneer u voor het eerst een budget bij de server registreert en opnieuw wanneer u de synchronisatie reset. Nginx hanteert standaard een limiet van 1 MB voor de request body. Zodra het bestand groter wordt, mislukken deze volledige uploads met 413 Request Entity Too Large in het nginx-toegangslogboek, terwijl de applicatie slechts een algemene synchronisatiefout toont. De server heeft zijn eigen limieten: ACTUAL_UPLOAD_FILE_SYNC_SIZE_LIMIT_MB staat standaard op 20 en ACTUAL_UPLOAD_SYNC_ENCRYPTED_FILE_SYNC_SIZE_LIMIT_MB op 50. Stel de nginx-limiet daarom hoger in dan de waarde die voor u van toepassing is.

Herladen en testen:

sudo nginx -t && sudo systemctl reload nginx
curl -fsS -o /dev/null -w '%{http_code}\n' https://budget.example.com/

Eerste keer opstarten: het wachtwoord en uw eerste budgetbestand

Open https://budget.example.com in een browser. Het eerste scherm vraagt u om een serverwachtwoord in te stellen. Dat ene wachtwoord beveiligt de gehele server; genereer daarom een lang, willekeurig wachtwoord en bewaar dit op een plek waar u het terug kunt vinden, zoals een zelfgehoste Vaultwarden wachtwoordmanager. Standaard zijn er geen gebruikersaccounts aan te maken: de server heeft één wachtwoord, dus het delen van een budget betekent het delen van dat wachtwoord. Aparte inloggegevens zijn alleen mogelijk als u een OpenID-provider koppelt, wat Actual ondersteunt voor opstellingen met meerdere gebruikers.

Maak vervolgens een budgetbestand aan. Actual vraagt of u end-to-end encryptie wilt inschakelen. Kies ja; de server slaat dan alleen versleutelde gegevens op, wat de juiste keuze is voor financiële data op een gehuurde machine. De prijs hiervoor is reëel: het encryptiewachtwoord bereikt de server nooit, dus als u het verliest, is het bestand weg en is herstel niet mogelijk. Noteer het wachtwoord voordat u verdergaat in het scherm.

Stel uw beginsaldi in op basis van de huidige cijfers van uw bank in plaats van jaren aan historie te importeren. Envelopbudgettering werkt vooruit vanaf het geld dat u nu heeft, dus een lege historie kost u niets.

Transacties importeren

Hier is eerlijkheid belangrijker dan enthousiasme, omdat het importproces de voornaamste reden is waarom mensen afhaken bij zelfgehost budgetteren.

Handmatige invoer is de basis en werkt altijd. Voor de enveloppenmethode is dit wellicht zelfs het doel, aangezien het intypen van een aankoop ervoor zorgt dat u zich bewust bent van uw uitgaven.

Bestandsimport verwerkt de bulk. Actual leest CSV, QIF, OFX, QFX en CAMT, en elke bank exporteert ten minste een van deze formaten. Importeer per rekening vanuit het rekeningscherm en wijs de kolommen toe wanneer het bestand een CSV is.

Automatische banksynchronisatie is beschikbaar, maar vereist een externe dienst omdat de server niet zelfstandig met banken kan communiceren. Actual ondersteunt SimpleFIN Bridge voor Noord-Amerikaanse banken, Enable Banking voor Europa, Akahu voor Nieuw-Zeeland en Pluggy.ai voor Brazilië. GoCardless wordt nog steeds ondersteund, maar accepteert geen nieuwe accounts meer. U meldt zich zelf aan bij de provider, genereert inloggegevens en voegt deze toe aan de server. SimpleFIN Bridge kost per oktober 2026 15 US dollar per jaar, exclusief belasting, voor maximaal 25 instellingen; de andere aanbieders hanteren andere prijzen.

Er zijn twee beperkingen waar u rekening mee moet houden voordat u hierop vertrouwt. De API-inloggegevens staan op de server en vallen niet onder end-to-end-encryptie, omdat de server deze moet kunnen gebruiken. Daarnaast synchroniseert er niets terwijl Actual gesloten is: automatische synchronisatie staat standaard uit, en zodra u dit inschakelt, werkt het alleen terwijl de applicatie geopend is. De server pollt uw banken dus nooit uit eigen beweging.

Backups, omdat het slechts bestanden zijn

Alles wat voor u van belang is, bevindt zich onder /opt/actual/data. Er is geen exportstap en geen database-dump nodig om te scripten.

De enige valkuil is SQLite. Het kopiëren van account.sqlite terwijl de server ernaar schrijft, kan een onvoltooide transactie vastleggen. U merkt dit pas op het moment dat u probeert te herstellen. Stop de container voor de paar seconden die het kopiëren in beslag neemt:

cd /opt/actual
docker compose stop
restic -r sftp:backup@backup.example.com:/srv/restic backup /opt/actual/data
docker compose start

Plan dit in met de aanpak in restic backups on a VPS, waarin het instellen van de repository, retentie en de herstelprocedure worden behandeld. Voer de herstelprocedure uit. Een back-up die u nooit heeft hersteld, is slechts een aanname.

De automatische back-ups van Actual zelf zijn een aparte zaak en het is nuttig om hiervan op de hoogte te zijn, maar deze bestaan alleen in de desktop-app. Deze houdt tot 10 recente kopieën van het budgetbestand bij, bereikbaar via File > Load Backup. Dit dekt scenario's zoals "ik heb per ongeluk een categorie verwijderd" zonder de server aan te raken. De web-app in een browser houdt dergelijke kopieën niet bij. Als iedereen in huis de browser gebruikt, is de bovenstaande server-back-up uw enige vangnet.

De server bijwerken

cd /opt/actual
docker compose pull
docker compose up --detach

Compose maakt de container opnieuw aan op basis van de nieuwe image en koppelt hetzelfde volume opnieuw, waardoor de data behouden blijft. Werk ook de clients bij. Een client die ouder is dan de data die deze ontvangt, stopt met het toepassen van wijzigingen en toont een Update Required-melding. Maak een back-up voordat u een grote versie-upgrade uitvoert, omdat migraties bij de eerste start worden uitgevoerd en er geen pad is om terug te keren naar een oudere versie. Actual is vergevingsgezind wat betreft een zwevende latest-tag omdat de status ervan een map met bestanden is, terwijl een applicatie met een echte database dat niet is. De handleiding self-hosting Chatwoot doorloopt de vastgezette tags en de pre-upgrade dump die deze werkwijze vereist.

Wat er misgaat en wat u zult zien

De applicatie laadt, maar de synchronisatie wordt nooit voltooid. Controleer het nginx-toegangslogboek op 413. Dit betekent dat client_max_body_size te laag is ingesteld. Een 502 betekent dat nginx geen bruikbaar antwoord heeft ontvangen van de upstream. Dit komt meestal doordat de container is gestopt of niet luistert op poort 5006; controleer daarom eerst docker compose ps --all en het containerlogboek.

De applicatie laadt niet op een ander apparaat, of meldt dat SharedArrayBuffer ontbreekt. De pagina bevindt zich niet in een beveiligde context. De adresbalk toont http:// met een IP-adres of een hostnaam die niet localhost is. Los het certificaatprobleem op in plaats van dit te omzeilen.

Een melding dat het budgetbestand niet compatibel is met deze versie. Het bestand is geopend in een nieuwere versie van Actual dan de client die u momenteel gebruikt. Werk de client bij naar ten minste die versie, of herstel een back-up die is gemaakt voordat de nieuwere versie het bestand heeft gewijzigd, en laad de pagina opnieuw.

De container start in een lus opnieuw op. Lees docker compose logs actual. Een foutmelding over rechten op /data betekent dat de gekoppelde map niet beschrijfbaar is voor de gebruiker van de container. Een foutmelding over een adres dat al in gebruik is, betekent dat een ander proces al poort 5006 op de loopback-interface bezet houdt.

De eerste keer laden voelt traag aan. Het volledige budgetbestand wordt naar de browser gedownload wanneer u het opent. Dit is één grote overdracht, gevolgd door lokale leesacties. Dit is geen probleem met de servercapaciteit; het toevoegen van RAM zal hier geen verandering in brengen.

FAQ

Heeft Actual Budget HTTPS nodig om te werken?

Ja, in de praktijk wel. Actual heeft de SharedArrayBuffer-functionaliteit van de browser nodig om te kunnen draaien, en browsers schakelen deze alleen in een beveiligde context in, oftewel https:// of http://localhost. Via standaard HTTP vanaf een andere machine zal de applicatie niet laden. Er is een officiële iOS-app in bètafase en er is nog geen officiële Android-app, dus de meeste telefoons gebruiken dezelfde webapplicatie en lopen tegen dezelfde regel aan. (FAQ) Gebruik een Let's Encrypt-certificaat op een echte hostnaam, of een zelfondertekend certificaat met ACTUAL_HTTPS_KEY en ACTUAL_HTTPS_CERT als u kunt leven met een certificaatwaarschuwing op elk apparaat.

Kan Actual mijn banktransacties automatisch importeren?

Alleen via een externe dienst waar u zich zelf voor aanmeldt: SimpleFIN Bridge in Noord-Amerika, Enable Banking in Europa, Akahu in Nieuw-Zeeland of Pluggy.ai in Brazilië. GoCardless wordt ondersteund, maar accepteert geen nieuwe accounts. Deze API-inloggegevens staan op uw server en vallen niet onder de end-to-end-versleuteling. Automatische synchronisatie staat standaard uit en, eenmaal ingeschakeld, draait dit alleen terwijl Actual geopend is; er wordt dus niets op de achtergrond op de server gepolld. Voor het importeren van CSV, QIF, OFX, QFX en CAMT is geen externe partij nodig.

Wat moet ik precies back-uppen?

De gemounte datamap, die in deze handleiding /opt/actual/data is. Deze bevat server-files/account.sqlite met inloggegevens en sessies, en user-files met de budgetbestanden. Stop de container voordat u kopieert, omdat het kopiëren van een actieve SQLite-database een gedeeltelijke schrijfactie kan vastleggen. Niets anders op de server bevat statusinformatie.

Wat gebeurt er als ik het versleutelingswachtwoord verlies?

Het bestand kan niet worden hersteld. Het wachtwoord bereikt de server nooit, wat juist het doel is van end-to-end-versleuteling; er is dus geen resetmogelijkheid en geen ondersteuningstraject. Sla het wachtwoord op in een wachtwoordbeheerder op het moment dat u het bestand aanmaakt en bewaar een kopie op een plek die niet afhankelijk is van deze server.

Hoeveel servercapaciteit heeft Actual Budget nodig?

Zeer weinig. De container serveert statische assets en bestanden, en de budgetberekeningen vinden plaats in de browser. Eén gedeelde vCPU met 1 GB RAM is ruim voldoende, en de datamap voor een huishoudbudget met meerdere jaren aan historie blijft in de tientallen megabytes. Schijfbelasting komt door uw back-ups en andere containers, niet door Actual. Als u een server kiest die ook zwaardere taken moet uitvoeren, is een fotoserver meestal de bepalende factor voor de minimale specificaties. Controleer daarom hoeveel RAM PhotoPrism en Immich daadwerkelijk nodig hebben voordat u een abonnement kiest. Dezelfde logica geldt voor een mediastack: transcodering bepaalt het abonnement, terwijl een browser-frontend zoals Halcyon, dat een Jellyfin-bibliotheek verandert in een beloopbare videotheek uit de jaren 90 ongeveer net zo weinig kost als Actual.

Was dit artikel nuttig?