Finance beginner ⏱ 30 minutes

Actual Budget: Privacy-Focused Budgeting

Local-first budgeting app with sync. Zero-based budgeting and bank import support.

By Homelab Team • 2025-01-15
#actual-budget#budgeting#finance#privacy#docker

Introduction

Actual Budget is a powerful, privacy-focused personal finance application that brings zero-based budgeting to your homelab. Originally a paid service, Actual was open-sourced in 2022 and has since become one of the most popular self-hosted budgeting solutions with over 13,000 GitHub stars.

What is Zero-Based Budgeting?

Zero-based budgeting is a method where every dollar you earn is assigned a specific job before the month begins. Unlike traditional budgeting that tracks past spending, zero-based budgeting is proactive - you decide where your money goes before you spend it. This approach helps you:

  • Eliminate wasteful spending by being intentional with every dollar
  • Build emergency funds and savings systematically
  • Break the paycheck-to-paycheck cycle
  • Achieve financial goals faster through focused allocation

Why Choose Actual Budget?

Complete Financial Privacy

Your bank account data, spending habits, and financial information never leave your server. Commercial apps like Mint, YNAB, or Personal Capital aggregate your data for advertising and data broker sales. With Actual Budget, you maintain complete control.

Modern Features Without Subscriptions

YNAB (You Need A Budget) charges $99/year for similar features. Actual Budget is completely free with:

  • Real-time bank synchronization via SimpleFIN or GoCardless
  • Multi-device sync with end-to-end encryption
  • Mobile-optimized web interface
  • Powerful reporting and analytics
  • Budget templates and recurring transactions
  • Goal tracking and debt paydown planning

Active Development and Community

With regular updates, an active Discord community of 3,000+ users, and comprehensive documentation, Actual Budget continues to improve while respecting your privacy.

Prerequisites

Before starting, ensure you have:

Required

  • Docker and Docker Compose v2.0+ installed
  • 512MB RAM minimum (1GB recommended)
  • 2GB free disk space
  • Basic understanding of Docker concepts
  • SimpleFIN Bridge account for automatic bank sync (USD 1.50/month)
  • GoCardless account for European bank sync (free tier available)
  • Reverse proxy for HTTPS access (Traefik, Caddy, or Nginx Proxy Manager)
  • Mobile device for on-the-go budget access

Financial Data Preparation

Have ready:

  • Current bank account balances
  • Recent transactions for import (CSV or OFX format)
  • List of monthly bills and due dates
  • Financial goals and savings targets

Understanding Actual Budget’s Architecture

Actual Budget consists of two main components:

Web Frontend

  • React-based single-page application
  • Runs entirely in browser after initial load
  • Stores data locally with IndexedDB
  • Syncs changes to server for multi-device access

Sync Server

  • Node.js backend for data synchronization
  • Handles encryption/decryption of budget data
  • Manages user authentication
  • Stores encrypted budget files

Data Flow:

User Device → Browser (Frontend) → Sync Server → Encrypted Storage
     ↓              ↓                    ↓
  Local DB    UI Updates          Multi-device Sync

All budget data is encrypted before leaving your device, ensuring privacy even if the server is compromised.

Installation with Docker Compose

Step 1: Create Project Directory

# Create directory structure
mkdir -p ~/actual-budget
cd ~/actual-budget

# Create data directory
mkdir -p ./data

Step 2: Create Docker Compose Configuration

Create docker-compose.yml:

version: '3.8'

services:
  actual-server:
    image: actualbudget/actual-server:latest
    container_name: actual-budget
    restart: unless-stopped
    ports:
      - '5006:5006'
    volumes:
      - ./data:/data
    environment:
      # Optional: Set a password for accessing the server
      # If not set, no authentication is required
      # ACTUAL_PASSWORD: 'your-secure-password-here'

      # Optional: Configure upload limits
      ACTUAL_UPLOAD_FILE_SYNC_SIZE_LIMIT_MB: 20
      ACTUAL_UPLOAD_SYNC_ENCRYPTED_FILE_SYNC_SIZE_LIMIT_MB: 50
      ACTUAL_UPLOAD_FILE_SIZE_LIMIT_MB: 20

    labels:
      # Traefik labels (if using Traefik)
      - "traefik.enable=true"
      - "traefik.http.routers.actual.rule=Host(`budget.yourdomain.com`)"
      - "traefik.http.routers.actual.entrypoints=websecure"
      - "traefik.http.routers.actual.tls=true"
      - "traefik.http.routers.actual.tls.certresolver=letsencrypt"
      - "traefik.http.services.actual.loadbalancer.server.port=5006"
    networks:
      - actual-network

