Actual Budget on AWS User Guide
Overview
This 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 Actual sync server (@actual-app/sync-server) is installed with npm on the Node.js 22 LTS runtime and is run by an unprivileged actual system account under a systemd service that starts it on boot and restarts it on failure. The same process serves both the multi-device sync API and the bundled web client, so a browser is all your users need.
The server listens on the loopback address 127.0.0.1:5006 by design and is never exposed directly. nginx is installed as a reverse proxy on port 80 that forwards every request to the server, with a raised upload limit for budget-file sync. Visitors reach the application on the standard HTTP port.
Actual protects the whole instance with a single server password. On the first boot of every deployed instance a one-shot service generates a fresh 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.
The persistent data - the account.sqlite database that holds the hashed server password and sessions, plus the server-files and user-files that store your budgets - lives under /var/lib/actual on a dedicated, independently resizable EBS data volume kept separate from the operating system disk.
The default security group for this listing opens port 22 (SSH) and port 80 (HTTP) only.
Important - secure context. Actual's web client uses
SharedArrayBuffer, which browsers only enable in a secure context: over HTTPS, or over plain HTTP only when the host islocalhost. If you browse to the instance over plainhttp://<public-ip>/you will see a "Fatal Error" aboutSharedArrayBuffer. 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 tolocalhost. 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.
- Recommended instance type:
t3.smallor larger.
Connecting to your instance
SSH in 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 and choose Continue to Subscribe, then 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 for it to reach 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
SSH to the instance and read the credentials file written on first boot. It is mode 0600 and owned by root, so read it with sudo:
sudo cat /root/actual-budget-credentials.txt
You will see 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=bqccNHwWsnd6tXJBBGrb04eD8zkl
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 both services are active and listening:
systemctl is-active actual-budget.service nginx.service
active
active
Confirm 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 has been 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 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/. You are presented with the sign-in screen. Enter the server password from Step 3.

Step 6 - Create or import a budget
After signing in you can start a fresh budget, view a demo, or import an existing one from Actual, YNAB or a nYNAB export. Once a budget is open you land 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, choose to connect to your own server, enter https://<your-domain>/ (or the tunnel URL) and the server password, and your budget is kept 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. 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 interactively (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 + 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/cloudimg-actual-budget |
The nginx reverse-proxy site configuration. |
Confirm the data tier is on its own volume:
df -h /var/lib/actual | tail -1
/dev/nvme1n1 20G 108K 19G 1% /var/lib/actual
Step 9 - Service management
systemctl status actual-budget.service --no-pager
Restart 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 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. Because the data lives on a dedicated EBS volume, you can grow it with the AWS console or CLI and then extend the filesystem, with no change to the OS disk.
Support
cloudimg 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.