How to Self-Host Kill Bill with Docker Compose
What Is Kill Bill?
Kill Bill is an open-source subscription billing and payment platform. It handles recurring billing, invoicing, payment processing, and revenue recognition — the kind of billing infrastructure that companies like Stripe Billing, Chargebee, and Zuora charge thousands per month for. Kill Bill powers billing for organizations processing millions in transactions. Self-hosting it gives you full control over your billing data and zero per-transaction fees.
Updated February 2026: Verified with latest Docker images and configurations.
Prerequisites
- A Linux server (Ubuntu 22.04+ recommended)
- Docker and Docker Compose installed (guide)
- 4 GB of free RAM (minimum)
- 10 GB of free disk space
- A domain name (recommended for production use)
Docker Compose Configuration
Create a docker-compose.yml file:
services:
killbill:
image: killbill/killbill:0.24.16
container_name: killbill
restart: unless-stopped
ports:
- "8080:8080"
environment:
# Database connection
- KILLBILL_DAO_URL=jdbc:postgresql://db:5432/killbill
- KILLBILL_DAO_USER=killbill
- KILLBILL_DAO_PASSWORD=changeme_kb_password
# Catalog and overdue config (optional)
- KILLBILL_CATALOG_URI=SpyCarAdvanced.xml
# JVM tuning — adjust based on available RAM
- JAVA_OPTS=-Xms512m -Xmx1g
volumes:
- killbill-logs:/var/lib/killbill/logs
depends_on:
db:
condition: service_healthy
networks:
- killbill-net
healthcheck:
test: ["CMD", "curl", "-f", "http://localhost:8080/1.0/healthcheck"]
interval: 30s
timeout: 10s
retries: 10
start_period: 120s
kaui:
image: killbill/kaui:3.0.23
container_name: kaui
restart: unless-stopped
ports:
- "9090:8080"
environment:
# Kaui database connection
- KAUI_DB_ADAPTER=postgresql
- KAUI_DB_URL=jdbc:postgresql://db:5432/kaui
- KAUI_DB_USERNAME=killbill
- KAUI_DB_PASSWORD=changeme_kb_password
# Kill Bill API connection
- KAUI_KILLBILL_URL=http://killbill:8080
- KAUI_KILLBILL_API_KEY=bob
- KAUI_KILLBILL_API_SECRET=lazar
depends_on:
killbill:
condition: service_healthy
networks:
- killbill-net
db:
image: postgres:16-alpine
container_name: killbill-db
restart: unless-stopped
environment:
- POSTGRES_USER=killbill
- POSTGRES_PASSWORD=changeme_kb_password
volumes:
- db-data:/var/lib/postgresql/data
- ./init-db.sql:/docker-entrypoint-initdb.d/init-db.sql
networks:
- killbill-net
healthcheck:
test: ["CMD-SHELL", "pg_isready -U killbill"]
interval: 10s
timeout: 5s
retries: 5
volumes:
killbill-logs:
db-data:
networks:
killbill-net:
Create an init-db.sql file to initialize both databases:
-- Kill Bill requires separate databases for the app and Kaui
CREATE DATABASE killbill;
CREATE DATABASE kaui;
-- Grant permissions
GRANT ALL PRIVILEGES ON DATABASE killbill TO killbill;
GRANT ALL PRIVILEGES ON DATABASE kaui TO killbill;
Important: Change changeme_kb_password to a strong password in all three services. The KAUI_KILLBILL_API_KEY and KAUI_KILLBILL_API_SECRET are the default tenant credentials — you’ll create your own tenant in the initial setup.
Start the stack:
docker compose up -d
Kill Bill takes 60–120 seconds to initialize on first startup (database migrations run automatically). Watch logs:
docker compose logs -f killbill
Wait until you see Kill Bill server started.
Initial Setup
- Access Kaui at
http://your-server:9090 - Log in with the default credentials: admin / password
- Create a tenant — Kill Bill is multi-tenant. Click “Create New Tenant”:
- API Key: choose something meaningful (e.g.,
production) - API Secret: generate a strong secret
- Name: your company name
- API Key: choose something meaningful (e.g.,
- Upload a catalog — the catalog defines your products, plans, and pricing. Kill Bill ships with a sample
SpyCarAdvanced.xmlcatalog. For production, you’ll create your own. - Change the default admin password immediately under Account Settings.
Configuration
Catalog (Product/Plan Definitions)
Kill Bill’s catalog is an XML file that defines your billing model:
<?xml version="1.0" encoding="UTF-8"?>
<catalog xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:noNamespaceSchemaLocation="CatalogSchema.xsd">
<effectiveDate>2026-03-01T00:00:00+00:00</effectiveDate>
<catalogName>MyBilling</catalogName>
<currencies>
<currency>USD</currency>
</currencies>
<products>
<product name="Standard">
<category>BASE</category>
</product>
</products>
<rules>
<changePolicy>
<changePolicyCase>
<policy>IMMEDIATE</policy>
</changePolicyCase>
</changePolicy>
</rules>
<plans>
<plan name="standard-monthly">
<product>Standard</product>
<finalPhase type="EVERGREEN">
<duration><unit>UNLIMITED</unit></duration>
<recurring>
<billingPeriod>MONTHLY</billingPeriod>
<recurringPrice>
<price><currency>USD</currency><value>29.99</value></price>
</recurringPrice>
</recurring>
</finalPhase>
</plan>
</plans>
<priceLists>
<defaultPriceList name="DEFAULT">
<plans>
<plan>standard-monthly</plan>
</plans>
</defaultPriceList>
</priceLists>
</catalog>
Upload via API:
curl -X POST \
http://localhost:8080/1.0/kb/catalog \
-u admin:password \
-H "X-Killbill-ApiKey: production" \
-H "X-Killbill-ApiSecret: your-secret" \
-H "Content-Type: application/xml" \
-H "X-Killbill-CreatedBy: admin" \
-d @catalog.xml
Payment Plugins
Kill Bill supports payment gateways via plugins. Popular options:
- Stripe:
killbill-stripe-plugin - PayPal:
killbill-paypal-express-plugin - Braintree:
killbill-braintree-plugin - Adyen:
killbill-adyen-plugin
Install plugins by adding them to the Kill Bill container or via the plugin API.
Email Notifications
Configure SMTP for invoice and payment notifications:
KB_org_killbill_billing_util_email_smtp_host=smtp.example.com
KB_org_killbill_billing_util_email_smtp_port=587
KB_org_killbill_billing_util_email_smtp_useSSL=true
KB_org_killbill_billing_util_email_smtp_userName=user@example.com
KB_org_killbill_billing_util_email_smtp_password=smtp-password
Reverse Proxy
For production, put Kill Bill behind a reverse proxy. Example Nginx configuration:
# Kaui admin UI
server {
listen 443 ssl;
server_name billing.example.com;
location / {
proxy_pass http://localhost:9090;
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header X-Forwarded-Proto $scheme;
}
}
# Kill Bill API (restrict access)
server {
listen 443 ssl;
server_name billing-api.example.com;
location / {
proxy_pass http://localhost:8080;
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
# Restrict to trusted IPs
allow 10.0.0.0/8;
deny all;
}
}
Backup
Back up the PostgreSQL database regularly:
# Dump both databases
docker exec killbill-db pg_dump -U killbill killbill > killbill-backup.sql
docker exec killbill-db pg_dump -U killbill kaui > kaui-backup.sql
# Compress
gzip killbill-backup.sql kaui-backup.sql
Key volumes to back up:
db-data— all billing data, invoices, payments, subscriptionskillbill-logs— audit trail
Troubleshooting
Kill Bill won’t start (database connection refused)
Symptom: Connection refused errors in Kill Bill logs.
Fix: Ensure the PostgreSQL container is healthy before Kill Bill starts. The depends_on with condition: service_healthy in the Compose file handles this. If using an external database, verify the connection URL, username, and password.
Kaui shows “Kill Bill server is not available”
Symptom: Kaui loads but can’t connect to Kill Bill.
Fix: Kill Bill takes 60–120 seconds to fully start. Wait for the healthcheck to pass. Verify KAUI_KILLBILL_URL points to the correct internal address (http://killbill:8080).
Invoice generation fails
Symptom: Subscriptions exist but no invoices generated. Fix: Check that a valid catalog is uploaded for the tenant. Invoices are generated on the billing date — if you just created the subscription, the first invoice may not appear until the next billing cycle. Force an invoice dry-run via API to verify.
Out of memory errors
Symptom: Kill Bill crashes with Java heap errors.
Fix: Increase JVM heap in JAVA_OPTS. Default is -Xmx1g. For production with 1,000+ active subscriptions, use -Xmx2g or higher.
Plugin installation fails
Symptom: Payment plugin won’t load after installation. Fix: Plugins must match the Kill Bill version. Check plugin compatibility at the Kill Bill plugin registry. Restart Kill Bill after plugin installation.
Resource Requirements
- RAM: 2 GB minimum (Kill Bill + Kaui + PostgreSQL). 4 GB recommended for production.
- CPU: 2 cores minimum. Payment processing and invoice generation are CPU-intensive during batch runs.
- Disk: 2 GB for application. Database grows with transaction volume — plan 1 GB per 100,000 invoices.
Verdict
Kill Bill is the real deal for subscription billing. It handles complex scenarios — proration, plan changes, add-ons, usage-based billing, multi-currency, multi-tenancy — that simpler tools like Invoice Ninja and SolidInvoice can’t touch. The trade-off is complexity: the catalog XML, plugin system, and API-first design mean Kill Bill has a steep learning curve.
Use Kill Bill if you’re running a SaaS product or subscription business and need production-grade billing infrastructure. It replaces Stripe Billing, Chargebee, or Zuora at a fraction of the cost.
Use Invoice Ninja instead if you need straightforward invoicing for freelance or small business work. Kill Bill is overkill for sending invoices to clients.
Related
Get self-hosting tips in your inbox
Get the Docker Compose configs, hardware picks, and setup shortcuts we don't put in articles. Weekly. No spam.
Comments