Skip to main content

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.
A real domain name is required

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.

Dedicated PBX admin account

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

  1. Verifies it is running as root, from /opt/vodia-billing, with the archive fully extracted, and that .env does not already exist.
  2. Checks that none of DOMAIN, MONGO_URI, DB_NAME, JWT_SECRET, ENCRYPTION_KEY, ADMIN_EMAIL, ADMIN_PASSWORD or PORT are 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.
  3. Creates a 2 GB swap file if there is no swap.
  4. Verifies DNS.
  5. Installs Docker Engine and the Compose plugin if absent.
  6. Generates a 64-character JWT_SECRET and a separate 64-character ENCRYPTION_KEY.
  7. Writes .env with mode 600.
  8. Runs docker compose up -d --build and waits for /api/health.
  9. 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

ENCRYPTION_KEY cannot be recovered

/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.