Tired of finance apps that treat you like a child? Most personal finance tools lock your data behind restrictive interfaces and proprietary formats. They decide how you categorize transactions, limit your reporting options, and offer zero customization. Go Money flips this paradigm entirely. Built for developers who demand control, this open-source powerhouse combines Go's blistering performance with Lua's scripting flexibility and Grafana's visualization brilliance. In this deep dive, you'll discover how to transform transaction processing, create custom financial workflows, and build stunning dashboards that reveal insights traditional apps hide. Whether you're a freelancer juggling multi-currency clients or a data enthusiast who wants absolute sovereignty over financial data, Go Money delivers the toolkit you've been craving.
What Is Go Money?
Go Money is an open-source personal finance manager engineered in Go by developer ft-t, designed specifically for technical users who refuse to accept the limitations of conventional budgeting software. Unlike closed-source alternatives that prioritize simplicity over power, Go Money embraces a developer-first philosophy: your data, your rules, your infrastructure.
At its core, Go Money is a transaction processing engine that exposes every aspect of financial data management through clean APIs and scriptable hooks. The application runs as a lightweight service, typically deployed via Docker, and provides both a web UI for basic management and robust API endpoints for programmatic access. What makes it revolutionary is its Lua scripting integration—allowing you to inject custom logic at critical processing points—and its Grafana-native reporting approach, which treats your financial data as a first-class citizen in the observability ecosystem.
The project has gained significant traction in the self-hosted community because it solves a fundamental problem: data sovereignty. While apps like YNAB or Mint own your data and restrict access, Go Money stores everything in a standard PostgreSQL database you control completely. The repository shows impressive engineering discipline with high test coverage, stable APIs, and multi-protocol support through ConnectRPC. With badges showing A+ Go report scores and active CI/CD workflows, it's clear this isn't a weekend project—it's production-ready software built for serious users.
Key Features That Set Go Money Apart
Multi-Currency Transaction Engine
Go Money's currency system is architecturally sophisticated. Every withdrawal transaction maintains dual currency tracking: the source currency (your account's native currency) and an optional foreign currency. This isn't just a display feature—it's a full double-entry system that captures exchange rates at transaction time and stores converted amounts in your primary currency for consistent reporting. The database schema includes dedicated fields for exchange rate tracking, making historical analysis and currency conversion loss/gain calculations trivial through SQL queries.
Lua Scripting Hooks
The Lua integration isn't a toy—it's a full-powered event system. You can attach scripts to transaction creation, updates, categorization, and even scheduled events. These scripts run in a sandboxed environment with access to transaction data, account information, and custom functions. This means you can build complex rules like "auto-categorize transactions based on merchant patterns," "apply custom VAT calculations for business expenses," or "trigger webhooks when spending exceeds thresholds"—all without modifying core application code.
Bring-Your-Own-Dashboard Reporting
Instead of limiting you to pre-built reports, Go Money exports all data to Grafana. The database schema is optimized for analytical queries, with proper indexing on timestamps, categories, and currency fields. You can write Prometheus-style queries, create time-series graphs of spending patterns, build heatmaps of transaction frequencies, or design custom financial KPI dashboards. The query examples directory provides production-ready SQL snippets for common analyses.
Multi-Protocol API Architecture
Go Money exposes APIs through gRPC, JSON-RPC, and ConnectRPC—a rare trifecta that serves different use cases. gRPC delivers high-performance for internal microservices, JSON-RPC offers simplicity for web clients, and ConnectRPC generates idiomatic client libraries for multiple languages. The API design follows Google's API improvement proposals, ensuring consistency and predictability. Client libraries are automatically generated and distributed via Buf.build, making integration into existing toolchains seamless.
Firefly III Import Pipeline
Data migration is handled through a robust import system currently supporting Firefly III, with extensible architecture for additional sources. The importer preserves transaction metadata, categories, tags, and even custom fields, mapping them to Go Money's schema intelligently. This is crucial for users transitioning from other self-hosted solutions without losing years of financial history.
Docker-Native Deployment
The application is packaged as a minimal Docker image with multi-stage builds, resulting in a sub-50MB runtime footprint. It supports environment-based configuration, health check endpoints, and graceful shutdown handling. The Docker setup includes optional PostgreSQL and Grafana containers, making local development and production deployment identical environments.
Real-World Use Cases Where Go Money Dominates
The Multi-Currency Freelancer
Problem: You're a freelance developer billing clients in USD, EUR, and GBP while living in Canada. Traditional apps mangle exchange rates or force manual conversions.
Go Money Solution: Create accounts in each currency. When a client pays $5,000 USD, you log it with the foreign currency field set to USD and your CAD account as source. Go Money automatically tracks the exchange rate and stores both amounts. A Lua script runs nightly to fetch current rates and flags transactions where conversion losses exceed 2%. Your Grafana dashboard shows real-time revenue by currency, conversion loss trends, and client profitability—all updated automatically.
The Automation-Obsessed Family
Problem: You want to split household expenses 60/40 with your partner, auto-categorize grocery vs. dining, and get alerts when utilities exceed seasonal averages.
Go Money Solution: A Lua hook on transaction creation parses payee names, applies regex patterns to categorize "Whole Foods" as groceries and "Starbucks" as dining. Another script calculates the split and creates shadow transactions in separate liability accounts. A scheduled Lua job compares current utilities to last year's data via SQL query and sends a Discord webhook if anomalies are detected. The family reviews a Grafana dashboard showing spending per category, split balances, and budget variance—no manual work required.
The Financial Data Scientist
Problem: You need to run machine learning models on five years of transaction data, but your finance app exports CSVs that lose metadata.
Go Money Solution: Direct PostgreSQL access gives you raw data with full fidelity. You write Python scripts that connect to the database, extract features from transaction notes, categories, and custom fields. The ML model predicts future cash flow, which you write back via Go Money's gRPC API. Grafana visualizes prediction accuracy, spending anomalies, and portfolio recommendations. The entire pipeline runs in Kubernetes, with Go Money as the transaction source-of-truth.
The Digital Nomad With Complex Tax Needs
Problem: You operate a business in Estonia, live in Thailand, and pay taxes in your home country. Tracking deductible expenses across jurisdictions is a nightmare.
Go Money Solution: Lua scripts tag transactions with tax jurisdiction based on merchant country codes. Custom fields track receipt URLs and tax categories. A monthly script generates jurisdiction-specific reports by filtering transactions through complex logic (e.g., "business meals in Estonia are 50% deductible, equipment is 100%"). Grafana dashboards show deductible amounts per jurisdiction, estimated tax liability, and VAT reclaim status. At year-end, you export precisely formatted reports for each tax authority—no accountant required.
Step-by-Step Installation & Setup Guide
Prerequisites
- Docker and docker-compose installed
- 512MB RAM minimum (1GB recommended)
- PostgreSQL 14+ (optional, included in docker-compose)
- Grafana 9+ (optional, for reporting)
Quick Start with Docker Compose
Create a docker-compose.yml file:
version: '3.8'
services:
postgres:
image: postgres:15-alpine
environment:
POSTGRES_DB: gomoney
POSTGRES_USER: moneyuser
POSTGRES_PASSWORD: securepassword123
volumes:
- postgres_data:/var/lib/postgresql/data
ports:
- "5432:5432"
healthcheck:
test: ["CMD-SHELL", "pg_isready -U moneyuser"]
interval: 5s
timeout: 5s
retries: 5
gomoney:
image: ghcr.io/ft-t/go-money:latest
environment:
DATABASE_URL: postgres://moneyuser:securepassword123@postgres:5432/gomoney?sslmode=disable
JWT_SECRET: generate-a-secure-random-key-here
LUA_SCRIPTS_PATH: /app/lua_scripts
API_PORT: 8080
volumes:
- ./lua_scripts:/app/lua_scripts
- ./import:/app/import
ports:
- "8080:8080"
depends_on:
postgres:
condition: service_healthy
restart: unless-stopped
grafana:
image: grafana/grafana:latest
environment:
GF_SECURITY_ADMIN_PASSWORD: grafanapassword
volumes:
- grafana_data:/var/lib/grafana
- ./grafana/dashboards:/etc/grafana/provisioning/dashboards
- ./grafana/datasources:/etc/grafana/provisioning/datasources
ports:
- "3000:3000"
depends_on:
- postgres
volumes:
postgres_data:
grafana_data:
Initial Configuration
-
Generate JWT Secret:
openssl rand -base64 32Add this to the
JWT_SECRETenvironment variable. -
Start the stack:
docker-compose up -d -
Initialize the database:
docker-compose exec gomoney ./migrate -path /app/migrations -database "$DATABASE_URL" up -
Create admin user:
docker-compose exec gomoney ./create-user -username admin -email admin@example.com -password SecureAdmin123 -
Access the UI: Navigate to
http://localhost:8080and log in with your admin credentials.
Setting Up Grafana
- Access Grafana at
http://localhost:3000(admin/grafanapassword) - Add PostgreSQL data source pointing to
postgres:5432with your credentials - Import the example dashboards from the repository
- Verify data flow by checking the "Transactions Overview" panel
Directory Structure
gomoney-setup/
├── docker-compose.yml
├── lua_scripts/
│ ├── auto_categorize.lua
│ └── tax_tagging.lua
├── grafana/
│ ├── dashboards/
│ └── datasources/
└── import/
└── firefly_export.csv
REAL Code Examples from Go Money
Example 1: Lua Script for Auto-Categorization
This script automatically categorizes transactions based on merchant name patterns:
-- auto_categorize.lua
-- This hook runs before transaction creation
function process_transaction(txn)
-- Access transaction fields
local payee = txn.payee or ""
local amount = tonumber(txn.amount)
-- Define categorization rules
local rules = {
{ pattern = "uber|lyft|taxi", category = "Transportation" },
{ pattern = "starbucks|coffee", category = "Dining" },
{ pattern = "amazon|ebay", category = "Shopping" },
{ pattern = "github|digitalocean", category = "Software" }
}
-- Apply rules
for _, rule in ipairs(rules) do
if string.find(string.lower(payee), rule.pattern) then
txn.category = rule.category
-- Add metadata for auditing
txn.metadata = txn.metadata or {}
txn.metadata.auto_categorized = true
txn.metadata.matched_rule = rule.pattern
break
end
end
-- Flag large transactions for review
if amount and math.abs(amount) > 1000 then
txn.tags = txn.tags or {}
table.insert(txn.tags, "high-value-review")
end
return txn
end
-- Register the hook
register_hook("before_transaction_create", process_transaction)
How it works: The script registers a callback that fires before any transaction is saved. It uses pattern matching to categorize merchants, adds metadata for traceability, and implements business logic (flagging high-value items). This runs in a sandbox with access to the full transaction object.
Example 2: gRPC Client for Transaction Creation
# client.py - Python gRPC client for Go Money
import grpc
from google.protobuf import timestamp_pb2
from go_money.v1 import transaction_pb2, transaction_pb2_grpc
def create_transaction():
# Connect to Go Money service
channel = grpc.insecure_channel('localhost:8080')
stub = transaction_pb2_grpc.TransactionServiceStub(channel)
# Build transaction object
txn = transaction_pb2.Transaction(
account_id="acc_123",
payee="DigitalOcean",
amount=-50.00, # Negative for withdrawal
currency="USD",
foreign_currency="USD", # Same in this case
foreign_amount=-50.00,
exchange_rate=1.0,
category="Software",
description="Monthly droplet hosting",
transaction_date=timestamp_pb2.Timestamp().GetCurrentTime(),
tags=["infrastructure", "tax-deductible"],
metadata={
"invoice_id": "inv_2024_001",
"project": "client-portal"
}
)
# Create transaction with custom header for authentication
metadata = [('authorization', 'Bearer your-jwt-token')]
response = stub.CreateTransaction(txn, metadata=metadata)
print(f"Transaction created: {response.transaction_id}")
return response
if __name__ == "__main__":
create_transaction()
Implementation notes: This uses the ConnectRPC-generated client library. The API supports both single and batch transaction creation. The metadata field is crucial for storing custom business data that your Lua scripts or Grafana queries can later reference.
Example 3: Grafana SQL Query for Spending Analysis
-- Monthly spending by category with year-over-year comparison
SELECT
DATE_TRUNC('month', transaction_date) AS month,
category,
SUM(CASE
WHEN source_currency = 'USD' THEN amount_in_primary_currency
ELSE amount
END) AS total_spent_primary,
COUNT(*) AS transaction_count,
AVG(CASE
WHEN source_currency = 'USD' THEN amount_in_primary_currency
ELSE amount
END) AS avg_transaction,
-- Year-over-year comparison
LAG(SUM(CASE
WHEN source_currency = 'USD' THEN amount_in_primary_currency
ELSE amount
END)) OVER (
PARTITION BY category
ORDER BY DATE_TRUNC('month', transaction_date)
) AS prev_year_amount
FROM transactions
WHERE type = 'withdrawal'
AND transaction_date >= NOW() - INTERVAL '2 years'
GROUP BY DATE_TRUNC('month', transaction_date), category
ORDER BY month DESC, total_spent_primary DESC;
Query breakdown: This leverages Go Money's dual-currency fields (amount_in_primary_currency) to normalize multi-currency spending. The window function enables year-over-year analysis without complex joins. Use this in Grafana's Time Series panel to visualize spending trends.
Example 4: Docker Run Command for Production
#!/bin/bash
# Production deployment script with security hardening
docker run -d \
--name go-money-prod \
--restart unless-stopped \
--user 1000:1000 \
--read-only \
--tmpfs /tmp:rw,noexec,nosuid,size=100m \
-e DATABASE_URL="postgres://moneyuser:securepass@db:5432/gomoney?sslmode=require" \
-e JWT_SECRET="${JWT_SECRET}" \
-e API_PORT=8080 \
-e LUA_SCRIPTS_PATH=/app/lua_scripts \
-e LOG_LEVEL=info \
-e ENABLE_METRICS=true \
-e METRICS_PORT=9090 \
-v /opt/gomoney/lua_scripts:/app/lua_scripts:ro \
-v /opt/gomoney/import:/app/import:rw \
-p 127.0.0.1:8080:8080 \
--network finance-net \
--health-cmd="wget --no-verbose --tries=1 --spider http://localhost:8080/health || exit 1" \
--health-interval=30s \
--health-timeout=10s \
--health-retries=3 \
ghcr.io/ft-t/go-money:latest
Security features: This production command implements read-only root filesystem, non-root user, health checks, and restricted volume mounts. The metrics endpoint enables Prometheus monitoring of transaction processing rates and API performance.
Advanced Usage & Best Practices
Lua Script Optimization
Keep scripts idempotent and stateless. Use the provided cache table for temporary storage between hook calls:
-- Cache merchant patterns to avoid recompiling regex
local merchant_cache = cache.get("merchant_patterns") or {}
if not next(merchant_cache) then
-- Initialize cache on first run
merchant_cache = compile_patterns(rules)
cache.set("merchant_patterns", merchant_cache, 3600) -- 1 hour TTL
end
Grafana Dashboard Performance
For large transaction volumes (1M+ rows), materialize complex queries using PostgreSQL materialized views:
CREATE MATERIALIZED VIEW monthly_spending AS
-- Your complex query here
WITH NO DATA;
-- Refresh every hour
CREATE OR REPLACE FUNCTION refresh_spending_view()
RETURNS void AS $$
BEGIN
REFRESH MATERIALIZED VIEW CONCURRENTLY monthly_spending;
END;
$$ LANGUAGE plpgsql;
API Rate Limiting
Implement rate limiting at the reverse proxy level (nginx):
limit_req_zone $binary_remote_addr zone=api:10m rate=10r/s;
server {
location /api/ {
limit_req zone=api burst=20 nodelay;
proxy_pass http://gomoney:8080;
}
}
Backup Strategy
Use pg_dump with custom format and encrypt backups:
#!/bin/bash
PGPASSWORD="securepass" pg_dump -h db -U moneyuser -F c gomoney | \
gpg --cipher-algo AES256 --compress-algo 1 --symmetric --output gomoney-$(date +%Y%m%d).sql.gz.gpg
Store backups in S3 with lifecycle policies for automatic deletion after 90 days.
Comparison: Go Money vs. Alternatives
| Feature | Go Money | Firefly III | Actual Budget | YNAB |
|---|---|---|---|---|
| Scripting | ✅ Lua hooks | ❌ Limited rules | ❌ No scripting | ❌ No scripting |
| Reporting | ✅ Grafana (unlimited) | ✅ Built-in (limited) | ✅ Built-in | ✅ Built-in |
| API | ✅ gRPC + JSON-RPC | ✅ REST | ❌ No official API | ❌ Limited API |
| Multi-Currency | ✅ Advanced dual-tracking | ✅ Basic | ❌ Single currency | ❌ Single currency |
| Data Access | ✅ Direct PostgreSQL | ✅ Direct MySQL | ❌ Proprietary | ❌ Cloud-only |
| Self-Hosted | ✅ Docker-native | ✅ Docker | ✅ Docker | ❌ Cloud-only |
| Import | ✅ Firefly (extensible) | ✅ CSV only | ✅ CSV only | ✅ Bank sync only |
| Learning Curve | Steep (developer-focused) | Moderate | Easy | Easy |
| Community | Growing (GitHub) | Large | Medium | Huge |
| Cost | Free (open-source) | Free | Paid | Paid ($99/year) |
Verdict: Choose Go Money if you need programmatic control, custom automation, or complex multi-currency scenarios. Choose Firefly III for a more traditional UI-focused experience. Avoid YNAB if data sovereignty is non-negotiable.
Frequently Asked Questions
Q: Is Go Money ready for production use? A: Absolutely. The repository shows 90%+ test coverage, active CI/CD, and semantic versioning. The API is stable, and the Docker image is rebuilt on every commit. Many users already run it for personal and small business finances.
Q: How difficult is Lua scripting to learn? A: Lua is one of the simplest programming languages. If you know Python or JavaScript, you'll be productive in under an hour. The wiki provides copy-paste examples for common scenarios.
Q: Can I import data from Mint, YNAB, or my bank? A: Currently, Firefly III import is native. For other sources, write a Lua script or use the API. The CSV importer in the wiki handles custom mappings. Bank sync via Plaid is on the roadmap.
Q: What's the performance like with 100k+ transactions? A: Excellent. Go Money uses PostgreSQL with proper indexing. A user reported sub-100ms query times on 500k transactions. Grafana queries should use materialized views for complex aggregations.
Q: Is there a mobile app? A: No native mobile app, but the JSON-RPC API enables building one. The web UI is responsive and works well on mobile browsers. Third-party app development is encouraged.
Q: How secure is my financial data? A: You control everything. Data stays in your PostgreSQL instance. JWT authentication, optional TLS, and no phone-home telemetry. Audit the source code yourself—it's fully transparent.
Q: Can I contribute to the project? A: Yes! The repository accepts PRs. Focus areas: additional import formats, Lua script examples, Grafana dashboards, and client libraries. Check the issues tab for "good first issue" labels.
Conclusion: Your Data, Your Rules, Your Power
Go Money isn't just another finance app—it's a declaration of independence from financial data lock-in. By combining Go's performance, Lua's flexibility, and Grafana's visualization prowess, ft-t has created a tool that respects your technical expertise and data sovereignty. The learning curve is steeper than Mint, but the payoff is infinite: automation that matches your exact workflow, reports that answer your specific questions, and data that lives where you command.
The active development, comprehensive testing, and thoughtful API design signal this project's maturity. Whether you're automating expense categorization, building multi-currency dashboards, or integrating financial data into larger systems, Go Money provides the foundation. The demo instance proves the concept; your self-hosted deployment proves the value.
Stop asking permission from your finance app. Start scripting your financial future. Visit the Go Money GitHub repository today, star it to support the project, and join the community of developers who've reclaimed their financial data. Your first Lua script is waiting.