VHB

A quick guide to self-hosting Actual Budget

Set up on Apache and migrate from YNAB

“[Self-hosting] makes me reasonably independent of whatever evil scheme your local $MEGA_CORP is up to these days (hint: it’s probably a subscription).”
Christian Hollinger

For the last couple of years my wife and I have been using YNAB to manage our monthly budget. In the interest of reducing costs and ensuring data privacy—and perhaps more generally because I have come to fancy self-hosting things where possible—I decided to give Actual Budget a try.

Actual Budget, like YNAB, uses the envelope budgeting method. Indeed for anyone already using YNAB or at least considering it, the method of accounting implemented across Actual and YNAB is nearly identical, except perhaps in niche use cases.

Prerequisites

I run an Apache server, so these instructions are written for that. However, besides the reverse proxy, everything should be identical for nginx. As always, these instructions exist as much for my own reference as they do for the benefit of anyone else looking to make this move.

Further, you would need Docker installed along with Docker Compose. If you plan to access your budget via a domain as I do, rather than by specifying a port, you would need something like Certbot to handle SSL as well.

Install Actual Budget

There are multiple ways to install Actual as outlined in their docs so the one I describe here might not be your choice. However this is what I chose and what worked for me.

Set up your folder of choice

Navigate to wherever you install apps on your server. I use /opt/ so with something like

cd /opt/ && mkdir actual_budget && cd actual_budget

you have readied your development folder.

Set up Docker

Now create a docker-compose.yml file and paste the contents over from the Actual Budget sync server. So run the following—

nano docker-compose.yml

and then paste the contents from that link into the newly-created file. Then edit the line that says environment: so it is commented i.e. change environment: to #environment: and save it with Ctrl–o and Ctrl–x. This is not necessary unless you want to specify one of the environment options.

Finally run Docker Compose:

docker compose up --detach

Or you can force a pull first:

docker compose pull && docker compose up --detach

Domain, SSL and Firewall

Set up your private domain or subdomain however you usually set it up on your server. Then setup your firewall to allow two-way access via port 5006 (if you followed the steps abov as-is) or whatever port you changed to in the previous step.

Finally run certbot to issue or check your certificate (you may not need this step depending on how you run things on your server):

certbot --apache -d domain.tld

The output of this command, if successful, usually tells you your certificate locations which might be something like—

Certificate → /etc/letsencrypt/live/domain.tld/fullchain.pem
KeyFile → /etc/letsencrypt/live/domain.tld/privkey.pem

Double-check these values though as you will need them to define a reverse proxy in the next step.

Set up Apache Reverse Proxy

Set up your configuration

As a final step create your configuration file in with nano e.g.

nano /etc/apache2/sites-available/actualbudget.conf

The contents of your configuration file may vary and you can stick to whatever works for you. Just make sure you specify the right location for your SSL certificates and the correct port number. If you have no preference or if this is your first go, here is the configuration file I used:

<VirtualHost ##IP##:443>
    ServerName ##domain.tld##
    ServerAdmin ##admin@email.tld##

    SSLEngine On
    SSLCertificateFile
    ##SSL-Certificate-Path##
    SSLCertificateKeyFile
    ##SSL-KeyFile-Path##

    # Reverse Proxy
    SSLProxyEngine Off
    SSLProxyVerify none
    SSLProxyCheckPeerCN off
    SSLProxyCheckPeerName off
    SSLProxyCheckPeerExpire off
    <Location />
        ProxyPass http://localhost:5006/ retry=0 upgrade=websocket
        ProxyPassReverse http://localhost:5006/
    </Location>

    Include /etc/letsencrypt/options-ssl-apache.conf
</VirtualHost>

<VirtualHost ##IP##:80>
    ServerName ##domain.tld##
    Redirect permanent / ##https://domain.tld/##

    RewriteEngine on
    RewriteCond %{SERVER_NAME} = ##domain.tld##
    RewriteRule ^ https://%{SERVER_NAME}%{REQUEST_URI} [END,NE,R=permanent]
</VirtualHost>

Everything you need to change is within ## double hash pairs except the port number 5006 in our example.

Next, symlink the configuration file to enable it:

ln -s /etc/apache2/sites-available/actualbudget.conf /etc/apache2/sites-enabled/

Restart Apache

You will need to restart Apache for changes to take place. Run the following commands ensuring no errors crop up after each:

ln -s a2enmod proxy proxy_http proxy_ajp remoteip headers proxy_wstunnel && systemctl restart apache2
a2ensite actualbudget.conf
systemctl restart apache2

If everything went on without a hitch so far, re-run Docker with docker compose up --detach same as before.

You should now be able to access Actual Budget via your domain. You will be prompted to set up a password the first time, and you will have no username. You will also be asked if you want to import data from another app, mainly YNAB; follow the next stage of this tutorial before you pick an export file if you wish to migrate.

Migrating from YNAB

Actual Budget needs a json file to move your data over from YNAB while the latter only provides a csv file when you export from within the main app. To get the json file you need to go via the Terminal.

Get your access token

First generate a new Personal Access Token from your YNAB developer settings and copy it somewhere safely for use in the next two steps.

List your budgets

Next, list your budgets via the API as follows:

curl -H "Authorization: Bearer ##TOKEN##" https://api.youneedabudget.com/v1/budgets

Make sure to put in your Personal Access Token where it says ##TOKEN## in the line above. If you have just the one budget you should have no problem finding its id now, but if, like me, you had a dozen of them, you might have to wade through the output and get the right id value. Luckily you can just F and search for your budget by name in the terminal. You should find its id somewhere just before the name value. Copy it.

Generate your json export

You are now ready to export your data away from YNAB. Run the following command on your Terminal. Know where you run it from because the output will be saved in the same folder:

curl -H "Authorization: Bearer ##TOKEN##" https://api.youneedabudget.com/v1/budgets/##ID## --output ynabexport.json

As before remember to update the command above with your ##TOKEN## and ##ID## values from the previous two steps. You will see no output prompt when you run this but if you check your user folder in Finder you should see a ynabexport.json file there.

Upload this to your new self-hosted Actual Budget set-up and you should be good to go. For more details consult the relevant section of the Actual Budget documentation.

As of this writing Actual does not have a mobile app available everywhere, but saving your domain onto your home screen gives you a perfectly capable web app. So far I have all features (besides those under development of course) on Actual Budget working without issues and consider this a successful move.