Skip to content

Repository files navigation

InvoiceRaider

Open for sort of CHANGELOGS?

V1.05 - cap-drop=all
Added the option to make use of --cap-drop=ALL as long as you specify an user in an parameter ( --user xx:xx)

V1.04 - Mail additions

  • From mail field can now be used to send mail from other aliases (as long as its within the same domain as the SMTP account has access to
  • Paid status now has the option to also send mail to allow for sending mails to Paperless-ngx or other software for archiving
  • A new email config setting has been added which enables the usage of the FROM field within the email. To be precise, when turned on it replaces your company email address with the FROM address of the email config selected. This allows for you to send mails to customers ujsing customer-specific mailaddresses which then automatically get added to our invoice based on the mailconfig used.
  • Removed the status line from the invoice when it is in Draft status. As this is mostly the status it is in the moment you send or save the invoice to get it to customers, the status is not needed. All other statusses do show in the invoice as they contribute to making sure the customer or archiving systems understand the status of the invoice when its not "normal"

V1.03 - Mail configs

  • Addition of mail configs in the settings menu
  • Addition of using mail configs to send out invoices by mail to customers or archiving purposes
  • Addition of using mail configs to send out reminders/overdue warnings by mail
  • Addition of logging per invoice on when and to who mail has been sent

V1.02 - Dashboard refresh

  • Addition of an improved dashboard with clickable elements

V1.01 - Initial QoL

  • Alligned specific settings within the page view
  • Fixed the way VAT is shown and calculated in the invoices allow it to switch correctly even when switching between products that have different VATs
  • Addition of an extra customer and company field to add country-specific company registration numbers
  • Changes addresses to be one-liners which might annoy some but in my eyes looks cleaner and is better proportional in a lot of situations
  • Allowed for things like whitelines/enters to work in most text blocks
  • More minor things

Formerly a fork of https://github.com/kittendevv/Invio, however at this point enough functionality has been added and removed to make it its own project. No changes made in the Invio project will be introduced in here anymore and it will go its own way.

Disclaimers:

AI is responsible for over half of the coding. Also keep in mind that this software is mostly developed for personal use by myself and thus might not receive all feature requests desired.

################################################################

A modern, self-hosted invoice management platform for freelancers and small to medium-sized businesses. Built with a SvelteKit frontend and a Deno/Hono backend, packaged as a single Docker container.


πŸ“Έ Click to show screenshots

Dashboard Invoice Customer Mail

Features

  • Invoice Management β€” Create, edit, and track invoices with draft/sent/paid/overdue/voided statuses and direct email delivery
  • Customer Database β€” Store contact info, tax IDs, company details, and country codes
  • Product Catalog β€” Define products and services with pricing, units, SKUs, and tax categories
  • Template Engine β€” Multiple built-in templates (Professional Modern, Minimalist Clean, Slate, Nova) with custom upload support
  • Export Formats β€” PDF, UBL 2.1, Factur-X (ZugFeRD), FatturaPA, and PEPPOL-compatible XML
  • Multi-User & Permissions β€” Role-based access control with a fine-grained resource/action permission matrix
  • Two-Factor Authentication β€” TOTP (authenticator app) with recovery codes
  • OpenID Connect β€” Optional OIDC integration for SSO
  • Email Sending β€” Send invoices via SMTP with PDF attachments; supports multiple email configurations, per-config normal/reminder templates, and status-aware reminder emails
  • Multi-Currency & Tax β€” Per-invoice currency, per-line tax rates, inclusive/exclusive pricing, flexible rounding
  • Themes β€” Light and dark themes via daisyUI

Tech Stack

Layer Technology
Frontend SvelteKit 2, Vite 8, Tailwind CSS 4, daisyUI 5
Frontend runtime Bun
Backend Deno 2 + Hono 4
Database SQLite 3 (via Deno)
PDF generation WeasyPrint
Auth JWT + TOTP
Packaging Docker + Supervisord

Quick Start (Docker Compose)

# docker-compose.yml
services:
  invoiceraider:
    image: ghcr.io/gittimeraider/invoiceraider:latest
    ports:
      - "8000:8000"
    volumes:
      - invoiceraider_data:/app/data
    environment:
      JWT_SECRET: change-me-to-a-long-random-string
      ADMIN_USER: admin
      ADMIN_PASS: changeme

volumes:
  invoiceraider_data:
docker compose up -d

Then open http://localhost:8000 and log in with your configured admin credentials.


Environment Variables

Variable Required Default Description
JWT_SECRET Yes β€” Secret key for signing JWTs
ADMIN_USER Yes β€” Initial admin username
ADMIN_PASS Yes β€” Initial admin password
BACKEND_PORT / PORT No 3000 Internal backend port
SESSION_TTL_SECONDS No 3600 JWT session lifetime (300–43200 s)
SECURE_HEADERS_DISABLED No false Disable security headers (not recommended)
ENABLE_HSTS No false Send HSTS header (enable when behind HTTPS)
CONTENT_SECURITY_POLICY No built-in Override the default CSP header

Data Persistence

All application data is stored under /app/data/ inside the container:

Path Contents
/app/data/invio.db SQLite database (override with DATABASE_PATH)
/app/data/logos/ Company logo uploads
/app/data/templates/ Custom invoice templates
/app/data/backups/ Automatic DB backups on schema upgrade

Mount a named volume at /app/data to persist data across container restarts.

Running as non-root / hardened containers

The image runs as an unprivileged user (invoiceraider, UID/GID 1000:1000), so it works with hardening options such as --cap-drop=ALL and --security-opt no-new-privileges:

services:
  invoiceraider:
    # ...
    cap_drop:
      - ALL
    security_opt:
      - no-new-privileges:true

The data directory must be writable by UID 1000. New named volumes get this automatically. When upgrading from an older image (which ran as root), or when using a bind mount, fix ownership once:

# named volume (replace invoiceraider_data with your volume name, see `docker volume ls`)
docker run --rm -v invoiceraider_data:/data debian:13-slim chown -R 1000:1000 /data

# bind mount (run on the Docker host)
sudo chown -R 1000:1000 /path/to/your/data

Running with a custom UID/GID (e.g. Unraid 99:100)

The container can run as any UID/GID, as long as the data directory is owned by it. There are two ways to set it:

Option A: user: / --user (recommended) Option B: PUID / PGID env vars
Works with --cap-drop=ALL alone Yes No, needs CHOWN, SETUID, SETGID added back
Fixes data ownership for you No, chown once (step 2) Yes, on every start
Container starts as the given UID root, then drops to PUID:PGID

Setting only PUID/PGID as environment variables does nothing unless the container also starts as root (option B). With the default image user you then get a Permission denied ... invio.db error; the container log shows an [entrypoint] warning explaining this.

Option A: user: / --user

  1. Set the IDs in .env (the provided docker-compose.yml reads them):

    PUID=99
    PGID=100

    Or directly in your compose file / docker run:

    services:
      invoiceraider:
        user: "99:100"
    docker run --user 99:100 --cap-drop=ALL ...
  2. Give the data directory to that UID/GID once (on the Docker host):

    # named volume
    docker run --rm -v invoiceraider_data:/data debian:13-slim chown -R 99:100 /data
    
    # bind mount, e.g. /mnt/user/appdata/invoiceraider
    sudo chown -R 99:100 /mnt/user/appdata/invoiceraider
  3. docker compose up -d

Option B: PUID / PGID (Unraid-style)

Start as root with just the capabilities needed to fix ownership and switch user:

services:
  invoiceraider:
    user: "0:0"
    environment:
      PUID: 99
      PGID: 100
    cap_drop:
      - ALL
    cap_add:
      - CHOWN
      - SETUID
      - SETGID
    security_opt:
      - no-new-privileges:true

docker run / Unraid Extra Parameters:

--user 0:0 -e PUID=99 -e PGID=100 --cap-drop=ALL --cap-add=CHOWN --cap-add=SETUID --cap-add=SETGID

On startup the entrypoint runs chown -R 99:100 on the data directory, then runs the app as 99:100.

A new named volume is created owned by 1000:1000 (the image default), so step 2 is needed for it as well. Alternatively, build your own image with the IDs baked in (docker build --build-arg APP_UID=99 --build-arg APP_GID=100 ., which docker-compose-dev.yml does automatically from PUID/PGID). A fresh volume then gets the right owner automatically.


Invoice Number Patterns

Invoice numbers can be customized in Settings using token placeholders:

Token Description
{YYYY} Full year (e.g. 2025)
{MM} Two-digit month
{DD} Two-digit day
{RAND4} 4-character random alphanumeric

Example: INV-{YYYY}-{MM}-{RAND4} β†’ INV-2025-06-A3F9


Export Formats

Format Standard Use Case
PDF β€” Universal; uses WeasyPrint for rendering
UBL 2.1 OASIS General B2B electronic invoicing
Factur-X / ZugFeRD EN 16931 German/French hybrid PDF+XML
FatturaPA Italian SDI Italian electronic invoicing
PEPPOL BIS 3.0 OpenPEPPOL Pan-European procurement network

Email Actions by Invoice Status

InvoiceRaider exposes email actions in the invoice detail view based on invoice status:

  • Draft β€” Shows Send via Email
  • Paid β€” Shows Resend via Email
  • Sent and Overdue β€” Shows Send Reminder
  • Complete and Voided β€” No email button; these statuses are final and cannot be emailed.

Each email configuration can define separate templates for:

  • Normal invoice emails β€” Default Subject and Default Body
  • Reminder emails β€” Reminder Subject and Reminder Body

When a reminder template is not set for a config, InvoiceRaider falls back to that same config's normal Subject/Body.

This behavior helps separate first-send emails from follow-up reminders while still keeping a safe fallback.

The company email shown on the invoice PDF attached to an outgoing email is automatically replaced with the actual SMTP "From" address used to send that message, so the recipient always sees an address that matches the sender and can safely reply to it. This behavior is opt-in per email configuration via the Use From Address as invoice company email setting on that configuration β€” when disabled (the default), the invoice PDF keeps showing the company email from Settings > Company Information regardless of which configuration sent the message.

Every send attempt (normal or reminder) is recorded in an Email Log on the invoice detail page, showing the mode, timestamp, and success/failure. Recipient addresses are masked by default and can be revealed with a click. SMTP servers that silently reject all recipients (e.g. when the "From" address isn't authorized for the authenticated account) are now detected and logged as a failure instead of appearing to succeed.


License

This software is released into the public domain under the Unlicense. You are free to use, copy, modify, and distribute it for any purpose without restriction.

About

Self-hosted invoicing. Dockerized invoice creator with mailing system and status logging

Topics

Resources

Code of conduct

Stars

0 stars

Watchers

0 watching

Forks

Packages

Used by

Contributors

Languages