How to Install Invoice Ninja v5 on Ubuntu 24.04 VPS — Self-Hosted Invoicing & Billing
Invoice Ninja is one of the most mature open-source invoicing platforms available, rivaling commercial products like FreshBooks and QuickBooks Online. Self-hosting it on your own VPS gives you unlimited clients, unlimited invoices, full data ownership, and no monthly subscription fees. This guide walks you through a production-grade installation of Invoice Ninja v5 on Ubuntu 24.04, from a freshly provisioned server to a TLS-secured billing system with queue workers, scheduled tasks, and PDF generation working end to end.
Skip the setup hassle? Deploy a pre-configured LEMP stack on our CloudCore Starter VPS and have Invoice Ninja running in under 30 minutes. All you need is a domain.
Table of Contents
What is Invoice Ninja?
Invoice Ninja is a free, open-source invoicing, billing, and business management suite built on the Laravel PHP framework with a React-based client portal. It began in 2014 as a simple invoicing tool and has grown into a full accounts-receivable platform used by freelancers, agencies, and small-to-medium businesses worldwide. The project is distributed under the Elastic License (AAL 3.0) and the source code is maintained at invoiceninja.github.io.
The v5 release (currently the actively developed branch) is a complete rewrite of the older v4 codebase. It introduces a modern Vue.js admin interface (the "React Admin Portal"), a faster API, native support for multiple companies under one account, improved tax handling for EU VAT and US state sales tax, and a dedicated mobile app for iOS and Android.
The feature set is broad. On the core billing side you get unlimited invoices and quotes, recurring invoices with auto-billing, proposals that convert to invoices, credit notes and refunds, and statements for client accounts. For time and project tracking, Invoice Ninja includes a project management module, task timers that convert tracked hours into billable line items, and expense tracking with receipt attachments and markup rules. The client portal lets your customers log in, view outstanding invoices, pay online, download PDFs, and approve quotes. You also get multi-currency support, 40+ payment gateway integrations (Stripe, PayPal, Authorize.net, Mollie, GoCardless, and more), customizable PDF templates, a REST API, webhooks, and Zapier integration.
Invoice Ninja is used by solo consultants tracking a handful of clients, agencies managing hundreds of recurring subscriptions, and SaaS companies that need a white-label billing backend. Because it is self-hosted, all invoice PDFs, client payment details, and financial records stay on your own server.
Why Self-Host Invoice Ninja v5?
The hosted SaaS version of Invoice Ninja starts at around $10/month per user for the Pro plan and scales up with feature tiers. Running your own copy on a VPS eliminates that recurring cost and delivers several additional advantages:
- Free forever, unlimited everything -- The self-hosted version has no client limits, no invoice limits, no user limits, and no feature restrictions. You only pay for the VPS.
- Complete data ownership -- Invoices, client details, tax numbers, and payment records stay on your server. Nothing is transmitted to Invoice Ninja's cloud.
- Custom branding -- Remove the "Powered by Invoice Ninja" footer, use your own domain, apply your own color scheme, and ship invoices that look like they came from your own billing system.
- API rate limits you control -- The hosted version throttles API calls. On your own server you set the limits based on your infrastructure.
- GDPR compliance by design -- Keeping EU citizen financial data on an EU-hosted VPS simplifies Article 28 compliance. You are the processor and the controller.
- Integration freedom -- Run the database locally alongside your other business tools (CRM, ERP, analytics), join tables directly, and build reports without hitting an external API.
- Custom PDF templates -- Modify the invoice templates with your own HTML/CSS and upload custom fonts. Full control over every pixel.
Cost Comparison: Self-Hosted vs. Hosted Invoice Ninja vs. Commercial Alternatives
| Scenario | Invoice Ninja Hosted Pro | FreshBooks Plus | QuickBooks Online Essentials | Self-Hosted on VPS |
|---|---|---|---|---|
| Monthly cost | $10/user/mo | $33/mo | $35/mo | EUR 7.99/mo (CloudCore Starter) |
| Client limit | Unlimited | 50 billable | Unlimited | Unlimited |
| Invoice limit | Unlimited | Unlimited | Unlimited | Unlimited |
| Users included | 1 (add-on for more) | 1 | 3 | Unlimited |
| Custom branding | Limited | Limited | Limited | Full |
| Data residency | US-based | US-based | US-based | Your choice |
| Annual cost (3 users) | $360 | $396 | $420 | ~EUR 60 |
Prerequisites
Before you begin, make sure you have:
- A VPS running Ubuntu 24.04 LTS with root or sudo access
- SSH access to your server (PuTTY on Windows, Terminal on macOS/Linux)
- A domain name pointed at your VPS IP address (required for TLS and email deliverability)
- At least 1 GB of RAM (2 GB+ recommended for comfortable operation with background workers)
- At least 10 GB of free disk space for the application, database, PDF storage, and attachments
- SMTP credentials or a transactional email service (Postmark, Mailgun, Amazon SES) for sending invoices via email
Recommended Plan: CloudCore Starter>
For a small-to-medium invoicing workload (up to a few thousand invoices per month), we recommend the CloudCore Starter plan:>
- 3 vCPU cores
- 8 GB RAM
- 75 GB NVMe SSD
- Unmetered bandwidth
- EUR 7.99/month>
This provides comfortable headroom for PHP-FPM, MariaDB, the queue worker, and the PDF rendering service to coexist. If you are billing larger volumes (tens of thousands of invoices per month) or running multiple companies under one install, scale up to a CloudCore Professional or Business plan.
Connect to your server via SSH to begin:
ssh root@your-server-ipThis guide assumes your domain is billing.example.com. Replace it with your actual domain throughout.
Step 1: Update System Packages
Start with a full package update. This ensures you have the latest security patches and that dependency resolution works cleanly during the PHP and MariaDB installs.
sudo apt update && sudo apt upgrade -yExpected output (abbreviated):
Hit:1 http://archive.ubuntu.com/ubuntu noble InRelease
Hit:2 http://archive.ubuntu.com/ubuntu noble-updates InRelease
Reading package lists... Done
Building dependency tree... Done
Calculating upgrade... Done
0 upgraded, 0 newly installed, 0 to remove and 0 not upgraded.Install a few basic utilities you will need throughout the install:
sudo apt install -y curl wget unzip git software-properties-common ca-certificates lsb-release gnupgIf the kernel was updated, reboot before continuing:
sudo rebootStep 2: Install Nginx
Invoice Ninja works with both Apache and Nginx. We recommend Nginx for its lower memory footprint and better handling of concurrent PHP-FPM workers under modest VPS resources.
sudo apt install -y nginxEnable and start the service:
sudo systemctl enable --now nginxAllow HTTP and HTTPS through the firewall:
sudo ufw allow OpenSSH
sudo ufw allow 'Nginx Full'
sudo ufw --force enableVerify Nginx is responding:
curl -I http://localhostExpected output:
HTTP/1.1 200 OK
Server: nginx/1.24.0 (Ubuntu)For a fuller walkthrough of the web server, see our guide on How to Install Nginx on Ubuntu 24.04.
Step 3: Install PHP 8.3 and Required Extensions
Invoice Ninja v5 requires PHP 8.1 or newer. PHP 8.3 is the current stable release and ships in Ubuntu 24.04's default repositories, so no third-party PPAs are needed.
Install PHP-FPM and the extensions Invoice Ninja depends on:
sudo apt install -y \
php8.3-fpm php8.3-cli php8.3-common \
php8.3-bcmath php8.3-ctype php8.3-curl \
php8.3-fileinfo php8.3-gd php8.3-gmp \
php8.3-iconv php8.3-intl php8.3-mbstring \
php8.3-mysql php8.3-opcache php8.3-tokenizer \
php8.3-xml php8.3-zip php8.3-soap php8.3-imapEach of these extensions has a purpose in Invoice Ninja:
- bcmath, gmp -- Arbitrary precision math for currency calculations
- ctype, mbstring, tokenizer -- Laravel core requirements
- fileinfo -- Attachment handling and MIME detection
- gd -- Image processing for logos and signatures
- iconv, intl -- Character set conversion and internationalization (locales, currencies)
- curl -- Outbound HTTP for payment gateway APIs and webhooks
- mysql, pdo_mysql -- MariaDB/MySQL database connectivity
- openssl (bundled with PHP core) -- TLS, encryption, signing
- xml, soap -- Integrations that use XML/SOAP endpoints
- zip -- Installer ZIP handling and attachment archives
- opcache -- Opcode caching for production performance
sudo nano /etc/php/8.3/fpm/php.iniChange the following values (search with Ctrl+W):
memory_limit = 512M
upload_max_filesize = 50M
post_max_size = 50M
max_execution_time = 180
max_input_time = 180
date.timezone = UTCRestart PHP-FPM:
sudo systemctl restart php8.3-fpm
sudo systemctl enable php8.3-fpmFor a general LEMP stack walkthrough, see our guide on How to Install the LEMP Stack on Ubuntu 24.04 and the deeper PHP configuration reference.
Step 4: Install and Configure MariaDB
Invoice Ninja supports MySQL and MariaDB. MariaDB is the drop-in replacement maintained by MySQL's original authors and is what we recommend.
sudo apt install -y mariadb-server mariadb-clientEnable and start the service:
sudo systemctl enable --now mariadbRun the secure installation wizard. Accept the defaults (press Enter) except where prompted for a root password:
sudo mysql_secure_installationAnswer the prompts:
- Switch to unix_socket authentication? n
- Change the root password? Y (set a strong password)
- Remove anonymous users? Y
- Disallow root login remotely? Y
- Remove test database? Y
- Reload privilege tables? Y
STRONG_PASSWORD_HERE with a randomly generated password:sudo mariadbInside the MariaDB shell:
CREATE DATABASE ninja CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci;
CREATE USER 'ninja'@'localhost' IDENTIFIED BY 'STRONG_PASSWORD_HERE';
GRANT ALL PRIVILEGES ON ninja.* TO 'ninja'@'localhost';
FLUSH PRIVILEGES;
EXIT;Invoice Ninja requires the local_infile setting to be enabled for CSV imports. Edit the MariaDB config:
sudo nano /etc/mysql/mariadb.conf.d/50-server.cnfUnder [mysqld], add or uncomment:
local_infile = 1
sql_mode = "STRICT_TRANS_TABLES,NO_ZERO_IN_DATE,NO_ZERO_DATE,ERROR_FOR_DIVISION_BY_ZERO,NO_ENGINE_SUBSTITUTION"
innodb_file_per_table = 1Restart MariaDB:
sudo systemctl restart mariadbFor a deeper dive into database tuning, see How to Install MariaDB on Ubuntu 24.04.
Step 5: Download Invoice Ninja v5
Create the application directory and download the latest release. The official release ZIPs are published at github.com/invoiceninja/invoiceninja/releases — always grab the latest v5.x.x tag.
sudo mkdir -p /var/www/invoiceninja
cd /var/www/invoiceninjaFetch the latest release archive. At the time of writing the most recent version is v5.10.x — check the releases page for the current version and substitute it below:
sudo wget https://github.com/invoiceninja/invoiceninja/releases/latest/download/invoiceninja.tar -O invoiceninja.tarExtract into the current directory:
sudo tar -xf invoiceninja.tar -C /var/www/invoiceninja --strip-components=1
sudo rm invoiceninja.tarThe directory should now contain the standard Laravel layout (app/, bootstrap/, config/, public/, storage/, vendor/, artisan, etc.). The official release bundle ships with vendor/ already populated, so you do not need to run composer install on the server.
Step 6: Configure the .env File
Copy the example environment file and generate an application key.
cd /var/www/invoiceninja
sudo cp .env.example .envGenerate the encryption key (Laravel uses this for cookies, sessions, and field-level encryption):
sudo php artisan key:generate --forceEdit the .env file:
sudo nano .envSet the core configuration values:
APP_NAME="Your Company Billing" APP_ENV=production APP_DEBUG=false APP_URL=https://billing.example.comDB_CONNECTION=mysql DB_HOST=127.0.0.1 DB_PORT=3306 DB_DATABASE=ninja DB_USERNAME=ninja DB_PASSWORD=STRONG_PASSWORD_HERE
BROADCAST_DRIVER=log CACHE_DRIVER=file QUEUE_CONNECTION=database SESSION_DRIVER=file SESSION_LIFETIME=120
MAIL_MAILER=smtp MAIL_HOST=smtp.postmarkapp.com MAIL_PORT=587 MAIL_USERNAME=your-smtp-username MAIL_PASSWORD=your-smtp-password MAIL_ENCRYPTION=tls [email protected] MAIL_FROM_NAME="${APP_NAME}"
REQUIRE_HTTPS=true TRUSTED_PROXIES=*
PDF_GENERATOR=snappdf
A few notes on these values:
APP_URLmust match the exact URL (includinghttps://) where you will access the admin interface. PDF generation and webhook signatures depend on this being correct.QUEUE_CONNECTION=databasestores pending jobs in MariaDB. For higher throughput you can switch toredislater.PDF_GENERATOR=snappdfuses Invoice Ninja's built-in headless Chromium wrapper for PDF rendering (covered in the troubleshooting section).
Ctrl+O, Enter, Ctrl+X).Step 7: Run Database Migrations
With the .env file in place, populate the database schema:
cd /var/www/invoiceninja
sudo php artisan migrate --seed --forceExpected output (abbreviated):
INFO Preparing database. Creating migration table .................................. 12ms DONE
INFO Running migrations. 2014_10_13_000000_create_users_table ..................... 245ms DONE 2014_10_13_000000_create_accounts_table .................. 198ms DONE ... Database seeding completed successfully.
The --seed flag loads reference data: countries, currencies, tax rates, language translations, and default invoice/quote templates.
To populate the app with example clients, invoices, and products for testing (optional, recommended only on non-production installs):
sudo php artisan db:seed --class=RandomDataSeeder --forceFinally, warm up the Laravel caches for production performance:
sudo php artisan optimizeThis compiles the config, routes, and view caches so they are not rebuilt on every request.
Step 8: Set File Permissions
Nginx and PHP-FPM run as the www-data user. The application needs write access to storage/ (logs, cached views, generated PDFs, uploaded attachments) and bootstrap/cache/ (compiled Laravel caches).
sudo chown -R www-data:www-data /var/www/invoiceninja
sudo find /var/www/invoiceninja -type d -exec chmod 755 {} \;
sudo find /var/www/invoiceninja -type f -exec chmod 644 {} \;
sudo chmod -R 775 /var/www/invoiceninja/storage
sudo chmod -R 775 /var/www/invoiceninja/bootstrap/cache
sudo chmod -R 775 /var/www/invoiceninja/publicThe 775 on storage/, bootstrap/cache/, and public/ allows the group (www-data) to write. Every other file and directory is read-only for the web server.
Step 9: Configure the Nginx Server Block
Create the Invoice Ninja server block. The document root must point at public/ (the Laravel front controller lives there).
sudo nano /etc/nginx/sites-available/invoiceninjaPaste the following, replacing billing.example.com with your domain:
server { listen 80; listen [::]:80; server_name billing.example.com;root /var/www/invoiceninja/public; index index.php index.html;
client_max_body_size 50M;
location / { try_files $uri $uri/ /index.php?$query_string; }
location ~ \.php$ { fastcgi_pass unix:/var/run/php/php8.3-fpm.sock; fastcgi_index index.php; include fastcgi_params; fastcgi_param SCRIPT_FILENAME $document_root$fastcgi_script_name; fastcgi_read_timeout 180; }
location ~ /\.(?!well-known).* { deny all; }
location ~* \.(jpg|jpeg|png|gif|ico|css|js|woff2?|ttf|svg)$ { expires 30d; add_header Cache-Control "public, no-transform"; } }
Enable the site and remove the default:
sudo ln -s /etc/nginx/sites-available/invoiceninja /etc/nginx/sites-enabled/
sudo rm -f /etc/nginx/sites-enabled/defaultTest the configuration and reload:
sudo nginx -t
sudo systemctl reload nginxYou should now be able to reach http://billing.example.com in a browser and see the Invoice Ninja setup screen. Do not proceed with the web installer yet — finish the TLS and cron setup first.
Step 10: Enable TLS with Let's Encrypt
Invoice Ninja stores credit card details (via tokenized gateway references) and authenticates users via cookies. TLS is mandatory for production. Let's Encrypt issues free, 90-day certificates that renew automatically.
Install Certbot and the Nginx plugin:
sudo apt install -y certbot python3-certbot-nginxRequest a certificate. Certbot will automatically modify your Nginx config to add the TLS listener and redirect HTTP traffic to HTTPS:
sudo certbot --nginx -d billing.example.comAnswer the prompts (enter an email, accept terms, choose redirect-to-HTTPS).
Verify auto-renewal is scheduled:
sudo systemctl status certbot.timer
sudo certbot renew --dry-runFor a complete walkthrough including DNS-01 challenges and wildcard certificates, see How to Install Let's Encrypt SSL on Ubuntu 24.04.
Step 11: Schedule the Cron Job
Invoice Ninja relies on Laravel's scheduler for recurring invoice generation, payment retries, reminder emails, daily stats, and cleanup tasks. The scheduler must be triggered every minute.
Open the crontab for the www-data user:
sudo crontab -u www-data -eAdd this line at the bottom:
* cd /var/www/invoiceninja && /usr/bin/php artisan schedule:run >> /dev/null 2>&1Save and exit. Laravel's scheduler runs once a minute and decides internally which jobs to dispatch based on their defined schedule (hourly, daily, monthly, etc.).
Verify the cron entry is registered:
sudo crontab -u www-data -lStep 12: Run the Queue Worker as a systemd Service
Invoice Ninja dispatches background jobs for email sending, PDF generation, webhook delivery, and payment processing to a queue. Without a queue worker, invoices will sit unsent and PDFs will never render.
Create a systemd unit:
sudo nano /etc/systemd/system/invoiceninja-worker.servicePaste:
[Unit] Description=Invoice Ninja Queue Worker After=network.target mariadb.service[Service] User=www-data Group=www-data Restart=always RestartSec=3 WorkingDirectory=/var/www/invoiceninja ExecStart=/usr/bin/php artisan queue:work --sleep=3 --tries=3 --max-time=3600 StandardOutput=append:/var/log/invoiceninja-worker.log StandardError=append:/var/log/invoiceninja-worker.log
[Install] WantedBy=multi-user.target
Reload systemd, enable, and start:
sudo systemctl daemon-reload
sudo systemctl enable --now invoiceninja-worker
sudo systemctl status invoiceninja-workerExpected output:
● invoiceninja-worker.service - Invoice Ninja Queue Worker
Loaded: loaded (/etc/systemd/system/invoiceninja-worker.service; enabled; preset: enabled)
Active: active (running) since Wed 2026-04-16 11:30:00 UTC; 5s ago
Main PID: 2345 (php)
Tasks: 1 (limit: 9404)
Memory: 42.0MThe --max-time=3600 flag restarts the worker every hour, which keeps memory usage stable (long-running PHP processes tend to accumulate memory).
Supervisor Alternative
If you prefer Supervisor to systemd (for multi-worker scaling), install it:
sudo apt install -y supervisor
sudo nano /etc/supervisor/conf.d/invoiceninja-worker.confPaste:
[program:invoiceninja-worker]
process_name=%(program_name)s_%(process_num)02d
command=/usr/bin/php /var/www/invoiceninja/artisan queue:work --sleep=3 --tries=3 --max-time=3600
autostart=true
autorestart=true
user=www-data
numprocs=2
redirect_stderr=true
stdout_logfile=/var/log/invoiceninja-worker.log
stopwaitsecs=3600Apply:
sudo supervisorctl reread
sudo supervisorctl update
sudo supervisorctl start invoiceninja-worker:*Use either systemd or Supervisor, not both.
Step 13: Complete First-Run Setup
Visit https://billing.example.com in your browser. You will be redirected to the setup wizard.
The wizard asks for:
.env. Click "Test Database Connection.".env. Click "Test Mail Configuration" to send yourself a test email.APP_URL in .env.Click Submit. Invoice Ninja writes a lock file, finalizes the install, and redirects you to the admin dashboard.
Log in with the admin email and password you just set. You will land on the dashboard, which shows outstanding invoices, overdue amounts, and revenue graphs (all at zero for a fresh install).
Post-Install: Payment Gateways, Branding, Recurring Invoices
With the core system running, these are the most common setup tasks for a production billing system.
Connect Stripe
Navigate to Settings -> Online Payments -> Payment Methods -> Add Gateway -> Stripe.
https://billing.example.com/payment_webhook/COMPANY_KEY/GATEWAY_ID (Invoice Ninja shows the exact URL in the gateway settings).Connect PayPal
Same flow under Settings -> Online Payments -> Payment Methods -> Add Gateway -> PayPal. You'll need a PayPal Business account and REST API credentials from developer.paypal.com.
Custom Branding
Go to Settings -> Company Details to set your company name, address, VAT/Tax ID, and logo. Then Settings -> Invoice Design to customize the PDF template, colors, fonts, and watermark. Upload your logo under Settings -> Branding -> Logo.
To remove the "Powered by Invoice Ninja" footer from emails and the client portal, enable White-Label License under Settings -> Account Management. The self-hosted white-label license is a one-time $30 purchase that removes all Invoice Ninja branding and unlocks custom email signatures — fair support for the open-source project.
Recurring Invoices
Create a recurring invoice under Recurring Invoices -> New. Set the frequency (weekly, monthly, annually, custom), the start date, and whether to auto-send and auto-bill. Recurring invoices dispatch on the schedule you set — powered by the cron job you configured in Step 11.
Client Portal
Each client automatically gets a portal at https://billing.example.com/client/login. They can view outstanding invoices, pay online, download PDFs, approve quotes, and update their contact details. Customize the portal under Settings -> Client Portal — enable/disable features, set the accent color, and add a custom terms-and-conditions URL.
Multi-Company Setup
Invoice Ninja supports up to 10 companies under a single admin account. Click the company dropdown in the top bar and select Add Company. Each company has its own branding, invoice numbering, clients, and financial records.
Backups and Updates
Database and File Backups
Schedule a nightly backup via cron:
sudo nano /usr/local/bin/invoiceninja-backup.shPaste:
#!/bin/bash
BACKUP_DIR="/var/backups/invoiceninja"
TIMESTAMP=$(date +%Y%m%d-%H%M%S)
mkdir -p "$BACKUP_DIR"Database dump
mysqldump -u ninja -p'STRONG_PASSWORD_HERE' ninja | gzip > "$BACKUP_DIR/ninja-db-$TIMESTAMP.sql.gz"Storage directory (uploaded attachments, logos)
tar -czf "$BACKUP_DIR/ninja-storage-$TIMESTAMP.tar.gz" -C /var/www/invoiceninja storageRetain last 14 days
find "$BACKUP_DIR" -type f -mtime +14 -deleteMake it executable and schedule:
sudo chmod +x /usr/local/bin/invoiceninja-backup.sh
sudo crontab -eAdd:
0 3 * /usr/local/bin/invoiceninja-backup.shFor off-site backup, add a rclone or rsync step to the script pointing at S3, B2, or another VPS.
In-App Updates
Invoice Ninja v5 includes a self-updater. Navigate to Settings -> Account Management -> System Settings -> Update App. Click Update and the application will fetch the latest release, back up the current install, and swap in the new version.
For the self-updater to work, the web server must be able to write to /var/www/invoiceninja. This is already the case if you followed Step 8.
If the in-app updater fails (common causes: disk full, permissions changed), you can update manually:
cd /var/www/invoiceninja
sudo -u www-data php artisan down
sudo wget https://github.com/invoiceninja/invoiceninja/releases/latest/download/invoiceninja.tar -O /tmp/invoiceninja.tar
sudo tar -xf /tmp/invoiceninja.tar -C /var/www/invoiceninja --strip-components=1
sudo chown -R www-data:www-data /var/www/invoiceninja
sudo -u www-data php artisan migrate --force
sudo -u www-data php artisan optimize
sudo -u www-data php artisan up
sudo systemctl restart invoiceninja-workerAlways run a database backup before upgrading.
Troubleshooting
| Problem | Cause | Solution |
|---|---|---|
| Invoices not sending via email | Queue worker not running | Check sudo systemctl status invoiceninja-worker. Inspect /var/log/invoiceninja-worker.log. Restart: sudo systemctl restart invoiceninja-worker |
| Recurring invoices not generating | Cron not configured or running as wrong user | Verify sudo crontab -u www-data -l shows the schedule line. Check /var/log/syslog for cron errors. |
| 500 Internal Server Error | PHP error, missing extension, or permissions | Tail the log: sudo tail -f /var/www/invoiceninja/storage/logs/laravel.log. Check PHP extensions: php -m \</td><td>grep -i gmp. Re-run Step 8 permissions. |
| PDFs fail to generate | snappdf Chromium binary missing | Run sudo -u www-data php artisan snappdf:chromium-download. Ensure /tmp is writable. |
| "Storage not linked" warning | Public symlink missing | sudo -u www-data php artisan storage:link |
| "Your database does not support server-side prepared statements" | MariaDB config | Confirm sql_mode from Step 4 is applied: sudo mariadb -e "SELECT @@sql_mode;" |
| Login fails with CSRF error | Session or APP_URL mismatch | Confirm APP_URL in .env matches the browser URL exactly (including protocol). Clear caches: sudo -u www-data php artisan optimize:clear |
| White label license shows as invalid | License key encrypted with different APP_KEY | Do not regenerate APP_KEY after install — it breaks stored encrypted fields. Restore from backup if needed. |
| Client portal shows 404 | Nginx try_files misconfigured | Re-check the location / block in Step 9. Confirm document root is /var/www/invoiceninja/public. |
| Queue jobs pile up | Worker crashed or insufficient memory | Check worker log. Increase memory_limit in php.ini. Restart worker. |
Viewing Logs
The Laravel application log captures most runtime errors:
sudo tail -f /var/www/invoiceninja/storage/logs/laravel.logThe queue worker log:
sudo tail -f /var/log/invoiceninja-worker.logNginx access and error logs:
sudo tail -f /var/log/nginx/access.log /var/log/nginx/error.logPDF Generation Deep Dive
Invoice Ninja v5 uses snappdf (a headless Chromium wrapper) to render PDFs in the same browser engine customers see in the client portal. On first install, you need to download the Chromium binary:
cd /var/www/invoiceninja
sudo -u www-data php artisan snappdf:chromium-downloadIf Chromium fails to launch on your VPS (common in constrained containers), install the system dependencies:
sudo apt install -y libnss3 libatk-bridge2.0-0 libx11-xcb1 libxcomposite1 \
libxdamage1 libxrandr2 libgbm1 libxkbcommon0 libpango-1.0-0 libcairo2 libasound2t64As an alternative, you can switch to the hosted PDF service by setting PDF_GENERATOR=hosted_ninja in .env — Invoice Ninja's team hosts a free rendering service for self-hosted installs.
FAQ
Is the self-hosted version of Invoice Ninja really free forever?
Yes. The core Invoice Ninja v5 codebase is published under the Elastic License (AAL 3.0) and remains free to self-host for commercial use. The only paid component is the optional White-Label License ($30 one-time), which removes Invoice Ninja branding from client-facing pages and emails and unlocks custom email signatures. There is no user cap, no invoice cap, no client cap, and no feature gate. All payment gateway integrations, recurring billing, the client portal, project/time tracking, and the API are included.
What's the difference between Invoice Ninja v4 and v5?
Invoice Ninja v4 (legacy) is the original Laravel codebase and will continue receiving security patches but no new features. Invoice Ninja v5 is a full rewrite with a new React-based admin interface, a faster API (3-5x improvement on most endpoints), native multi-company support, better tax handling for EU VAT and US state sales tax, and an improved client portal. All new installs should use v5. If you have a v4 install, the project provides a v4-to-v5 migration tool that exports v4 data and imports it into a fresh v5 install.
Can I run Invoice Ninja behind a reverse proxy or CDN?
Yes. Add TRUSTED_PROXIES= (or a specific IP range) to .env and ensure your proxy sets X-Forwarded-Proto: https and X-Forwarded-For headers. For Cloudflare, enable "Full (Strict)" SSL mode and set the origin certificate appropriately. For Varnish or Nginx reverse proxies, pass through all /api/ and /payment_webhook/* routes without caching.
How do I send invoices via Postmark, Mailgun, or Amazon SES?
All three work seamlessly via SMTP. In .env:
- Postmark:
MAIL_HOST=smtp.postmarkapp.com, port 587, username/password = your server API token - Mailgun:
MAIL_HOST=smtp.mailgun.org, port 587, username = your Mailgun SMTP user, password = your SMTP password - Amazon SES:
MAIL_HOST=email-smtp.us-east-1.amazonaws.com(pick your region), port 587, use IAM SMTP credentials
MAIL_MAILER=postmark, MAIL_MAILER=ses) — see the Invoice Ninja docs for driver-specific env variables.Can I run multiple companies or white-label for clients?
Yes. A single Invoice Ninja install supports up to 10 companies under one admin account, each with its own branding, clients, and numbering. For true multi-tenant SaaS (separate databases per customer), you'll need to run separate Invoice Ninja installs per tenant or use the API to provision under a single instance. For agency use cases where you bill your own clients and also manage billing for their end-customers, the 10-company limit is usually sufficient.
How does Invoice Ninja compare to Akaunting, Crater, and BillBee?
Invoice Ninja has the most mature feature set, the largest gateway integration library (40+), a polished client portal, and native mobile apps for iOS and Android. The community is large and the release cadence is fast (monthly patch releases).
Akaunting is a broader accounting suite (invoices + double-entry bookkeeping + inventory + payroll) and competes more directly with QuickBooks than with pure invoicing tools. Heavier to self-host, smaller gateway library.
Crater is a newer, Laravel-based invoicing tool with a clean UI but fewer features (no recurring invoices in the core, no time tracking, smaller gateway library). Best for simple use cases.
BillBee is a commercial SaaS focused on e-commerce order management with invoicing as one feature. Not self-hostable.
For most VPS users wanting a self-hosted invoicing and billing platform, Invoice Ninja remains the best balance of features, maturity, and active development.
Next Steps
Now that Invoice Ninja is running on your VPS, here are recommended next steps to harden and extend your setup:
- Harden SSH and enable unattended upgrades -- Disable password authentication, switch to SSH keys, and enable
unattended-upgradesso security patches apply automatically. See our guide on Ubuntu server hardening.
- Add Redis for faster queues and sessions -- Swap
QUEUE_CONNECTION=databaseandSESSION_DRIVER=fileforredis. Redis on a local socket is dramatically faster under load. Install Redis, update.env, and restart PHP-FPM and the worker.
- Monitor uptime and alert on failures -- Deploy Uptime Kuma on a separate server and monitor both the admin URL and the
/api/v1/healthendpoint. Alert via email, Slack, or Discord when either is down.
- Integrate with your CRM via the API -- Invoice Ninja exposes a full REST API documented at api-docs.invoicing.co. Push new clients from your CRM (HubSpot, Pipedrive) directly into Invoice Ninja, or pull invoice status back into your CRM for commission tracking.
- Set up Zapier or n8n automations -- Connect Invoice Ninja to 5,000+ apps via the official Zapier integration or self-host n8n on the same VPS. Common workflows: send a Slack alert on payment received, create a Trello card for each new invoice, sync payments into a Google Sheet.
- Explore the plugin ecosystem -- The Invoice Ninja GitHub org hosts companion tools including the iOS/Android apps, Chrome extension for time tracking, and community gateway packs.
Skip the Manual Install — Get a Production-Ready VPS>
Our CloudCore Starter VPS gives you the exact stack this guide installs — Ubuntu 24.04, 8 GB RAM, 75 GB NVMe SSD — for EUR 7.99/month. Deploy in 60 seconds, point your domain, and follow this guide end to end in under 30 minutes.>
- Ubuntu 24.04 LTS pre-installed
- Full root access with SSH
- Unmetered bandwidth on a 1 Gbit/s port
- Daily automated snapshots available
- 24/7 support from humans who use Invoice Ninja themselves>
Deploy Your Billing VPS Now — Plans start at EUR 7.99/month.