networks:
  actual-network:
    driver: bridge

Step 3: Configure Environment Variables (Optional)

Create .env file for sensitive configuration:

# Server password (optional but recommended for production)
ACTUAL_PASSWORD=your-secure-password-here

# Upload limits (adjust based on budget file size)
ACTUAL_UPLOAD_FILE_SYNC_SIZE_LIMIT_MB=20
ACTUAL_UPLOAD_SYNC_ENCRYPTED_FILE_SYNC_SIZE_LIMIT_MB=50
ACTUAL_UPLOAD_FILE_SIZE_LIMIT_MB=20

# Your domain (for reverse proxy)
DOMAIN=budget.yourdomain.com

Update docker-compose.yml to use environment file:

services:
  actual-server:
    env_file: .env
    # ... rest of configuration

Step 4: Deploy the Stack

# Start Actual Budget
docker compose up -d

# Verify container is running
docker compose ps

# Check logs for any errors
docker compose logs -f actual-server

# You should see: "Listening on 0.0.0.0:5006..."

Step 5: Initial Access and Setup

Navigate to http://YOUR-SERVER-IP:5006 in your browser.

First-Time Setup:

  1. Click “Create New Budget File”
  2. Enter budget name (e.g., “Family Budget 2025”)
  3. Set optional password for budget file encryption
  4. Choose starting month (typically current month)
  5. Click “Create”

Important: If you set ACTUAL_PASSWORD in your environment, you’ll be prompted to enter it before accessing the application.

Configuring Your First Budget

Understanding Budget Categories

Actual Budget uses category groups to organize spending:

Default Category Groups:

  • Food (Groceries, Dining Out)
  • Bills (Rent/Mortgage, Utilities, Phone)
  • Transportation (Car Payment, Gas, Insurance)
  • Personal (Clothing, Health, Entertainment)
  • Savings (Emergency Fund, Vacation, Goals)

Creating a Zero-Based Budget

Step 1: Enter Your Income

1. Click "Add Income" in current month
2. Enter paycheck amount: $3,500
3. Name: "Salary - Main Job"
4. Date: Expected payment date
5. Set as recurring if regular paycheck

Step 2: Assign Every Dollar

Category Budgeting Example (Monthly):

Income: $3,500
├─ Rent/Mortgage: $1,200
├─ Utilities: $150
├─ Groceries: $450
├─ Transportation: $300
├─ Insurance: $200
├─ Debt Payment: $400
├─ Emergency Fund: $300
├─ Entertainment: $150
├─ Dining Out: $100
├─ Clothing: $75
├─ Miscellaneous: $75
└─ Savings Goal: $100
Total: $3,500 (Zero Remaining)

The Key Rule: “To Be Budgeted” should equal $0 when done. Every dollar has a job.

Adding Accounts

Step 1: Create Bank Accounts

1. Sidebar → Accounts → "+ Add Account"
2. Select type:
   ├─ Checking (everyday spending)
   ├─ Savings (emergency fund, goals)
   ├─ Credit Card (track debt)
   ├─ Investment (retirement, stocks)
   └─ Other (mortgage, loans)
3. Enter starting balance
4. Click "Create"

Step 2: Reconcile Starting Balances

After creating accounts, reconcile them with actual bank balances:

1. Click account name
2. Click "Reconcile" button
3. Enter current bank balance
4. Review discrepancies
5. Add missing transactions
6. Mark as reconciled

Importing Transactions

From CSV or OFX Files:

1. Open account
2. Click "Import" button
3. Select file type (CSV, OFX, QFX, QIF)
4. Choose file from bank download
5. Map columns:
   ├─ Date → Transaction Date
   ├─ Payee → Merchant/Vendor
   ├─ Amount → Transaction Amount
   └─ Notes → Optional memo
6. Review imported transactions
7. Click "Import X Transactions"

CSV Format Example:

Date,Payee,Amount,Notes
2025-01-15,Grocery Store,-125.43,Weekly shopping
2025-01-14,Gas Station,-45.00,Fuel
2025-01-13,Salary,3500.00,Biweekly paycheck
2025-01-12,Electric Company,-89.50,Monthly bill

Tips for Clean Imports:

  • Use ISO date format: YYYY-MM-DD
  • Negative amounts for expenses, positive for income
  • Remove currency symbols (use -125.43, not -$125.43)
  • One transaction per line
  • Include header row

Bank Connection Setup

SimpleFIN provides secure, read-only access to 10,000+ US financial institutions.

