Installation
Vodia Billing installs on a fresh Ubuntu 24.04 LTS server with one command.
Requirements
- Ubuntu 24.04 LTS, 2 vCPU, 4 GB RAM, 40 GB disk. 2 GB RAM works; the installer creates a 2 GB swap file if none exists.
- A resolvable domain name with an A record pointing at this server.
- Ports 80 and 443 reachable from the internet.
- A Vodia PBX with an administrator account for Vodia Billing to use.
Vodia Billing serves invoice share links to your customers, receives OAuth
callbacks from Xero and QuickBooks Online, obtains a Let's Encrypt
certificate, and binds passkeys to its hostname. None of that works on a bare
IP address or on localhost, so the installer refuses to continue without
working DNS.
Create a dedicated administrator account on your Vodia PBX for Vodia Billing. To restrict its use, whitelist the Vodia Billing server's IP address on the PBX so that only this server can authenticate with those credentials.
Install
apt-get install -y wget unzip
cd /opt
wget https://cdn.vodia.net/vodia-billing/vodia-billing.zip
unzip vodia-billing.zip
cd vodia-billing
bash install.sh
The installer must run from /opt/vodia-billing and refuses to run anywhere
else, because the upgrade command, the backup instructions and this
documentation all assume that path.
What the installer asks
Domain name — the hostname Vodia Billing will be reached on, for example
billing.example.com. The installer resolves it and compares the result
against this server's public IP. If it does not resolve, or resolves
elsewhere, the install stops. Create the A record first.
Admin email — your login username, and the seeded super admin account.
Admin password — minimum twelve characters, entered twice.
Everything else is generated or defaulted.
What the installer does
- Verifies it is running as root, from
/opt/vodia-billing, with the archive fully extracted, and that.envdoes not already exist. - Checks that none of
DOMAIN,MONGO_URI,DB_NAME,JWT_SECRET,ENCRYPTION_KEY,ADMIN_EMAIL,ADMIN_PASSWORDorPORTare exported in your shell. Docker Compose reads exported shell variables ahead of.env, and a stray value from an unrelated project on the same server would silently override this configuration. - Creates a 2 GB swap file if there is no swap.
- Verifies DNS.
- Installs Docker Engine and the Compose plugin if absent.
- Generates a 64-character
JWT_SECRETand a separate 64-characterENCRYPTION_KEY. - Writes
.envwith mode 600. - Runs
docker compose up -d --buildand waits for/api/health. - Adds firewall rules for SSH, 80 and 443. Existing rules are left intact, so an install alongside Vodia Analytics does not disturb it.
Installation takes two to four minutes on a typical VPS, most of it the container build.
First login
Open https://your-domain and sign in with the admin email and password.
Two-factor authentication is required. The first login routes straight to 2FA setup: enroll an authenticator app such as Google Authenticator, Authy or 1Password, or register a passkey. After that, login is password plus six-digit code, or passkey alone.
Back up .env immediately
/opt/vodia-billing/.env holds ENCRYPTION_KEY. It decrypts every PBX
administrator password, every Xero refresh token, every QuickBooks Online
refresh token, and the SMTP password stored in MongoDB.
If that file is lost, those credentials cannot be recovered. Every PBX system and every accounting connection has to be re-entered and re-authorised by hand. Changing the key has the same effect.
Copy .env somewhere off this server before you do anything else, and back up
the whole server on a schedule.
JWT_SECRET and ENCRYPTION_KEY are generated as two independent values on
purpose. Vodia Billing will fall back to using JWT_SECRET as the encryption
key if ENCRYPTION_KEY is unset, which means rotating the session secret on
such a box would orphan every stored credential. A fresh install never has
that problem, and you should never remove ENCRYPTION_KEY from .env.
Configuring the PBX
Once Vodia Billing is running, point the PBX at it. In the PBX administration interface, find the CDR URL setting and append Vodia Billing to the existing value rather than replacing it. The field is space-delimited:
https://analytics.example.com/cdr https://billing.example.com/cdr
If Vodia Analytics is already receiving CDRs, it keeps receiving them. Both systems get every record.
Then verify ingestion: Spend → ingest counters should start moving within a
few calls. The system field in each incoming CDR is a hash identifying the
PBX, and Vodia Billing needs to know it. If you do not have it, send one test
CDR and read the value from the ingest counters.
Verifying the install
curl https://your-domain/api/health
A healthy response looks like this:
{
"status": "ok",
"mongo": "up",
"mode": "standalone",
"ingest": { "accepted": 41, "unknown_system": 0 },
"version": "1.0.0-beta"
}
unknown_system counting up means CDRs are arriving but the system ID has
not been configured yet.
Uninstalling
cd /opt/vodia-billing
docker compose down -v # -v also deletes the MongoDB volume
cd / && rm -rf /opt/vodia-billing
-v destroys all billing data irreversibly. Omit it to keep the database
volume for a later reinstall.