Actual Budget
Overview
Actual Budget is a free, open source, local-first personal finance application built around the envelope budgeting method. You give every unit of money a job across category groups, reconcile your accounts and get a precise picture of your finances. It runs as a self-hosted sync server with a bundled web app, and the same server keeps the Actual desktop and mobile clients in sync, so your financial data stays on infrastructure you control.
This SecureAnyCloud image delivers Actual Budget fully installed and configured on Ubuntu 24.04, as a Node.js sync server behind an nginx reverse proxy. A complete budgeting service is running within minutes of launch.
What you get:
- Actual Budget 26.6 (the
@actual-app/sync-server26.6.0 release, MIT), installed globally with npm on Node.js 22 LTS - nginx as a reverse proxy on port 80, forwarding to the sync server on loopback
127.0.0.1:5006 - All persistent data on a dedicated, independently resizable Azure data disk mounted at
/var/lib/actual: theaccount.sqliteserver database, plus the server-files and user-files budget blobs - A per-VM server password generated on first boot and written to a root-only file, so no default or shared login ships in the image
actual-budget.serviceandnginx.serviceas enabled, active systemd units- 24/7 SecureAnyCloud support
Actual Budget is licensed under the MIT License. This image repackages the upstream open source release with SecureAnyCloud's provisioning and support. All product and company names are trademarks or registered trademarks of their respective holders.
Prerequisites
You need an active Azure subscription, an SSH key pair, and a VNet with a subnet in the target region.
Standard_B2s (2 vCPU / 4 GiB RAM) is a good starting size. Scale up for larger households or heavy sync activity.
Configure the network security group (NSG) to allow inbound 22/tcp from your management network and 80/tcp (HTTP) from your users. Add 443/tcp if you enable HTTPS.
Step 1 - Deploy from the Azure Marketplace
Sign in to the Azure Portal, choose Create a resource, search the Marketplace for Actual Budget by SecureAnyCloud, and select Create.
On the Basics tab, pick your subscription, resource group, region and size. Under Administrator account, choose SSH public key and paste your key. Under Inbound port rules, allow SSH (22) and HTTP (80).
Select Review + create, then Create.
Step 2 - Deploy from the Azure CLI
Accept the Marketplace terms once, then create the VM:
# Accept the Marketplace terms once per subscription (required before the first CLI deploy)
az vm image terms accept --urn sac1647283583153:actual-budget-ubuntu-24-04:default:latest
az vm create \
--resource-group <your-rg> \
--name actual-budget \
--image sac1647283583153:actual-budget-ubuntu-24-04:default:latest \
--size Standard_B2s \
--admin-username azureuser \
--generate-ssh-keys \
--public-ip-sku StandardOpen HTTP to reach the web interface:
az vm open-port --resource-group <your-rg> --name actual-budget --port 80 --priority 900Step 3 - Connect to your VM
Connect to the VM over SSH as the administrator user you created:
ssh azureuser@<vm-public-ip>Step 4 - Confirm the services are running
Both services that back Actual Budget should report active. The unauthenticated /account/needs-bootstrap health endpoint answers through nginx on port 80.
systemctl is-active actual-budget nginx curl -s http://127.0.0.1/account/needs-bootstrap; echoExpect two lines of active, then a JSON body reporting "bootstrapped":true. The Actual sync server listens only on the loopback address 127.0.0.1:5006. nginx on port 80 is the only exposed application port, so the server is never reachable directly.
Step 5 - Retrieve your server password
On the first boot of every VM, a one-shot service (actual-budget-firstboot.service) starts the sync server and generates a server password that is unique to that VM. It applies the password through Actual's own bootstrap mechanism, which bcrypt-hashes it into the server database. No shared or default password ships in the image.
Read the credentials file as root:
sudo cat /root/actual-budget-credentials.txtThe file (mode 0600, root only) contains the sign-in URL (ACTUAL_URL) and the server password (ACTUAL_SERVER_PASSWORD).
You can verify the sign-in round trip from the VM's own shell. The command below reads the per-VM password and posts a real sign-in. A correct password answers HTTP 200, and the server returns a session token:
PASS=$(sudo grep '^ACTUAL_SERVER_PASSWORD=' /root/actual-budget-credentials.txt | cut -d= -f2-)
echo -n 'sign-in HTTP status -> ' curl -s -o /dev/null -w '%{http_code}\n' -X POST http://127.0.0.1/account/login \
-H 'Content-Type: application/json' \
-d "$(jq -nc --arg p "$PASS" '{loginMethod:"password",password:$p}')"The expected output is sign-in HTTP status -> 200, which confirms that the password in the file signs in.
Step 6 - Sign in and create your budget
Browse to http://<vm-public-ip>/ and enter the server password from the credentials file.
Actual offers three options: Start fresh with a new empty budget, Import my budget from an Actual, YNAB or nYNAB export, or View demo data. Choose Start fresh to begin.
Actual opens the monthly envelope budget. Assign every unit of available money to a category group, so you always know what is left to spend.
Step 7 - Add accounts and review reports
Open Accounts and add your asset accounts, such as checking, savings and credit cards. You can create a local account and enter transactions manually, import QIF/OFX/QFX files, or configure bank sync so transactions download automatically.
The Reports dashboard summarizes your finances with configurable widgets for net worth, cash flow, and income and expenses over time.
To keep budgeting on the move, install the Actual desktop or mobile app and point it at http://<vm-public-ip>/. Enter the same server password, and your budget stays in sync across every device.
Step 8 - Confirm the version, the stack and the data disk
Check the sync server version, the Node.js version and the data disk mount:
node -e "console.log('@actual-app/sync-server ' + require('/opt/actual/lib/node_modules/@actual-app/sync-server/package.json').version)"
node --version findmnt /var/lib/actualExpect @actual-app/sync-server 26.6.0, Node.js v22, and /var/lib/actual mounted from the dedicated data disk (/dev/sdc). The server database and all budget files live on that disk. It is independently resizable and survives instance replacement. Resize it from the Azure portal if you need more room.
First-boot service and security model
A one-shot actual-budget-firstboot.service runs after the network is up. It does the following, in order:
- Starts the sync server.
- Generates a fresh server password unique to the VM.
- Applies the password through the server's own
POST /account/bootstrapendpoint. - Resolves the instance address from the Azure instance metadata service.
- Writes the credentials file at mode
0600(root only). - Drops a sentinel so the service runs exactly once.
The image ships with an empty database and no usable login. The per-VM password is created on first boot. Verify the service state and the file permissions:
systemctl is-active actual-budget-firstboot.service
sudo stat -c '%a %U:%G %n' /root/actual-budget-credentials.txtExpect active, then 600 root:root /root/actual-budget-credentials.txt.
Enabling HTTPS with your own domain
The image serves the web interface over plain HTTP on port 80. For a browser-trusted certificate, point a DNS record at the VM and open port 443 in the NSG. Then install a certificate from your CA, for example Let's Encrypt:
sudo apt-get update && sudo apt-get install -y certbot python3-certbot-nginx
sudo certbot --nginx -d your-domain.example.comCertbot updates the nginx site to serve your domain certificate. Actual runs behind the proxy on loopback, so no application configuration change is needed. Your budget is then reachable over HTTPS at your domain.
Backup and maintenance
Actual stores everything under /var/lib/actual on the dedicated data disk, including the account.sqlite server database and all budget files. A backup is therefore a single directory. Snapshot the data disk and OS disk from the Azure portal for a full point-in-time copy, or archive the tree:
sudo tar -C /var/lib -czf /var/backups/actual-$(date +%F).tar.gz actualKeep the OS current with sudo apt-get update && sudo apt-get upgrade. The image ships with unattended security updates enabled.
Review the Actual Budget documentation before moving between releases, and always back up before an upgrade.
Actual also supports optional per-file end-to-end encryption, configured inside the app. Use it if you want budget data encrypted at rest with a key only you hold.
Support
This image is backed by 24/7 SecureAnyCloud support for deployment and initial configuration, retrieving the first-boot server password, multi-device sync setup, importing existing budgets from YNAB, nYNAB or Actual exports, end-to-end encryption, HTTPS and custom domain setup, performance tuning and storage administration.
Email sales@secureanycloud.com or use the live chat in the support portal. Critical issues receive a one-hour average response.
Deploy on Azure
Launch Actual Budget on Ubuntu 24.04 LTS on Azure with 24/7 support from SecureAnyCloud.
View this image
Need Help?
Our support team is available 24/7.
sales@secureanycloud.com
Overview
This SecureAnyCloud image runs the Actual Budget 26.6 sync server, the open source, local-first personal finance and envelope budgeting application, on Ubuntu 24.04 LTS. The same process serves both the multi-device sync API and the bundled web client, so a browser is all your users need.
What you get:
- The Actual sync server (
@actual-app/sync-server), installed with npm on the Node.js 22 LTS runtime - An unprivileged
actualsystem account running the server under a systemd service that starts it on boot and restarts it on failure - The server on the loopback address
127.0.0.1:5006by design, never exposed directly - nginx as a reverse proxy on port 80, forwarding every request to the server with a raised upload limit for budget-file sync
- A single server password protecting the whole instance, generated fresh on the first boot of every deployed instance
- The persistent data under
/var/lib/actualon a dedicated, independently resizable EBS data volume, separate from the operating system disk: theaccount.sqlitedatabase that holds the hashed server password and sessions, plus the server-files and user-files that store your budgets - A default security group that opens port 22 (SSH) and port 80 (HTTP) only
On first boot, a one-shot service generates the per-instance server password, applies it through the server's own bootstrap mechanism, and writes it to /root/actual-budget-credentials.txt (mode 0600, readable only by root). Two instances launched from the same AMI never share a password, and no default password ships in the image.
Important: secure context. Actual's web client uses SharedArrayBuffer, which browsers enable only in a secure context: over HTTPS, or over plain HTTP only when the host is localhost. If you browse to the instance over plain http://<public-ip>/, you see a "Fatal Error" about SharedArrayBuffer.
This is a browser security rule, not a fault in the image. The server already sends the required cross-origin isolation headers. For day-to-day use, enable HTTPS (see Step 7) or reach the server over an SSH tunnel to localhost. The Actual desktop and mobile apps connect to the sync API directly and are unaffected.
Prerequisites
- An AWS account subscribed to this product in AWS Marketplace
- An EC2 key pair in your target Region for SSH access
- A security group allowing inbound TCP 22 (SSH) from your IP and TCP 80 (HTTP) from your users
- A recommended instance type of
t3.smallor larger
Connecting to your instance
Connect over SSH as the default login user for your operating system variant, using the key pair you launched with.
OS variant Login user Example
Ubuntu 24.04 ubuntu ssh -i your-key.pem ubuntu@<instance-public-ip>
Step 1 - Launch from the AWS Marketplace console
- Open the product page in AWS Marketplace, choose Continue to Subscribe, then choose Continue to Configuration.
- Select the software version and your AWS Region, then choose Continue to Launch.
- Choose an instance type (
t3.smallor larger), your VPC subnet, the security group described above, and your EC2 key pair. - Launch the instance and wait until it reaches the running state with status checks passed.
Step 2 - Launch from the AWS CLI (alternative)
Replace the AMI id, key name, security group and subnet with your own values:
aws ec2 run-instances \
--image-id ami-xxxxxxxxxxxxxxxxx \
--instance-type t3.small \
--key-name your-key \
--security-group-ids sg-xxxxxxxx \
--subnet-id subnet-xxxxxxxx \
--metadata-options "HttpTokens=required" \
--tag-specifications 'ResourceType=instance,Tags=[{Key=Name,Value=actual-budget}]'Step 3 - Retrieve the per-instance server password
Connect to the instance over SSH and read the credentials file written on first boot. The file is mode 0600 and owned by root, so read it with sudo:
sudo cat /root/actual-budget-credentials.txtThe file shows the unique URL and server password generated for this instance:
# Actual Budget - generated on first boot by actual-budget-firstboot.service
# This server password is unique to this VM. Store it somewhere safe. ACTUAL_URL=http://203.0.113.10/ ACTUAL_SERVER_PASSWORD=<set-at-first-boot>Step 4 - Confirm the service is healthy
The sync server runs on loopback 127.0.0.1:5006, and nginx fronts it on port 80. Confirm that both services are active:
systemctl is-active actual-budget.service nginx.serviceactive activeConfirm the listeners. The server is loopback-only on 5006, and nginx is on 80:
sudo ss -tlnp | grep -E ':80 |:5006 'LISTEN 0 511 127.0.0.1:5006 0.0.0.0:* users:(("node",pid=3056,fd=22)) LISTEN 0 511 0.0.0.0:80 0.0.0.0:* users:(("nginx",...))The server's unauthenticated health endpoint, proxied through nginx, reports that the instance is bootstrapped with its per-instance password:
curl -s http://127.0.0.1/account/needs-bootstrap{"status":"ok","data":{"bootstrapped":true,"loginMethod":"password","availableLoginMethods":[{"method":"password","active":1,"displayName":"Password"}],"multiuser":false}}Step 5 - Sign in
Open the application in your browser. Because of the secure-context rule above, use HTTPS (after Step 7) or an SSH tunnel to localhost. To open a tunnel, run this command from your workstation:
ssh -i your-key.pem -N -L 5006:127.0.0.1:80 ubuntu@<instance-public-ip>Then browse to http://localhost:5006/. The sign-in screen opens. Enter the server password from Step 3.
Step 6 - Create or import a budget
After you sign in, start a fresh budget, view a demo, or import an existing budget from an Actual, YNAB or nYNAB export. An open budget lands on the monthly envelope budget view, where you give every dollar a job across category groups.
Switch to All accounts to manage accounts and reconcile transactions. Add accounts manually, import transaction files, or schedule recurring transactions.
To sync across devices, install the Actual desktop or mobile app and choose to connect to your own server. Enter https://<your-domain>/ (or the tunnel URL) and the server password, and your budget stays in sync.
Step 7 - Enable HTTPS (recommended)
For production use, terminate TLS so the web client runs in a secure context and your traffic is encrypted. There are two common options.
Option A: certbot on the instance. Point a DNS name at the instance, open port 443 in the security group, then install certbot and obtain a certificate. Run these commands interactively, because they prompt for your email and domain:
- Install the packages:
sudo apt-get update && sudo apt-get install -y certbot python3-certbot-nginx - Obtain and install the certificate:
sudo certbot --nginx -d your-domain.example.com
certbot edits the nginx site to listen on 443 with your certificate and sets up automatic renewal.
Option B: terminate TLS upstream. Place an AWS Application Load Balancer or Amazon CloudFront in front of the instance with an ACM certificate, forwarding to port 80. This keeps certificate management off the instance.
Step 8 - Configuration and data locations
Path Purpose
/etc/actual-budget/actual-budget.env Static (non-secret) server configuration: hostname, port, data-dir paths, login method.
/var/lib/actual Data volume mount: account.sqlite (hashed server password and sessions), server-files, user-files.
/root/actual-budget-credentials.txt The per-instance server password generated on first boot (mode 0600).
/etc/nginx/sites-available/sac-actual-budget The nginx reverse-proxy site configuration.
Confirm that the data tier is on its own volume:
df -h /var/lib/actual | tail -1/dev/nvme1n1 20G 108K 19G 1% /var/lib/actualStep 9 - Service management
Check the status of the sync server:
systemctl status actual-budget.service --no-pagerRestart the server or nginx after a configuration change:
- Restart the sync server:
sudo systemctl restart actual-budget.service - Restart nginx:
sudo systemctl restart nginx.service
Step 10 - Backup and maintenance
- Back up the data volume. Take regular EBS snapshots of the
/var/lib/actualvolume, or copyaccount.sqliteand theuser-filesdirectory to off-instance storage. This captures your budgets and the server password hash. - End-to-end encryption. Actual can encrypt each budget file end to end with a separate encryption password that you set inside the app. This is independent of the server password and protects your data even from the server operator.
- Keep the OS patched. Apply Ubuntu security updates regularly with
sudo apt-get update && sudo apt-get upgrade. - Resize storage. The data lives on a dedicated EBS volume, so you can grow it with the AWS console or CLI and then extend the filesystem, with no change to the OS disk.
Support
SecureAnyCloud provides 24/7 technical support for this image by email and chat, covering Actual Budget deployment, multi-device sync, importing existing budgets, end-to-end encryption, TLS termination, backups and scaling.
All product and company names are trademarks or registered trademarks of their respective holders. Use of them does not imply any affiliation with or endorsement by them.
Deploy on AWS
Launch Actual Budget on AWS with 24/7 support from SecureAnyCloud.
View on Marketplace
Need Help?
Our support team is available 24/7.
sales@secureanycloud.com