Setup Process:

  1. Create SimpleFIN account at https://beta-bridge.simplefin.org
  2. Connect your bank accounts (uses OAuth, no credentials stored)
  3. Generate SimpleFIN access token
  4. In Actual Budget:
    Settings → Experimental → SimpleFIN
    ├─ Enable SimpleFIN
    ├─ Paste access token
    ├─ Click "Link Accounts"
    └─ Select accounts to sync

Features:

  • Automatic transaction import (daily sync)
  • No credential storage
  • OAuth-based authentication
  • Support for checking, savings, credit cards
  • Cost: USD 1.50/month per user

Security: SimpleFIN uses banking APIs, not screen scraping. Your credentials never touch Actual Budget or SimpleFIN servers.

Option 2: GoCardless (European Banks)

For European users, GoCardless provides PSD2-compliant bank access.

Setup Process:

  1. Register at https://bankaccountdata.gocardless.com
  2. Create application (free tier: 100 transactions/month)
  3. Obtain API keys (Secret ID and Secret Key)
  4. In Actual Budget:
    Settings → Experimental → GoCardless
    ├─ Enable GoCardless
    ├─ Enter Secret ID
    ├─ Enter Secret Key
    ├─ Select country
    ├─ Choose bank from list
    └─ Authorize access via bank login

Supported Countries:

  • United Kingdom, Germany, France, Spain, Italy
  • Netherlands, Belgium, Ireland, Austria
  • Nordic countries (Sweden, Denmark, Norway, Finland)
  • And 30+ more European countries

Limitations:

  • 90-day access token expiration (must re-authorize)
  • Some banks limit API access frequency
  • Free tier has transaction limits

Option 3: Manual Import

For maximum privacy or unsupported banks:

Weekly Manual Import Workflow:

# Every Sunday evening:
1. Download transactions from bank (CSV or OFX)
2. Open Actual Budget
3. Import file to each account
4. Categorize new transactions (5-10 minutes)
5. Reconcile accounts
6. Review budget performance

Benefits:

  • No third-party service dependencies
  • Works with any bank
  • Complete privacy (no API access)
  • Free

Time Investment: 15-30 minutes weekly

Data Import from Other Tools

Migrating from YNAB

Actual Budget can directly import YNAB4 budget files:

Export from YNAB:

YNAB4 (Desktop):
1. File → Export Budget Data
2. Save .ynab4 file

YNAB Web (nYNAB):
1. Account → Export Budget Data
2. Download .zip file
3. Extract to get .ynab4 format

Import to Actual:

1. File → Import File
2. Select YNAB4 file
3. Review import summary:
   ├─ Accounts matched
   ├─ Categories mapped
   ├─ Transactions imported
   └─ Budget amounts transferred
4. Click "Import"
5. Verify data accuracy

What Gets Imported:

  • All accounts with balances
  • Categories and category groups
  • Transaction history
  • Budget amounts
  • Payee names

What Doesn’t Import:

  • Goals (must recreate manually)
  • Reports/analytics
  • Scheduled transactions (create new)

Migrating from Mint

Mint allows CSV export of transactions:

Export from Mint:

1. Transactions tab
2. Filter date range (max 1 year at a time)
3. Click "Export" → "Export all X transactions"
4. Save CSV file
5. Repeat for each year if needed

Import to Actual:

1. Create accounts matching Mint accounts
2. For each account:
   ├─ Import CSV file
   ├─ Map columns (Date, Description, Amount, Category)
   ├─ Review transactions
   └─ Categorize according to your budget
3. Set starting balances to match current bank accounts
4. Begin budgeting current month

Post-Migration Tasks:

  • Create budget categories matching your Mint categories
  • Set up recurring transactions for bills
  • Establish budget amounts based on Mint spending history
  • Connect bank sync if desired

Migrating from Spreadsheets

If you track finances in Excel/Google Sheets:

Data Preparation:

Date,Account,Payee,Category,Inflow,Outflow
2025-01-01,Checking,Opening Balance,Starting Balance,2500.00,
2025-01-02,Checking,Grocery Store,Food,,125.43
2025-01-03,Checking,Employer,Income,3500.00,

Import Process:

  1. Export spreadsheet to CSV
  2. Create accounts in Actual Budget
  3. Import transactions to corresponding accounts
  4. Verify balances match your records
  5. Build budget based on historical spending

Security Best Practices for Financial Data

Encryption at Rest

Enable Full Disk Encryption:

