Blnk
Overview
Blnk is an open-source double-entry ledger and financial core. It exposes an HTTP JSON API that records money movements between balances as immutable, balanced transactions. It enforces double-entry invariants so that every posting nets to zero, and it supports multi-currency balances, precise integer amounts and reconciliation.
This SecureAnyCloud image ships the standalone Blnk ledger on a single self-contained Ubuntu 24.04 VM on Azure. Blnk runs as two Go processes on the same VM, the API server and an async accounting worker. They are backed by a local PostgreSQL 16 database and a local Redis 7 queue.
The Blnk binary is built from the pinned upstream source at build time with a pinned Go toolchain. The optional Typesense search engine is not shipped. The core ledger runs fully without it, including creating ledgers and balances, recording transactions and reading balances.
Blnk has no web interface. It is an API server: HTTP requests in, JSON out. Everything in this guide uses curl and standard HTTP clients. Point your own application at the API.
Secure by design, deny by default. A ledger holds financial records, so this appliance refuses first.
What you get:
- The Blnk API runs in secure mode. Every route requires the per-VM secret key, sent in the
X-Blnk-Keyheader, and every request without it, read or write, is refused with HTTP 401. - PostgreSQL and Redis are bound to the loopback interface only and are never reachable off the VM. The API on TCP
5001is the only routable interface. - Nothing is baked into the image. On first boot, each VM generates a unique API secret key, a unique password for the
postgressuperuser, a unique password for the ledger database role and a unique Redis password. It writes them to the root-only file/root/blnk-credentials.txt. The shipped image contains no configuration and an empty ledger database. - Blnk v0.15.2 from the official upstream source (github.com/blnkfinance/blnk, Apache-2.0), built from the pinned tag with a pinned Go toolchain, running under systemd as
blnk-server.service(the API) andblnk-worker.service(the async accounting worker) - PostgreSQL 16 from the official PostgreSQL PGDG repository, bound to loopback, running as
postgresql.service - Redis 7 as the intake queue between the API and the worker, bound to loopback with a per-VM password, running as
redis-server.service - Per-VM secrets generated on first boot and written to a root-only credentials file, with no default login
- A small demo ledger seeded on first boot, so the appliance is live immediately
blnk-selfcheck, a helper that verifies the whole appliance end to end: loopback binding, refusal of anonymous and wrong-key requests, correct double-entry balances and the sum-zero invariant- Unattended security upgrades left enabled, so the appliance keeps receiving patches
Blnk is a trademark of Blnk Finance. This image is produced by SecureAnyCloud and is not affiliated with, endorsed by or sponsored by Blnk Finance. The name is used only to identify the open-source software that runs in the image.
Prerequisites
You need an active Azure subscription, an SSH public key, and a VNet with a subnet in the target region.
Subscribe to this listing on Azure Marketplace.
Create a Network Security Group that allows TCP 22 for administration and TCP 5001 for the Blnk API. In production, restrict 5001 to your application subnet, because the ledger holds financial data.
Recommended virtual machine size: Standard_B2s with 2 vCPU and 4 GB RAM for development and light workloads. For higher transaction rates, choose a larger size such as Standard_D2s_v5 or above.
Deploy the virtual machine
In the Azure Portal, select the image from Azure Marketplace, choose your VM size, and supply your SSH public key for the azureuser account. Alternatively, deploy from the Azure CLI. Run these commands on your own workstation, not on the VM:
# Accept the Marketplace terms once per subscription (required before the first CLI deploy)
az vm image terms accept --urn sac1647283583153:blnk-ubuntu-24-04:default:latest
az vm create \
--resource-group my-resource-group \
--name my-blnk \
--image sac1647283583153:blnk-ubuntu-24-04:default:latest \
--size Standard_B2s \
--admin-username azureuser \
--generate-ssh-keys \
--public-ip-sku Standard
# then open the API port to your client network (tighten the source range in production)
az vm open-port --resource-group my-resource-group --name my-blnk --port 5001SSH in as azureuser:
ssh azureuser@<vm-ip>Confirm the services are healthy
On first boot, the appliance generates its per-VM secrets and runs the ledger schema migrations against the empty database. It then starts the API and the worker and seeds a small demo ledger. Confirm that all four services are active:
sudo systemctl is-active postgresql redis-server blnk-server blnk-workerAll four report active. The Blnk API listens on 0.0.0.0:5001. PostgreSQL on 127.0.0.1:5432 and Redis on 127.0.0.1:6379 are loopback only.
Retrieve your per-VM credentials
Every secret is generated on first boot and written to a root-only file. Read the file with:
sudo cat /root/blnk-credentials.txtThe api.secret_key is the key you send in the X-Blnk-Key header to authenticate to the API on port 5001. The file also holds the PostgreSQL and Redis passwords, which are for loopback administration only.
Keep this file safe. The secrets are unique to your VM and appear in plain text only here.
The API is deny by default
Every request to the API needs the per-VM key. An anonymous request, or a request with the wrong key, is refused with HTTP 401 before it does anything:
# anonymous write is refused curl -s -o /dev/null -w '%{http_code}\n' -X POST -H 'Content-Type: application/json' \
-d '{"name":"x"}' http://<vm-ip>:5001/ledgers
# a wrong key is refused curl -s -o /dev/null -w '%{http_code}\n' -H 'X-Blnk-Key: wrong-key' \
-X POST -H 'Content-Type: application/json' -d '{"name":"x"}' http://<vm-ip>:5001/ledgersBoth commands print 401. With the per-VM key, the same request succeeds:
curl -s -o /dev/null -w '%{http_code}\n' -H 'X-Blnk-Key: <api.secret_key>' \
http://<vm-ip>:5001/ledgersThis command prints 200.
Create a ledger, balances and a double-entry transaction
A ledger is an isolated set of balances and transactions. Create one:
curl -s -H 'X-Blnk-Key: <api.secret_key>' -H 'Content-Type: application/json' \
-X POST http://<vm-ip>:5001/ledgers -d '{"name":"Demo Ledger"}'The response carries a ledger_id (for example ldg_...). Next, create two balances in that ledger. One represents the external world, which is the source of funds, and the other belongs to a user, alice:
curl -s -H 'X-Blnk-Key: <api.secret_key>' -H 'Content-Type: application/json' \
-X POST http://<vm-ip>:5001/balances -d '{"ledger_id":"<ledger_id>","currency":"USD"}'Each response carries a balance_id (for example bln_...). Next, post a transaction. In double-entry terms, money entering the system is debited from an external source and credited to a destination. The source is an ordinary balance that is allowed to go negative with allow_overdraft, which is the "world" leg.
This transaction moves USD 1000.00 (amount 1000 at precision 100) from the world balance into alice:
curl -s -H 'X-Blnk-Key: <api.secret_key>' -H 'Content-Type: application/json' \
-X POST http://<vm-ip>:5001/transactions \
-d '{"amount":1000,"precision":100,"currency":"USD","source":"<world_balance_id>","destination":"<alice_balance_id>","allow_overdraft":true,"reference":"fund-alice-1","description":"fund alice from world"}'The API queues the transaction in Redis, and the worker applies it to PostgreSQL within a second or two. Read the balances back:
curl -s -H 'X-Blnk-Key: <api.secret_key>' http://<vm-ip>:5001/balances/<alice_balance_id>
curl -s -H 'X-Blnk-Key: <api.secret_key>' http://<vm-ip>:5001/balances/<world_balance_id>alice shows a balance of 100000 (minor units, so USD 1000.00), and world shows -100000. Double-entry means the destination gained exactly what the source lost, so the sum across the balances is zero.
Verify the whole appliance
Re-prove the entire appliance with one command. It checks loopback binding of PostgreSQL and Redis, refusal of anonymous and wrong-key requests, a correct double-entry funding and transfer, and the sum-zero invariant:
sudo blnk-selfcheckWhere to go next
- Model your own money flows. Create a ledger per product line and a balance per account, and post a transaction for every movement. Use
allow_overdraftonly on balances that represent the outside world. - Add
meta_datato ledgers, balances and transactions to carry your domain identifiers. - Read balances, transactions and the immutable transaction log through the API at
/balances/{id},/transactionsand/transactions/{id}. - Read the full API documentation from the upstream project at docs.blnkfinance.com.
Security notes
- The API is deny by default. It runs in secure mode, so every request must carry the per-VM key in the
X-Blnk-Keyheader. PostgreSQL and Redis are loopback only and never reachable off the VM. - No default credentials. The API secret key, the
postgressuperuser password, the ledger database password and the Redis password are all generated uniquely on first boot. The shipped image has no configuration and an empty database. - Rotate the API key by generating a new value and updating
secret_keyin/etc/blnk/blnk.json. Then runsudo systemctl restart blnk-server blnk-worker. - In production, restrict TCP 5001 to your application subnet with the Azure NSG. Consider terminating TLS at a load balancer or reverse proxy in front of the API.
- Back up PostgreSQL (
sudo -u postgres pg_dump blnk) as you would any system of record. The ledger's data lives entirely in theblnkdatabase. - Unattended security upgrades are left enabled, so the OS keeps receiving patches.
Deploy on Azure
Launch Blnk on Ubuntu 24.04 on Azure with 24/7 support from SecureAnyCloud.
View this image
Need Help?
Our support team is available 24/7.
sales@secureanycloud.com