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.

Official site

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

  1. Access Kaui at http://your-server:9090
  2. Log in with the default credentials: admin / password
  3. 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
  4. Upload a catalog — the catalog defines your products, plans, and pricing. Kill Bill ships with a sample SpyCarAdvanced.xml catalog. For production, you’ll create your own.
  5. 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;
    }
}

Reverse Proxy Setup

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, subscriptions
  • killbill-logs — audit trail

Backup Strategy

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.

Comments