# For Ubuntu/Debian with LUKS
sudo cryptsetup luksFormat /dev/sdX
sudo cryptsetup luksOpen /dev/sdX actual-data
sudo mkfs.ext4 /dev/mapper/actual-data

# Mount encrypted volume
sudo mount /dev/mapper/actual-data /mnt/actual-data

# Update docker-compose.yml volume path
volumes:
  - /mnt/actual-data:/data

File-Level Encryption Alternative:

# Create encrypted container with VeraCrypt or LUKS
# Mount before starting Docker container
# Unmount when not in use

Network Security

Enable HTTPS with Reverse Proxy:

Using Caddy (simplest):

budget.yourdomain.com {
    reverse_proxy localhost:5006
    tls {
        dns cloudflare YOUR_CLOUDFLARE_API_TOKEN
    }
}

Using Nginx:

server {
    listen 443 ssl http2;
    server_name budget.yourdomain.com;

    ssl_certificate /etc/letsencrypt/live/budget.yourdomain.com/fullchain.pem;
    ssl_certificate_key /etc/letsencrypt/live/budget.yourdomain.com/privkey.pem;

    location / {
        proxy_pass http://localhost:5006;
        proxy_set_header Host $host;
        proxy_set_header X-Real-IP $remote_addr;
        proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
        proxy_set_header X-Forwarded-Proto $scheme;
    }
}

Access Control

Option 1: Server Password

Set in docker-compose.yml:

environment:
  ACTUAL_PASSWORD: 'your-complex-password-here-min-16-chars'

Option 2: Reverse Proxy Authentication

Using Traefik with BasicAuth:

# Generate password hash
echo $(htpasswd -nb username password) | sed -e s/\$/\$\$/g

# Add to docker-compose.yml labels:
- "traefik.http.middlewares.actual-auth.basicauth.users=username:$$apr1$$hash$$here"
- "traefik.http.routers.actual.middlewares=actual-auth"

Option 3: VPN-Only Access

Restrict access to VPN network:

services:
  actual-server:
    ports:
      - '10.8.0.1:5006:5006'  # Bind to VPN IP only

Backup Encryption

Encrypt Backups Before Cloud Storage:

#!/bin/bash
# backup-actual-encrypted.sh

BACKUP_DIR="/home/user/actual-budget/data"
BACKUP_DEST="/backups/actual"
DATE=$(date +%Y%m%d-%H%M%S)

# Create encrypted backup
tar czf - "$BACKUP_DIR" |   gpg --symmetric --cipher-algo AES256   --output "$BACKUP_DEST/actual-backup-$DATE.tar.gz.gpg"

# Keep only last 30 days
find "$BACKUP_DEST" -name "actual-backup-*.tar.gz.gpg" -mtime +30 -delete

echo "Encrypted backup created: actual-backup-$DATE.tar.gz.gpg"

Restore Process:

# Decrypt and extract
gpg --decrypt actual-backup-20250115-120000.tar.gz.gpg | tar xzf -

Audit Logging

Monitor access to your budget:

# Enable Docker logging
docker compose logs -f actual-server | grep -i "GET|POST" >> /var/log/actual-access.log

# Review periodically for unauthorized access
grep "POST /api/login" /var/log/actual-access.log

Backup Procedures

Automated Daily Backups

Backup Script:

#!/bin/bash
# /usr/local/bin/backup-actual.sh

ACTUAL_DATA="/home/user/actual-budget/data"
BACKUP_DIR="/backups/actual"
DATE=$(date +%Y%m%d)
RETENTION_DAYS=30

# Create backup
mkdir -p "$BACKUP_DIR"
tar czf "$BACKUP_DIR/actual-budget-$DATE.tar.gz" "$ACTUAL_DATA"

# Verify backup
if [ $? -eq 0 ]; then
    echo "Backup successful: actual-budget-$DATE.tar.gz"

    # Calculate size
    SIZE=$(du -h "$BACKUP_DIR/actual-budget-$DATE.tar.gz" | cut -f1)
    echo "Backup size: $SIZE"
else
    echo "ERROR: Backup failed!"
    exit 1
fi

# Remove old backups
find "$BACKUP_DIR" -name "actual-budget-*.tar.gz" -mtime +$RETENTION_DAYS -delete

# Optional: Upload to remote location
# rclone copy "$BACKUP_DIR/actual-budget-$DATE.tar.gz" remote:backups/actual/

Schedule with Cron:

# Edit crontab
crontab -e

# Add daily backup at 2 AM
0 2 * * * /usr/local/bin/backup-actual.sh >> /var/log/actual-backup.log 2>&1

