Envelope Budgeting with ledger

To fully appreciate this tutorial, a rudimentary understanding of ledger, double-entry accounting, and envelope budgeting will be necessary.

The Goal:

  • An envelope budgeting system similar to YNAB
  • Envelopes that "roll-over" and don't reset monthly
  • Have budgeted items tied to real accounts, ie. rent money comes out of Checking
  • Once setup, the envelopes are transparent and just work

The Result:

ledger -f finances.ldg bal


ledger -f finances.ldg bal -R


Some interesting observations:

  • The total Assets value ($8100) did not change by breaking out the budgets.
  • The value of the real bank accounts diminished and was broken out and split between virtual budget accounts.
  • The value shown in the real accounts is unbudgeted

Note: If there are several asset classes (IRA, CDs, etc) it would be trivial to nest "Budget" under "Banking" so the sum of "Banking" in the budget view would the true amount of cash-on-hand. It would also be possible to nest the budget accounts, say Rent, directly under the corresponding real account, say Assets:Banking:Checking:Rent. However, if this is done, the amount of unbudgeted funds isn't immediately obvious.

Virtual Transactions

The key to making this style of budgeting work in ledger is virtual transactions. Virtual transactions are pretty cool see the docs), but the only feature used here is that they can quickly be turned off using the 'Real' -R switch. Virtual transactions are denoted by parenthesis () or brackets [] around the transaction. If a virtual transaction is enclosed in [] it must balance - the laws of double-entry book keeping are not broken with this budgeting method and [] will be used.

The Ledger File

In the following example, budgeted items are taken out of specific bank accounts as follows:

Savings Account:

  • Vacation

Checking Account:

  • Food
  • Rent
  • Gas

Let's get rolling by setting up some initial balances:

2016/10/26      Opening Balance
        Assets:Banking:Checking         $2000.00
        Assets:Banking:Savings          $100.00
        [Assets:Budget:Food]            $100.00
        [Assets:Budget:Rent]            $1000.00
        [Assets:Budget:Vacation]        $200.00
        [Assets:Budget:Gas]             $50.00
        [Assets:Banking:Checking]       $-1150.00
        [Assets:Banking:Savings]        $-200.00

The real accounts are the Checking, Savings, and Opening Equity. The virtual budget accounts are Food, Rent, Vacation, and Gas.

Viewing only the real transactions:

Starting Balance - Unbudgeted

Now, money that has been budgeted is treated as 'spent' from the real account, so the budgeted amounts for food,rent, etc. are removed from their respective real accounts with the use of a virtual transaction.

Starting Balance - Budgeted

We've virtually overdrawn the Savings account from a budgeting perspective; however, we know that there is actually $100 there when viewing only real transactions.

Okay, say a paycheck comes in, a portion goes into Checking and a portion goes into Savings.

2016/11/01      Paycheck #1
        Income:Salary                   $-1500.00
        Assets:Banking:Savings          $1000.00
        Assets:Banking:Checking         $500.00

Paycheck Image

Now, when a paycheck comes in some of it needs put into envelopes. One option is to type up a bunch of virtual transactions divvying the paycheck into envelopes. However, one of the goals is to make the budgeting as transparent as possible and constantly divvying paychecks is far from transparent. Enter automated transactions (hledger does not support automatic transactions).

An automated transaction is applied to any entry that matches the expression specified after the =, in the example below, amounts are added to the virtual budget accounts for every transaction into or out of Income:Salary.

= /Income:Salary/
	[Assets:Budget:Food]		$100.00
	[Assets:Budget:Rent]		$500.00
	[Assets:Budget:Vacation]	$200.00
	[Assets:Budget:Gas]		$100.00
	[Assets:Banking:Checking]	$-700.00
	[Assets:Banking:Savings]	$-200.00

Budget Image

As with the starting balances, once a dollar is budgeted it is considered virtually spent and subtracted from the real account (virtually!).

And you can see that the value of the Checking and Savings account went up, and the values of the Budget accounts went up, and everything balances.

Income Unbudgeted

Income Budgeted

What about an expense?

2016/11/04      Gas
        Expenses:Gas                    $40.00

Just like with Income, one option to incorporate budgeting is to manually add virtual transactions to every expense. And once again, automatic transactions will make the budgeting experience transparent. However, this time amount multipliers come into play:

= /Expenses:Gas/
        [Assets:Budget:Gas]             -1.0
        [Assets:Banking:Checking]        1.0

Expense Image

Every gas expense gets subtracted from the budget envelope and then readded back to the real account. Intuitively, this doesn't make all that much sense. But! When a dollar gets put into an envelope, it is considered spent. And in the Expense transaction, the item is paid for out of the checking account, so the dollar is really leaving the budget envelope and going to replace the dollar spent from the real account.

As you can see, the total amount of money in the checking account does go down:

Gas Unbudgeted

But when we take a look at the envelopes, the Budget:Gas has gone down, while the unbudgeted money in the Checking account has remained the same.

Gas Budgeted

Now, it may be unrealistic to assume that gas is being paid for directly by check. It is trivial to use a Liabilities:Credit Card account as a pass-through account. The Expense is charged directly to Liabilities:Credit Card and a new transaction occurs in which the credit card is paid off from the checking account.

A complete ledger file demonstrating the budgeting method can be found here.

What happens if we get a raise and don't want to mess up all of the previous allocations? Thanks to ledger's expression system, we can apply an automated transaction by date.

By appending & expr date =~ /REGEX/ to the expression matching on Income:Salary, the rule will only apply to Income:Salary transactions that also match the date REGEX. For example, the below transaction matches for all Income:Salary transactions that occur in January and February 2016.

= /Income:Salary/ & expr date =~ /2016\/0[1-2]/
	[Assets:Budget:Food]		$150.00
	[Assets:Budget:Rent]		$550.00
	[Assets:Budget:Vacation]	$250.00
	[Assets:Budget:Gas]		$150.00
	[Assets:Banking:Checking]	$-850.00
	[Assets:Banking:Savings]	$-250.00

Was this information useful? Please consider using my Amazon Affilliate URL as a way of saying thanks. Or, feel free to donate BTC (1DNwgPQMfoWZqnH78yt6cu4WukJa3h8P1f) or ETH (0xf3c4a78c24D34E111f272Ac2AC72b1f01ba52DF3).