From Code to Budget: An Engineer's Take on Actual
Let's dive into Actual, a fantastic personal finance app, from a software engineer's perspective. It's not just a tool for tracking your spending; it's a great example of a modern, local-first application, and understanding how it works can be incredibly useful for your own projects.
Actual is a self-hosted personal finance application. The core concept is "local-first," which means your data is primarily stored on your own device. This is a big deal for a few reasons
Data Privacy and Ownership
As engineers, we often work with sensitive data. Actual's local-first model gives you full control over your financial data. You're not entrusting a third-party with your personal information. This is a great lesson in building secure and privacy-focused applications.
Offline Functionality
Because the data is local, Actual works perfectly offline. For engineers who might be traveling or working in areas with poor internet connectivity, this is a huge plus. It also demonstrates how to build resilient applications that aren't constantly dependent on a network connection.
Performance
Local data access is almost always faster than fetching data from a remote server. This results in a snappy, responsive user experience. It's a great example of optimizing for performance by minimizing network latency.
Extensibility and Customization
Since you're self-hosting, you have the ability to inspect and even modify the code. This is invaluable for an engineer. You can create custom reports, integrate it with other services, or even contribute to the project itself. It's a living codebase that you can learn from.
Actual is designed to be easy to get up and running. The most common and recommended way to install it is using Docker, which is a great skill for any software engineer to have.
Here's a step-by-step guide
Prerequisites
Make sure you have Docker installed on your machine. You can find installation instructions for your OS on the official Docker website.
Create a docker-compose.yml file
Create a new directory for your Actual instance, and inside it, create a file named docker-compose.yml. This file will define the services needed to run Actual.
version: '3.7'
services:
actual:
image: actualbudget/actual-server:latest
container_name: actual
ports:
- "5006:5006"
volumes:
- actual_data:/data
restart: unless-stopped
volumes:
actual_data:
image: actualbudget/actual-server:latest
This specifies the Docker image to use. latest means it will pull the most recent version of the Actual server.
ports: - "5006:5006"
This maps port 5006 on your host machine to port 5006 inside the container. This is how you'll access the web interface.
volumes: - actual_data:/data
This is crucial. It persists your data. actual_data is a named volume that will be created and managed by Docker. This ensures your financial data isn't lost when the container is stopped or restarted.
Run the container
Open your terminal, navigate to the directory where you saved docker-compose.yml, and run the following command
docker-compose up -d
up
This command starts the services defined in your docker-compose.yml file.
-d
This runs the containers in "detached" mode, meaning they will run in the background.
Access Actual
Once the containers are up and running, open your web browser and go to http://localhost:5006. You'll be greeted with the setup screen to create your first budget.
Actual provides a GraphQL API, which is a modern and powerful way to interact with an application's data. Understanding and using GraphQL is a fantastic skill for any engineer.
Let's imagine you want to programmatically add a transaction to your budget. Here's a simple example using curl to demonstrate how you would do it. You could easily adapt this to any programming language (e.g., Node.js, Python, etc.)
First, you'll need to create an API key from within the Actual UI. Go to Settings > API Access to generate one.
Then, you can use a command like this in your terminal
curl -X POST \
http://localhost:5006/api/v1/graphql \
-H 'Content-Type: application/json' \
-H 'X-Actual-API-Key: <YOUR_API_KEY>' \
-d '{
"query": "mutation createTransaction($accountId: String!, $payee: String!, $amount: Int!) { createTransaction(accountId: $accountId, payee: $payee, amount: $amount) { id } }",
"variables": {
"accountId": "<YOUR_ACCOUNT_ID>",
"payee": "Coffee Shop",
"amount": -500
}
}'
X-Actual-API-Key
This is the header where you put the API key you generated.
query
This is the GraphQL mutation. createTransaction is the function we're calling. We're telling it to create a transaction and asking it to return the id of the new transaction.
variables
This is where you pass the data for the transaction.
accountId
The ID of the account you want to add the transaction to. You can get this from the URL when you're viewing an account in the UI.
payee
The name of the payee.
amount
The transaction amount in milliunits (cents). So, -500 means -$5.00.
This example shows you the power of a well-designed API. You can automate tasks, build custom integrations, and analyze your data in new ways.
Actual is more than just a personal finance tool. For a software engineer, it's a masterclass in building a modern, local-first application. You can learn about
Docker and containerization
Data persistence and local storage
GraphQL API design and consumption
Building a resilient, offline-first user experience
The benefits of open-source software and self-hosting