Backup to Cloud Storage

Using Rclone (supports 40+ cloud providers):

# Configure rclone (one-time setup)
rclone config

# Create remote named "backups" (Google Drive, Dropbox, etc.)

# Update backup script
rclone copy "$BACKUP_DIR/actual-budget-$DATE.tar.gz" backups:actual-budget/

Testing Backup Restoration

Quarterly Restore Test:

#!/bin/bash
# test-restore.sh

TEST_DIR="/tmp/actual-restore-test"
LATEST_BACKUP=$(ls -t /backups/actual/actual-budget-*.tar.gz | head -1)

# Create test directory
mkdir -p "$TEST_DIR"

# Extract backup
tar xzf "$LATEST_BACKUP" -C "$TEST_DIR"

# Verify critical files exist
if [ -f "$TEST_DIR/data/db.sqlite" ]; then
    echo "✓ Database file exists"
else
    echo "✗ Database file missing!"
    exit 1
fi

if [ -f "$TEST_DIR/data/files/default-budget.sqlite" ]; then
    echo "✓ Budget file exists"
else
    echo "✗ Budget file missing!"
    exit 1
fi

# Cleanup
rm -rf "$TEST_DIR"
echo "Restore test successful!"

Troubleshooting

Container Won’t Start

Check logs:

docker compose logs actual-server

# Common issues:
# - Port 5006 already in use
# - Permission denied on /data volume
# - Invalid environment variables

Solutions:

# Port conflict - change port
ports:
  - '5007:5006'  # Use different external port

# Permission fix
sudo chown -R $USER:$USER ./data
chmod 755 ./data

# Reset container
docker compose down
docker compose up -d

Budget File Won’t Sync Across Devices

Verify sync server:

# Check server logs for sync errors
docker compose logs -f actual-server | grep sync

# Test sync endpoint
curl http://YOUR-SERVER-IP:5006/health
# Should return: {"status":"ok"}

Client-side fixes:

  1. Settings → Advanced → Reset Sync
  2. Close and reopen budget file
  3. Verify correct server URL
  4. Check network connectivity

Bank Sync Not Working

SimpleFIN troubleshooting:

# Verify access token is valid
# Settings → SimpleFIN → Test Connection

# Check for expired credentials
# SimpleFIN dashboard → Connections → Refresh token

# Review sync logs
docker compose logs actual-server | grep simplefin

GoCardless troubleshooting:

# Access tokens expire after 90 days
# Settings → GoCardless → Reconnect Account

# Some banks require re-authorization more frequently
# Check GoCardless dashboard for connection status

Import Fails with CSV Error

Common CSV issues:

# Date format - use YYYY-MM-DD
# Wrong: 01/15/2025
# Right: 2025-01-15

# Currency symbols - remove all
# Wrong: -$125.43
# Right: -125.43

# Encoding - use UTF-8
# Convert with: iconv -f ISO-8859-1 -t UTF-8 input.csv > output.csv

# Line endings - use Unix (LF)
# Convert with: dos2unix transactions.csv

Slow Performance with Large Budget

Optimization steps:

# 1. Archive old transactions
# Settings → Advanced → Archive transactions older than 2 years

# 2. Increase Docker memory
docker compose down
# Edit docker-compose.yml:
deploy:
  resources:
    limits:
      memory: 1G
docker compose up -d

# 3. Clear browser cache
# Browser settings → Clear browsing data → Cached files

# 4. Compact database
# Settings → Advanced → Compact Database

Conclusion

Actual Budget provides a powerful, privacy-respecting alternative to commercial budgeting services. By self-hosting, you maintain complete control over your financial data while accessing enterprise-grade budgeting features at zero ongoing cost.

What you’ve accomplished:

  • Deployed a secure, self-hosted budgeting platform
  • Configured automatic bank synchronization (optional)
  • Implemented robust backup and security measures
  • Established a zero-based budgeting workflow

Next steps:

  • Spend first month learning your spending patterns
  • Adjust budget categories based on actual expenses
  • Set up financial goals and tracking
  • Explore reporting features for spending insights
  • Consider adding Actual Budget mobile access via VPN

Additional resources:

  • Official documentation: https://actualbudget.org/docs/
  • Community Discord: 3,000+ active users
  • GitHub repository: https://github.com/actualbudget/actual
  • Reddit: r/ActualBudget for tips and workflows

With Actual Budget running in your homelab, you’ve taken a significant step toward financial awareness and privacy. The zero-based budgeting method, combined with self-hosted control, provides a powerful foundation for achieving your financial goals.