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.